diff options
| author | Dante Catalfamo | 2021-10-18 17:36:47 -0400 | 
|---|---|---|
| committer | Dante Catalfamo | 2021-10-18 17:36:47 -0400 | 
| commit | 894afa96cd14a84cd1a1bcfb9523f10210aebb7c (patch) | |
| tree | f8e30e801d3dec12c23933883bbf75765502b755 /content/posts/how-bsd-authentication-works | |
| parent | 230c8996ec91714db52a8593e8dac24939955438 (diff) | |
| download | blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.tar.gz blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.tar.bz2 blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.zip | |
bsd-auth: no longer WIP
Diffstat (limited to 'content/posts/how-bsd-authentication-works')
| -rwxr-xr-x | content/posts/how-bsd-authentication-works/gen_dot.rb | 99 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/graph.dot | 152 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/graph.svg | 856 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/index.org | 2811 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/notes.org | 83 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/openbsd_internals.gif | bin | 0 -> 690203 bytes | 
6 files changed, 4001 insertions, 0 deletions
| diff --git a/content/posts/how-bsd-authentication-works/gen_dot.rb b/content/posts/how-bsd-authentication-works/gen_dot.rb new file mode 100755 index 0000000..9f71876 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/gen_dot.rb @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Copyright (C) 2021 Dante Catalfamo +# SPDX-License-Identifier: MIT + +require 'digest' +require 'set' + +SOURCE_DIR = File.join Dir.home, 'src', 'github.com', 'openbsd', 'src', 'lib', 'libc', 'gen' +FILENAMES = %w[authenticate.c auth_subr.c login_cap.c].freeze + +FUNCTION_REGEX = /^\w.*?$\n(?!DEF)(\w*)\(.*?\)\n\{(.*?)^\}/m.freeze +ONELINE_FUNCTION_REFGEX = /^\w.*?(\w*)\(.*?\).*?\{(.*?)\}/.freeze +CALL_REGEX = /[^\n](\w+)\(.*?\)/.freeze + +class FunctionDigraph +  attr_accessor :pairs, :subgraphs + +  class Subgraph +    attr_accessor :name, :label, :functions + +    def initialize(name, label) +      @name = name +      @label = label +      @functions = [] +    end + +    def emit +      puts "subgraph cluster_#{name} {" +      puts "label = \"#{label}\"" +      functions.each { |f| puts f unless f == 'DEF_WEAK' } +      puts '}' +    end +  end + +  class Pair +    attr_accessor :to, :from + +    def initialize(from, to) +      @from = from +      @to = to +    end + +    def emit +      puts "#{from} -> #{to} [color = \"##{color}\"]" +    end + +    def color +      Digest::MD5.hexdigest(from)[..5] +    end +  end + +  def initialize +    @pairs = [] +    @subgraphs = [] +  end + +  def emit +    puts 'digraph G {' +    puts 'rankdir=LR' +    puts 'splines=ortho' +    puts 'graph [pad="0.5", nodesep="0.5", ranksep="1.5"]' +    all_functions = Set.new +    @subgraphs.each { |s| all_functions.merge(s.functions) } +    @subgraphs.each(&:emit) +    @pairs.uniq { |p| [p.to, p.from] }.each do |p| +      p.emit if all_functions.include?(p.to) +    end +    puts '}' +  end + +  def parse_files(filenames) +    filenames.each do |filename| +      contents = File.read(filename) +      basename = File.basename filename +      subgraph = Subgraph.new(basename.gsub(/\..*/, ''), basename) +      functions = contents.scan(FUNCTION_REGEX) +      oneliners = contents.scan(ONELINE_FUNCTION_REFGEX) +      functions.concat(oneliners) unless oneliners.empty? +      functions.each do |function| +        function_name = function[0] +        function_body = function[1] +        subgraph.functions << function_name +        function_body.scan(CALL_REGEX) do |call| +          @pairs << Pair.new(function_name, call[0]) +        end +      end +      @subgraphs << subgraph +    end +  end +end + +fg = FunctionDigraph.new + +files = FILENAMES.map { |f| File.join(SOURCE_DIR, f) } +fg.parse_files files + +fg.emit diff --git a/content/posts/how-bsd-authentication-works/graph.dot b/content/posts/how-bsd-authentication-works/graph.dot new file mode 100644 index 0000000..9a2be3c --- /dev/null +++ b/content/posts/how-bsd-authentication-works/graph.dot @@ -0,0 +1,152 @@ +digraph G { +rankdir=LR +splines=ortho +graph [pad="0.5", nodesep="0.5", ranksep="1.5"] +subgraph cluster_authenticate { +label = "authenticate.c" +auth_mkvalue +auth_checknologin +_auth_checknologin +auth_cat +_auth_validuser +auth_approval +auth_usercheck +auth_userokay +auth_userchallenge +auth_userresponse +auth_verify +} +subgraph cluster_auth_subr { +label = "auth_subr.c" +auth_open +auth_clean +auth_close +auth_challenge +auth_setenv +auth_clrenv +auth_getitem +auth_setitem +auth_setoption +auth_clroptions +auth_clroption +auth_setdata +auth_setpwd +auth_getvalue +auth_check_expire +auth_check_change +auth_call +_recv_fd +_auth_spool +_add_rmlist +_auth_next_arg +auth_setstate +auth_set_va_list +auth_getstate +auth_getpwd +} +subgraph cluster_login_cap { +label = "login_cap.c" +login_getclass +login_getstyle +login_getcapstr +login_getcaptime +login_getcapnum +login_getcapsize +login_getcapbool +login_close +gsetrl +setclasscontext +setusercontext +setuserpath +setuserenv +login_setenv +strtosize +strtolimit +multiply +secure_path +expandstr +} +auth_checknologin -> _auth_checknologin [color = "#f0bfd8"] +_auth_checknologin -> login_getcapbool [color = "#f51748"] +_auth_checknologin -> login_getcapstr [color = "#f51748"] +_auth_checknologin -> auth_cat [color = "#f51748"] +auth_approval -> auth_getitem [color = "#8f94eb"] +auth_approval -> auth_getpwd [color = "#8f94eb"] +auth_approval -> _auth_validuser [color = "#8f94eb"] +auth_approval -> login_getclass [color = "#8f94eb"] +auth_approval -> login_close [color = "#8f94eb"] +auth_approval -> login_getcapstr [color = "#8f94eb"] +auth_approval -> auth_open [color = "#8f94eb"] +auth_approval -> auth_setstate [color = "#8f94eb"] +auth_approval -> auth_setitem [color = "#8f94eb"] +auth_approval -> auth_check_expire [color = "#8f94eb"] +auth_approval -> login_getcapbool [color = "#8f94eb"] +auth_approval -> auth_call [color = "#8f94eb"] +auth_approval -> auth_close [color = "#8f94eb"] +auth_approval -> auth_getstate [color = "#8f94eb"] +auth_usercheck -> _auth_validuser [color = "#96fc6d"] +auth_usercheck -> login_getclass [color = "#96fc6d"] +auth_usercheck -> login_getstyle [color = "#96fc6d"] +auth_usercheck -> login_close [color = "#96fc6d"] +auth_usercheck -> auth_open [color = "#96fc6d"] +auth_usercheck -> auth_setitem [color = "#96fc6d"] +auth_usercheck -> auth_setdata [color = "#96fc6d"] +auth_usercheck -> auth_verify [color = "#96fc6d"] +auth_userokay -> auth_usercheck [color = "#3eb09b"] +auth_userokay -> auth_close [color = "#3eb09b"] +auth_userchallenge -> _auth_validuser [color = "#0db8d4"] +auth_userchallenge -> login_getclass [color = "#0db8d4"] +auth_userchallenge -> login_getstyle [color = "#0db8d4"] +auth_userchallenge -> auth_open [color = "#0db8d4"] +auth_userchallenge -> login_close [color = "#0db8d4"] +auth_userchallenge -> auth_setitem [color = "#0db8d4"] +auth_userchallenge -> auth_close [color = "#0db8d4"] +auth_userchallenge -> auth_challenge [color = "#0db8d4"] +auth_userresponse -> auth_setstate [color = "#cc7ac2"] +auth_userresponse -> auth_getitem [color = "#cc7ac2"] +auth_userresponse -> _auth_validuser [color = "#cc7ac2"] +auth_userresponse -> auth_close [color = "#cc7ac2"] +auth_userresponse -> auth_setdata [color = "#cc7ac2"] +auth_userresponse -> auth_getstate [color = "#cc7ac2"] +auth_userresponse -> auth_check_expire [color = "#cc7ac2"] +auth_verify -> auth_open [color = "#83192f"] +auth_verify -> auth_setstate [color = "#83192f"] +auth_verify -> auth_setitem [color = "#83192f"] +auth_verify -> auth_getitem [color = "#83192f"] +auth_verify -> _auth_validuser [color = "#83192f"] +auth_verify -> auth_set_va_list [color = "#83192f"] +auth_verify -> auth_call [color = "#83192f"] +auth_clean -> auth_clrenv [color = "#759227"] +auth_clean -> auth_setitem [color = "#759227"] +auth_close -> auth_setenv [color = "#4a5505"] +auth_challenge -> _auth_validuser [color = "#5e3ac3"] +auth_challenge -> auth_getvalue [color = "#5e3ac3"] +auth_setitem -> auth_setitem [color = "#e67693"] +auth_setitem -> _auth_validuser [color = "#e67693"] +auth_check_expire -> auth_setpwd [color = "#739550"] +auth_check_change -> auth_setpwd [color = "#902d9d"] +auth_call -> _auth_next_arg [color = "#a9e6c9"] +auth_call -> _auth_spool [color = "#a9e6c9"] +auth_call -> _add_rmlist [color = "#a9e6c9"] +auth_call -> auth_clrenv [color = "#a9e6c9"] +_auth_spool -> _recv_fd [color = "#977e1c"] +login_getstyle -> login_getcapstr [color = "#51a344"] +login_getcapsize -> strtolimit [color = "#97959e"] +gsetrl -> login_getcaptime [color = "#35d53a"] +gsetrl -> login_getcapsize [color = "#35d53a"] +gsetrl -> login_getcapnum [color = "#35d53a"] +setclasscontext -> login_getclass [color = "#5b8e44"] +setclasscontext -> setusercontext [color = "#5b8e44"] +setclasscontext -> login_close [color = "#5b8e44"] +setusercontext -> login_getclass [color = "#7eb75f"] +setusercontext -> login_close [color = "#7eb75f"] +setusercontext -> login_getcapnum [color = "#7eb75f"] +setusercontext -> setuserenv [color = "#7eb75f"] +setusercontext -> setuserpath [color = "#7eb75f"] +setuserpath -> login_setenv [color = "#35ed7d"] +setuserenv -> login_setenv [color = "#a1aa6a"] +login_setenv -> expandstr [color = "#502c54"] +strtosize -> multiply [color = "#a61402"] +strtosize -> strtosize [color = "#a61402"] +strtolimit -> strtosize [color = "#c987ba"] +} diff --git a/content/posts/how-bsd-authentication-works/graph.svg b/content/posts/how-bsd-authentication-works/graph.svg new file mode 100644 index 0000000..94f22d2 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/graph.svg @@ -0,0 +1,856 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.44.0 (0) + --> +<!-- Title: G Pages: 1 --> +<svg width="2433pt" height="2378pt" + viewBox="0.00 0.00 2432.97 2378.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(36 2342)"> +<title>G</title> +<polygon fill="white" stroke="transparent" points="-36,36 -36,-2342 2396.97,-2342 2396.97,36 -36,36"/> +<g id="clust1" class="cluster"> +<title>cluster_authenticate</title> +<polygon fill="none" stroke="black" points="8,-734 8,-1135 1114.93,-1135 1114.93,-734 8,-734"/> +<text text-anchor="middle" x="561.47" y="-1119.8" font-family="Times-Roman" font-size="14.00">authenticate.c</text> +</g> +<g id="clust2" class="cluster"> +<title>cluster_auth_subr</title> +<polygon fill="none" stroke="black" points="918.25,-1143 918.25,-2298 1645.5,-2298 1645.5,-1143 918.25,-1143"/> +<text text-anchor="middle" x="1281.88" y="-2282.8" font-family="Times-Roman" font-size="14.00">auth_subr.c</text> +</g> +<g id="clust3" class="cluster"> +<title>cluster_login_cap</title> +<polygon fill="none" stroke="black" points="930.6,-8 930.6,-515 2352.97,-515 2352.97,-8 930.6,-8"/> +<text text-anchor="middle" x="1641.78" y="-499.8" font-family="Times-Roman" font-size="14.00">login_cap.c</text> +</g> +<!-- auth_mkvalue --> +<g id="node1" class="node"> +<title>auth_mkvalue</title> +<ellipse fill="none" stroke="black" cx="110.89" cy="-1086" rx="74.19" ry="18"/> +<text text-anchor="middle" x="110.89" y="-1082.3" font-family="Times-Roman" font-size="14.00">auth_mkvalue</text> +</g> +<!-- auth_checknologin --> +<g id="node2" class="node"> +<title>auth_checknologin</title> +<ellipse fill="none" stroke="black" cx="110.89" cy="-760" rx="94.78" ry="18"/> +<text text-anchor="middle" x="110.89" y="-756.3" font-family="Times-Roman" font-size="14.00">auth_checknologin</text> +</g> +<!-- _auth_checknologin --> +<g id="node3" class="node"> +<title>_auth_checknologin</title> +<ellipse fill="none" stroke="black" cx="413.22" cy="-760" rx="99.38" ry="18"/> +<text text-anchor="middle" x="413.22" y="-756.3" font-family="Times-Roman" font-size="14.00">_auth_checknologin</text> +</g> +<!-- auth_checknologin->_auth_checknologin --> +<g id="edge1" class="edge"> +<title>auth_checknologin->_auth_checknologin</title> +<path fill="none" stroke="#f0bfd8" d="M206.14,-760C206.14,-760 303.76,-760 303.76,-760"/> +<polygon fill="#f0bfd8" stroke="#f0bfd8" points="303.76,-763.5 313.76,-760 303.76,-756.5 303.76,-763.5"/> +</g> +<!-- auth_cat --> +<g id="node4" class="node"> +<title>auth_cat</title> +<ellipse fill="none" stroke="black" cx="719.46" cy="-760" rx="49.29" ry="18"/> +<text text-anchor="middle" x="719.46" y="-756.3" font-family="Times-Roman" font-size="14.00">auth_cat</text> +</g> +<!-- _auth_checknologin->auth_cat --> +<g id="edge4" class="edge"> +<title>_auth_checknologin->auth_cat</title> +<path fill="none" stroke="#f51748" d="M513.07,-760C513.07,-760 659.96,-760 659.96,-760"/> +<polygon fill="#f51748" stroke="#f51748" points="659.96,-763.5 669.96,-760 659.96,-756.5 659.96,-763.5"/> +</g> +<!-- login_getcapstr --> +<g id="node39" class="node"> +<title>login_getcapstr</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-398" rx="79.89" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-394.3" font-family="Times-Roman" font-size="14.00">login_getcapstr</text> +</g> +<!-- _auth_checknologin->login_getcapstr --> +<g id="edge3" class="edge"> +<title>_auth_checknologin->login_getcapstr</title> +<path fill="none" stroke="#f51748" d="M446.97,-743.03C446.97,-671.46 446.97,-398 446.97,-398 446.97,-398 1222.76,-398 1222.76,-398"/> +<polygon fill="#f51748" stroke="#f51748" points="1222.76,-401.5 1232.76,-398 1222.76,-394.5 1222.76,-401.5"/> +</g> +<!-- login_getcapbool --> +<g id="node43" class="node"> +<title>login_getcapbool</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-359" rx="87.18" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-355.3" font-family="Times-Roman" font-size="14.00">login_getcapbool</text> +</g> +<!-- _auth_checknologin->login_getcapbool --> +<g id="edge2" class="edge"> +<title>_auth_checknologin->login_getcapbool</title> +<path fill="none" stroke="#f51748" d="M380.97,-742.77C380.97,-665.31 380.97,-353 380.97,-353 380.97,-353 933.38,-353 933.38,-353"/> +<polygon fill="#f51748" stroke="#f51748" points="933.38,-356.5 943.38,-353 933.38,-349.5 933.38,-356.5"/> +</g> +<!-- _auth_validuser --> +<g id="node5" class="node"> +<title>_auth_validuser</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-875" rx="81.49" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-871.3" font-family="Times-Roman" font-size="14.00">_auth_validuser</text> +</g> +<!-- auth_approval --> +<g id="node6" class="node"> +<title>auth_approval</title> +<ellipse fill="none" stroke="black" cx="719.46" cy="-942" rx="74.99" ry="18"/> +<text text-anchor="middle" x="719.46" y="-938.3" font-family="Times-Roman" font-size="14.00">auth_approval</text> +</g> +<!-- auth_approval->_auth_validuser --> +<g id="edge7" class="edge"> +<title>auth_approval->_auth_validuser</title> +<path fill="none" stroke="#8f94eb" d="M768.94,-928.5C842.72,-928.5 972.97,-928.5 972.97,-928.5 972.97,-928.5 972.97,-898.78 972.97,-898.78"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="976.47,-898.78 972.97,-888.78 969.47,-898.78 976.47,-898.78"/> +</g> +<!-- auth_open --> +<g id="node12" class="node"> +<title>auth_open</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1313" rx="57.69" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1309.3" font-family="Times-Roman" font-size="14.00">auth_open</text> +</g> +<!-- auth_approval->auth_open --> +<g id="edge11" class="edge"> +<title>auth_approval->auth_open</title> +<path fill="none" stroke="#8f94eb" d="M791.98,-946.5C842.54,-946.5 900.97,-946.5 900.97,-946.5 900.97,-946.5 900.97,-1309.4 900.97,-1309.4 900.97,-1309.4 958.8,-1309.4 958.8,-1309.4"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="958.8,-1312.9 968.8,-1309.4 958.8,-1305.9 958.8,-1312.9"/> +</g> +<!-- auth_close --> +<g id="node14" class="node"> +<title>auth_close</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1601" rx="58.49" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1597.3" font-family="Times-Roman" font-size="14.00">auth_close</text> +</g> +<!-- auth_approval->auth_close --> +<g id="edge17" class="edge"> +<title>auth_approval->auth_close</title> +<path fill="none" stroke="#8f94eb" d="M777.91,-953.25C813.03,-953.25 850.97,-953.25 850.97,-953.25 850.97,-953.25 850.97,-1597.4 850.97,-1597.4 850.97,-1597.4 958,-1597.4 958,-1597.4"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="958,-1600.9 968,-1597.4 958,-1593.9 958,-1600.9"/> +</g> +<!-- auth_getitem --> +<g id="node18" class="node"> +<title>auth_getitem</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-2177" rx="70.39" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-2173.3" font-family="Times-Roman" font-size="14.00">auth_getitem</text> +</g> +<!-- auth_approval->auth_getitem --> +<g id="edge5" class="edge"> +<title>auth_approval->auth_getitem</title> +<path fill="none" stroke="#8f94eb" d="M794.52,-942C920.77,-942 1161.97,-942 1161.97,-942 1161.97,-942 1161.97,-2177 1161.97,-2177 1161.97,-2177 1106.22,-2177 1106.22,-2177"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="1106.22,-2173.5 1096.22,-2177 1106.22,-2180.5 1106.22,-2173.5"/> +</g> +<!-- auth_setitem --> +<g id="node19" class="node"> +<title>auth_setitem</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1169" rx="69.59" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1165.3" font-family="Times-Roman" font-size="14.00">auth_setitem</text> +</g> +<!-- auth_approval->auth_setitem --> +<g id="edge13" class="edge"> +<title>auth_approval->auth_setitem</title> +<path fill="none" stroke="#8f94eb" d="M793.88,-939.75C955.47,-939.75 1323.97,-939.75 1323.97,-939.75 1323.97,-939.75 1323.97,-1141.01 1323.97,-1141.01"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="1320.47,-1141.01 1323.97,-1151.01 1327.47,-1141.01 1320.47,-1141.01"/> +</g> +<!-- auth_check_expire --> +<g id="node26" class="node"> +<title>auth_check_expire</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-2033" rx="94.48" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-2029.3" font-family="Times-Roman" font-size="14.00">auth_check_expire</text> +</g> +<!-- auth_approval->auth_check_expire --> +<g id="edge14" class="edge"> +<title>auth_approval->auth_check_expire</title> +<path fill="none" stroke="#8f94eb" d="M769.03,-955.5C801.51,-955.5 837.97,-955.5 837.97,-955.5 837.97,-955.5 837.97,-2027 837.97,-2027 837.97,-2027 926.78,-2027 926.78,-2027"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="926.78,-2030.5 936.78,-2027 926.78,-2023.5 926.78,-2030.5"/> +</g> +<!-- auth_call --> +<g id="node28" class="node"> +<title>auth_call</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1457" rx="50.89" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1453.3" font-family="Times-Roman" font-size="14.00">auth_call</text> +</g> +<!-- auth_approval->auth_call --> +<g id="edge16" class="edge"> +<title>auth_approval->auth_call</title> +<path fill="none" stroke="#8f94eb" d="M788.89,-948.75C835.71,-948.75 888.97,-948.75 888.97,-948.75 888.97,-948.75 888.97,-1451 888.97,-1451 888.97,-1451 967.87,-1451 967.87,-1451"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="967.87,-1454.5 977.87,-1451 967.87,-1447.5 967.87,-1454.5"/> +</g> +<!-- auth_setstate --> +<g id="node33" class="node"> +<title>auth_setstate</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1529" rx="71.49" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1525.3" font-family="Times-Roman" font-size="14.00">auth_setstate</text> +</g> +<!-- auth_approval->auth_setstate --> +<g id="edge12" class="edge"> +<title>auth_approval->auth_setstate</title> +<path fill="none" stroke="#8f94eb" d="M784.47,-951C827.47,-951 875.97,-951 875.97,-951 875.97,-951 875.97,-1520 875.97,-1520 875.97,-1520 953.5,-1520 953.5,-1520"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="953.5,-1523.5 963.5,-1520 953.5,-1516.5 953.5,-1523.5"/> +</g> +<!-- auth_getstate --> +<g id="node35" class="node"> +<title>auth_getstate</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-2105" rx="72.59" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-2101.3" font-family="Times-Roman" font-size="14.00">auth_getstate</text> +</g> +<!-- auth_approval->auth_getstate --> +<g id="edge18" class="edge"> +<title>auth_approval->auth_getstate</title> +<path fill="none" stroke="#8f94eb" d="M755.66,-957.75C788.22,-957.75 830.97,-957.75 830.97,-957.75 830.97,-957.75 830.97,-2099 830.97,-2099 830.97,-2099 947.04,-2099 947.04,-2099"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="947.04,-2102.5 957.04,-2099 947.04,-2095.5 947.04,-2102.5"/> +</g> +<!-- auth_getpwd --> +<g id="node36" class="node"> +<title>auth_getpwd</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1241" rx="69.59" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1237.3" font-family="Times-Roman" font-size="14.00">auth_getpwd</text> +</g> +<!-- auth_approval->auth_getpwd --> +<g id="edge6" class="edge"> +<title>auth_approval->auth_getpwd</title> +<path fill="none" stroke="#8f94eb" d="M793.83,-944.25C848.74,-944.25 913.97,-944.25 913.97,-944.25 913.97,-944.25 913.97,-1241 913.97,-1241 913.97,-1241 945.89,-1241 945.89,-1241"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="945.89,-1244.5 955.89,-1241 945.89,-1237.5 945.89,-1244.5"/> +</g> +<!-- login_getclass --> +<g id="node37" class="node"> +<title>login_getclass</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-394" rx="74.19" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-390.3" font-family="Times-Roman" font-size="14.00">login_getclass</text> +</g> +<!-- auth_approval->login_getclass --> +<g id="edge8" class="edge"> +<title>auth_approval->login_getclass</title> +<path fill="none" stroke="#8f94eb" d="M784.19,-933C975.17,-933 1522.97,-933 1522.97,-933 1522.97,-933 1522.97,-412.04 1522.97,-412.04"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="1526.47,-412.04 1522.97,-402.04 1519.47,-412.04 1526.47,-412.04"/> +</g> +<!-- auth_approval->login_getcapstr --> +<g id="edge10" class="edge"> +<title>auth_approval->login_getcapstr</title> +<path fill="none" stroke="#8f94eb" d="M778.01,-930.75C922.42,-930.75 1283.97,-930.75 1283.97,-930.75 1283.97,-930.75 1283.97,-424.82 1283.97,-424.82"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="1287.47,-424.82 1283.97,-414.82 1280.47,-424.82 1287.47,-424.82"/> +</g> +<!-- auth_approval->login_getcapbool --> +<g id="edge15" class="edge"> +<title>auth_approval->login_getcapbool</title> +<path fill="none" stroke="#8f94eb" d="M755.67,-926.25C798.19,-926.25 862.97,-926.25 862.97,-926.25 862.97,-926.25 862.97,-365 862.97,-365 862.97,-365 933.38,-365 933.38,-365"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="933.38,-368.5 943.38,-365 933.38,-361.5 933.38,-368.5"/> +</g> +<!-- login_close --> +<g id="node44" class="node"> +<title>login_close</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-466" rx="59.59" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-462.3" font-family="Times-Roman" font-size="14.00">login_close</text> +</g> +<!-- auth_approval->login_close --> +<g id="edge9" class="edge"> +<title>auth_approval->login_close</title> +<path fill="none" stroke="#8f94eb" d="M789.07,-935.25C986.53,-935.25 1535.97,-935.25 1535.97,-935.25 1535.97,-935.25 1535.97,-484.14 1535.97,-484.14"/> +<polygon fill="#8f94eb" stroke="#8f94eb" points="1539.47,-484.14 1535.97,-474.14 1532.47,-484.14 1539.47,-484.14"/> +</g> +<!-- auth_usercheck --> +<g id="node7" class="node"> +<title>auth_usercheck</title> +<ellipse fill="none" stroke="black" cx="413.22" cy="-1014" rx="81.49" ry="18"/> +<text text-anchor="middle" x="413.22" y="-1010.3" font-family="Times-Roman" font-size="14.00">auth_usercheck</text> +</g> +<!-- auth_usercheck->_auth_validuser --> +<g id="edge19" class="edge"> +<title>auth_usercheck->_auth_validuser</title> +<path fill="none" stroke="#96fc6d" d="M386.97,-996.92C386.97,-985.29 386.97,-972 386.97,-972 386.97,-972 999.97,-972 999.97,-972 999.97,-972 999.97,-902.34 999.97,-902.34"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="1003.47,-902.34 999.97,-892.34 996.47,-902.34 1003.47,-902.34"/> +</g> +<!-- auth_verify --> +<g id="node11" class="node"> +<title>auth_verify</title> +<ellipse fill="none" stroke="black" cx="719.46" cy="-1014" rx="61.19" ry="18"/> +<text text-anchor="middle" x="719.46" y="-1010.3" font-family="Times-Roman" font-size="14.00">auth_verify</text> +</g> +<!-- auth_usercheck->auth_verify --> +<g id="edge26" class="edge"> +<title>auth_usercheck->auth_verify</title> +<path fill="none" stroke="#96fc6d" d="M483.86,-1023C483.86,-1023 656.37,-1023 656.37,-1023"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="656.37,-1026.5 666.37,-1023 656.37,-1019.5 656.37,-1026.5"/> +</g> +<!-- auth_usercheck->auth_open --> +<g id="edge23" class="edge"> +<title>auth_usercheck->auth_open</title> +<path fill="none" stroke="#96fc6d" d="M453.97,-1029.79C453.97,-1092.92 453.97,-1323.8 453.97,-1323.8 453.97,-1323.8 969.39,-1323.8 969.39,-1323.8"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="969.39,-1327.3 979.39,-1323.8 969.39,-1320.3 969.39,-1327.3"/> +</g> +<!-- auth_usercheck->auth_setitem --> +<g id="edge24" class="edge"> +<title>auth_usercheck->auth_setitem</title> +<path fill="none" stroke="#96fc6d" d="M413.97,-1032C413.97,-1041 413.97,-1050 413.97,-1050 413.97,-1050 1263.97,-1050 1263.97,-1050 1263.97,-1050 1263.97,-1145.81 1263.97,-1145.81"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="1260.47,-1145.81 1263.97,-1155.81 1267.47,-1145.81 1260.47,-1145.81"/> +</g> +<!-- auth_setdata --> +<g id="node23" class="node"> +<title>auth_setdata</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-2249" rx="69.59" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-2245.3" font-family="Times-Roman" font-size="14.00">auth_setdata</text> +</g> +<!-- auth_usercheck->auth_setdata --> +<g id="edge25" class="edge"> +<title>auth_usercheck->auth_setdata</title> +<path fill="none" stroke="#96fc6d" d="M372.97,-1029.69C372.97,-1173.35 372.97,-2255 372.97,-2255 372.97,-2255 950.12,-2255 950.12,-2255"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="950.12,-2258.5 960.12,-2255 950.12,-2251.5 950.12,-2258.5"/> +</g> +<!-- auth_usercheck->login_getclass --> +<g id="edge20" class="edge"> +<title>auth_usercheck->login_getclass</title> +<path fill="none" stroke="#96fc6d" d="M440.97,-996.9C440.97,-990.16 440.97,-984 440.97,-984 440.97,-984 1526.97,-984 1526.97,-984 1526.97,-984 1526.97,-413.75 1526.97,-413.75"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="1530.47,-413.75 1526.97,-403.75 1523.47,-413.75 1530.47,-413.75"/> +</g> +<!-- login_getstyle --> +<g id="node38" class="node"> +<title>login_getstyle</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-287" rx="73.39" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-283.3" font-family="Times-Roman" font-size="14.00">login_getstyle</text> +</g> +<!-- auth_usercheck->login_getstyle --> +<g id="edge21" class="edge"> +<title>auth_usercheck->login_getstyle</title> +<path fill="none" stroke="#96fc6d" d="M483.88,-1005C516.86,-1005 548.97,-1005 548.97,-1005 548.97,-1005 548.97,-281 548.97,-281 548.97,-281 946.37,-281 946.37,-281"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="946.37,-284.5 956.37,-281 946.37,-277.5 946.37,-284.5"/> +</g> +<!-- auth_usercheck->login_close --> +<g id="edge22" class="edge"> +<title>auth_usercheck->login_close</title> +<path fill="none" stroke="#96fc6d" d="M494.57,-1014C538.96,-1014 584.97,-1014 584.97,-1014 584.97,-1014 584.97,-469.6 584.97,-469.6 584.97,-469.6 1520.7,-469.6 1520.7,-469.6"/> +<polygon fill="#96fc6d" stroke="#96fc6d" points="1520.7,-473.1 1530.7,-469.6 1520.7,-466.1 1520.7,-473.1"/> +</g> +<!-- auth_userokay --> +<g id="node8" class="node"> +<title>auth_userokay</title> +<ellipse fill="none" stroke="black" cx="110.89" cy="-1014" rx="76.09" ry="18"/> +<text text-anchor="middle" x="110.89" y="-1010.3" font-family="Times-Roman" font-size="14.00">auth_userokay</text> +</g> +<!-- auth_userokay->auth_usercheck --> +<g id="edge27" class="edge"> +<title>auth_userokay->auth_usercheck</title> +<path fill="none" stroke="#3eb09b" d="M182.92,-1020C182.92,-1020 326.46,-1020 326.46,-1020"/> +<polygon fill="#3eb09b" stroke="#3eb09b" points="326.46,-1023.5 336.46,-1020 326.46,-1016.5 326.46,-1023.5"/> +</g> +<!-- auth_userokay->auth_close --> +<g id="edge28" class="edge"> +<title>auth_userokay->auth_close</title> +<path fill="none" stroke="#3eb09b" d="M182.76,-1008C191.25,-1008 196.97,-1008 196.97,-1008 196.97,-1008 196.97,-1611.8 196.97,-1611.8 196.97,-1611.8 968.76,-1611.8 968.76,-1611.8"/> +<polygon fill="#3eb09b" stroke="#3eb09b" points="968.76,-1615.3 978.76,-1611.8 968.76,-1608.3 968.76,-1615.3"/> +</g> +<!-- auth_userchallenge --> +<g id="node9" class="node"> +<title>auth_userchallenge</title> +<ellipse fill="none" stroke="black" cx="719.46" cy="-870" rx="98.58" ry="18"/> +<text text-anchor="middle" x="719.46" y="-866.3" font-family="Times-Roman" font-size="14.00">auth_userchallenge</text> +</g> +<!-- auth_userchallenge->_auth_validuser --> +<g id="edge29" class="edge"> +<title>auth_userchallenge->_auth_validuser</title> +<path fill="none" stroke="#0db8d4" d="M794.37,-881.8C794.37,-881.8 940.22,-881.8 940.22,-881.8"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="940.22,-885.3 950.22,-881.8 940.22,-878.3 940.22,-885.3"/> +</g> +<!-- auth_userchallenge->auth_open --> +<g id="edge32" class="edge"> +<title>auth_userchallenge->auth_open</title> +<path fill="none" stroke="#0db8d4" d="M818.41,-869.4C863.92,-869.4 907.97,-869.4 907.97,-869.4 907.97,-869.4 907.97,-1302.2 907.97,-1302.2 907.97,-1302.2 969.16,-1302.2 969.16,-1302.2"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="969.16,-1305.7 979.16,-1302.2 969.16,-1298.7 969.16,-1305.7"/> +</g> +<!-- auth_userchallenge->auth_close --> +<g id="edge35" class="edge"> +<title>auth_userchallenge->auth_close</title> +<path fill="none" stroke="#0db8d4" d="M813.46,-875.6C837.63,-875.6 856.97,-875.6 856.97,-875.6 856.97,-875.6 856.97,-1590.2 856.97,-1590.2 856.97,-1590.2 968.69,-1590.2 968.69,-1590.2"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="968.69,-1593.7 978.69,-1590.2 968.69,-1586.7 968.69,-1593.7"/> +</g> +<!-- auth_challenge --> +<g id="node15" class="node"> +<title>auth_challenge</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1169" rx="79.09" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1165.3" font-family="Times-Roman" font-size="14.00">auth_challenge</text> +</g> +<!-- auth_userchallenge->auth_challenge --> +<g id="edge36" class="edge"> +<title>auth_userchallenge->auth_challenge</title> +<path fill="none" stroke="#0db8d4" d="M811.2,-863.2C864,-863.2 919.97,-863.2 919.97,-863.2 919.97,-863.2 919.97,-1169 919.97,-1169 919.97,-1169 936.37,-1169 936.37,-1169"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="936.37,-1172.5 946.37,-1169 936.37,-1165.5 936.37,-1172.5"/> +</g> +<!-- auth_userchallenge->auth_setitem --> +<g id="edge34" class="edge"> +<title>auth_userchallenge->auth_setitem</title> +<path fill="none" stroke="#0db8d4" d="M804.97,-879.1C804.97,-897.53 804.97,-937.5 804.97,-937.5 804.97,-937.5 1342.97,-937.5 1342.97,-937.5 1342.97,-937.5 1342.97,-1142.72 1342.97,-1142.72"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="1339.47,-1142.72 1342.97,-1152.72 1346.47,-1142.72 1339.47,-1142.72"/> +</g> +<!-- auth_userchallenge->login_getclass --> +<g id="edge30" class="edge"> +<title>auth_userchallenge->login_getclass</title> +<path fill="none" stroke="#0db8d4" d="M781.97,-855.84C781.97,-839.54 781.97,-815 781.97,-815 781.97,-815 1518.97,-815 1518.97,-815 1518.97,-815 1518.97,-409.63 1518.97,-409.63"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="1522.47,-409.63 1518.97,-399.63 1515.47,-409.63 1522.47,-409.63"/> +</g> +<!-- auth_userchallenge->login_getstyle --> +<g id="edge31" class="edge"> +<title>auth_userchallenge->login_getstyle</title> +<path fill="none" stroke="#0db8d4" d="M793.97,-857.95C793.97,-775.09 793.97,-293 793.97,-293 793.97,-293 946.17,-293 946.17,-293"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="946.17,-296.5 956.17,-293 946.17,-289.5 946.17,-296.5"/> +</g> +<!-- auth_userchallenge->login_close --> +<g id="edge33" class="edge"> +<title>auth_userchallenge->login_close</title> +<path fill="none" stroke="#0db8d4" d="M805.97,-861.05C805.97,-802.52 805.97,-476.8 805.97,-476.8 805.97,-476.8 1531.29,-476.8 1531.29,-476.8"/> +<polygon fill="#0db8d4" stroke="#0db8d4" points="1531.29,-480.3 1541.29,-476.8 1531.29,-473.3 1531.29,-480.3"/> +</g> +<!-- auth_userresponse --> +<g id="node10" class="node"> +<title>auth_userresponse</title> +<ellipse fill="none" stroke="black" cx="719.46" cy="-1086" rx="96.38" ry="18"/> +<text text-anchor="middle" x="719.46" y="-1082.3" font-family="Times-Roman" font-size="14.00">auth_userresponse</text> +</g> +<!-- auth_userresponse->_auth_validuser --> +<g id="edge39" class="edge"> +<title>auth_userresponse->_auth_validuser</title> +<path fill="none" stroke="#cc7ac2" d="M815.92,-1086C914.42,-1086 1052.97,-1086 1052.97,-1086 1052.97,-1086 1052.97,-902.27 1052.97,-902.27"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="1056.47,-902.27 1052.97,-892.27 1049.47,-902.27 1056.47,-902.27"/> +</g> +<!-- auth_userresponse->auth_close --> +<g id="edge40" class="edge"> +<title>auth_userresponse->auth_close</title> +<path fill="none" stroke="#cc7ac2" d="M760.97,-1102.38C760.97,-1190.53 760.97,-1604.6 760.97,-1604.6 760.97,-1604.6 958.25,-1604.6 958.25,-1604.6"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="958.25,-1608.1 968.25,-1604.6 958.25,-1601.1 958.25,-1608.1"/> +</g> +<!-- auth_userresponse->auth_getitem --> +<g id="edge38" class="edge"> +<title>auth_userresponse->auth_getitem</title> +<path fill="none" stroke="#cc7ac2" d="M678.97,-1102.36C678.97,-1238.82 678.97,-2183 678.97,-2183 678.97,-2183 949.25,-2183 949.25,-2183"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="949.25,-2186.5 959.25,-2183 949.25,-2179.5 949.25,-2186.5"/> +</g> +<!-- auth_userresponse->auth_setdata --> +<g id="edge41" class="edge"> +<title>auth_userresponse->auth_setdata</title> +<path fill="none" stroke="#cc7ac2" d="M650.97,-1098.99C650.97,-1226.11 650.97,-2243 650.97,-2243 650.97,-2243 949.99,-2243 949.99,-2243"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="949.99,-2246.5 959.99,-2243 949.99,-2239.5 949.99,-2246.5"/> +</g> +<!-- auth_userresponse->auth_check_expire --> +<g id="edge43" class="edge"> +<title>auth_userresponse->auth_check_expire</title> +<path fill="none" stroke="#cc7ac2" d="M733.97,-1104.19C733.97,-1235.94 733.97,-2039 733.97,-2039 733.97,-2039 926.58,-2039 926.58,-2039"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="926.58,-2042.5 936.58,-2039 926.58,-2035.5 926.58,-2042.5"/> +</g> +<!-- auth_userresponse->auth_setstate --> +<g id="edge37" class="edge"> +<title>auth_userresponse->auth_setstate</title> +<path fill="none" stroke="#cc7ac2" d="M788.97,-1098.56C788.97,-1171.56 788.97,-1538 788.97,-1538 788.97,-1538 953.49,-1538 953.49,-1538"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="953.49,-1541.5 963.49,-1538 953.49,-1534.5 953.49,-1541.5"/> +</g> +<!-- auth_userresponse->auth_getstate --> +<g id="edge42" class="edge"> +<title>auth_userresponse->auth_getstate</title> +<path fill="none" stroke="#cc7ac2" d="M705.97,-1104.23C705.97,-1241.74 705.97,-2111 705.97,-2111 705.97,-2111 946.71,-2111 946.71,-2111"/> +<polygon fill="#cc7ac2" stroke="#cc7ac2" points="946.71,-2114.5 956.71,-2111 946.71,-2107.5 946.71,-2114.5"/> +</g> +<!-- auth_verify->_auth_validuser --> +<g id="edge48" class="edge"> +<title>auth_verify->_auth_validuser</title> +<path fill="none" stroke="#83192f" d="M760.16,-1000.5C843.54,-1000.5 1025.97,-1000.5 1025.97,-1000.5 1025.97,-1000.5 1025.97,-903.25 1025.97,-903.25"/> +<polygon fill="#83192f" stroke="#83192f" points="1029.47,-903.25 1025.97,-893.25 1022.47,-903.25 1029.47,-903.25"/> +</g> +<!-- auth_verify->auth_open --> +<g id="edge44" class="edge"> +<title>auth_verify->auth_open</title> +<path fill="none" stroke="#83192f" d="M778.88,-1009.5C829.8,-1009.5 894.97,-1009.5 894.97,-1009.5 894.97,-1009.5 894.97,-1316.6 894.97,-1316.6 894.97,-1316.6 958.73,-1316.6 958.73,-1316.6"/> +<polygon fill="#83192f" stroke="#83192f" points="958.73,-1320.1 968.73,-1316.6 958.73,-1313.1 958.73,-1320.1"/> +</g> +<!-- auth_verify->auth_getitem --> +<g id="edge47" class="edge"> +<title>auth_verify->auth_getitem</title> +<path fill="none" stroke="#83192f" d="M759.95,-1027.5C789.7,-1027.5 824.97,-1027.5 824.97,-1027.5 824.97,-1027.5 824.97,-2171 824.97,-2171 824.97,-2171 949.41,-2171 949.41,-2171"/> +<polygon fill="#83192f" stroke="#83192f" points="949.41,-2174.5 959.41,-2171 949.41,-2167.5 949.41,-2174.5"/> +</g> +<!-- auth_verify->auth_setitem --> +<g id="edge46" class="edge"> +<title>auth_verify->auth_setitem</title> +<path fill="none" stroke="#83192f" d="M772.62,-1005C916.71,-1005 1303.97,-1005 1303.97,-1005 1303.97,-1005 1303.97,-1141.14 1303.97,-1141.14"/> +<polygon fill="#83192f" stroke="#83192f" points="1300.47,-1141.14 1303.97,-1151.14 1307.47,-1141.14 1300.47,-1141.14"/> +</g> +<!-- auth_verify->auth_call --> +<g id="edge50" class="edge"> +<title>auth_verify->auth_call</title> +<path fill="none" stroke="#83192f" d="M780.9,-1014C826.92,-1014 881.97,-1014 881.97,-1014 881.97,-1014 881.97,-1463 881.97,-1463 881.97,-1463 967.83,-1463 967.83,-1463"/> +<polygon fill="#83192f" stroke="#83192f" points="967.83,-1466.5 977.83,-1463 967.83,-1459.5 967.83,-1466.5"/> +</g> +<!-- auth_verify->auth_setstate --> +<g id="edge45" class="edge"> +<title>auth_verify->auth_setstate</title> +<path fill="none" stroke="#83192f" d="M778.94,-1018.5C821.01,-1018.5 869.97,-1018.5 869.97,-1018.5 869.97,-1018.5 869.97,-1529 869.97,-1529 869.97,-1529 943.76,-1529 943.76,-1529"/> +<polygon fill="#83192f" stroke="#83192f" points="943.76,-1532.5 953.76,-1529 943.76,-1525.5 943.76,-1532.5"/> +</g> +<!-- auth_set_va_list --> +<g id="node34" class="node"> +<title>auth_set_va_list</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1673" rx="81.79" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1669.3" font-family="Times-Roman" font-size="14.00">auth_set_va_list</text> +</g> +<!-- auth_verify->auth_set_va_list --> +<g id="edge49" class="edge"> +<title>auth_verify->auth_set_va_list</title> +<path fill="none" stroke="#83192f" d="M772.62,-1023C806.47,-1023 843.97,-1023 843.97,-1023 843.97,-1023 843.97,-1673 843.97,-1673 843.97,-1673 933.6,-1673 933.6,-1673"/> +<polygon fill="#83192f" stroke="#83192f" points="933.6,-1676.5 943.6,-1673 933.6,-1669.5 933.6,-1676.5"/> +</g> +<!-- auth_clean --> +<g id="node13" class="node"> +<title>auth_clean</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1385" rx="59.59" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1381.3" font-family="Times-Roman" font-size="14.00">auth_clean</text> +</g> +<!-- auth_clrenv --> +<g id="node17" class="node"> +<title>auth_clrenv</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1358" rx="63.09" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1354.3" font-family="Times-Roman" font-size="14.00">auth_clrenv</text> +</g> +<!-- auth_clean->auth_clrenv --> +<g id="edge51" class="edge"> +<title>auth_clean->auth_clrenv</title> +<path fill="none" stroke="#759227" d="M1065.35,-1371.5C1065.35,-1371.5 1261.02,-1371.5 1261.02,-1371.5"/> +<polygon fill="#759227" stroke="#759227" points="1261.02,-1375 1271.02,-1371.5 1261.02,-1368 1261.02,-1375"/> +</g> +<!-- auth_clean->auth_setitem --> +<g id="edge52" class="edge"> +<title>auth_clean->auth_setitem</title> +<path fill="none" stroke="#759227" d="M1054.75,-1369.25C1081.14,-1369.25 1115.97,-1369.25 1115.97,-1369.25 1115.97,-1369.25 1115.97,-1163 1115.97,-1163 1115.97,-1163 1237.44,-1163 1237.44,-1163"/> +<polygon fill="#759227" stroke="#759227" points="1237.44,-1166.5 1247.44,-1163 1237.44,-1159.5 1237.44,-1166.5"/> +</g> +<!-- auth_setenv --> +<g id="node16" class="node"> +<title>auth_setenv</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1646" rx="65.79" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1642.3" font-family="Times-Roman" font-size="14.00">auth_setenv</text> +</g> +<!-- auth_close->auth_setenv --> +<g id="edge53" class="edge"> +<title>auth_close->auth_setenv</title> +<path fill="none" stroke="#4a5505" d="M1025.97,-1619.35C1025.97,-1630.07 1025.97,-1641.5 1025.97,-1641.5 1025.97,-1641.5 1239.36,-1641.5 1239.36,-1641.5"/> +<polygon fill="#4a5505" stroke="#4a5505" points="1239.36,-1645 1249.36,-1641.5 1239.36,-1638 1239.36,-1645"/> +</g> +<!-- auth_challenge->_auth_validuser --> +<g id="edge54" class="edge"> +<title>auth_challenge->_auth_validuser</title> +<path fill="none" stroke="#5e3ac3" d="M1078.97,-1155.6C1078.97,-1155.6 1078.97,-898.81 1078.97,-898.81"/> +<polygon fill="#5e3ac3" stroke="#5e3ac3" points="1082.47,-898.81 1078.97,-888.81 1075.47,-898.82 1082.47,-898.81"/> +</g> +<!-- auth_getvalue --> +<g id="node25" class="node"> +<title>auth_getvalue</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1259" rx="74.99" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1255.3" font-family="Times-Roman" font-size="14.00">auth_getvalue</text> +</g> +<!-- auth_challenge->auth_getvalue --> +<g id="edge55" class="edge"> +<title>auth_challenge->auth_getvalue</title> +<path fill="none" stroke="#5e3ac3" d="M1101.97,-1174.31C1101.97,-1192.33 1101.97,-1250 1101.97,-1250 1101.97,-1250 1238.3,-1250 1238.3,-1250"/> +<polygon fill="#5e3ac3" stroke="#5e3ac3" points="1238.3,-1253.5 1248.3,-1250 1238.3,-1246.5 1238.3,-1253.5"/> +</g> +<!-- auth_setitem->_auth_validuser --> +<g id="edge57" class="edge"> +<title>auth_setitem->_auth_validuser</title> +<path fill="none" stroke="#e67693" d="M1362.97,-1156.37C1362.97,-1100.09 1362.97,-875 1362.97,-875 1362.97,-875 1117.34,-875 1117.34,-875"/> +<polygon fill="#e67693" stroke="#e67693" points="1117.34,-871.5 1107.34,-875 1117.34,-878.5 1117.34,-871.5"/> +</g> +<!-- auth_setitem->auth_setitem --> +<g id="edge56" class="edge"> +<title>auth_setitem->auth_setitem</title> +<path fill="none" stroke="#e67693" d="M1247.45,-1175C1221.6,-1175 1197.97,-1175 1197.97,-1175 1197.97,-1175 1197.97,-1205 1197.97,-1205 1197.97,-1205 1474.97,-1205 1474.97,-1205 1474.97,-1205 1474.97,-1169 1474.97,-1169 1474.97,-1169 1392.67,-1169 1392.67,-1169"/> +<polygon fill="#e67693" stroke="#e67693" points="1392.67,-1165.5 1382.67,-1169 1392.67,-1172.5 1392.67,-1165.5"/> +</g> +<!-- auth_setoption --> +<g id="node20" class="node"> +<title>auth_setoption</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1889" rx="77.19" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1885.3" font-family="Times-Roman" font-size="14.00">auth_setoption</text> +</g> +<!-- auth_clroptions --> +<g id="node21" class="node"> +<title>auth_clroptions</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1817" rx="79.09" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1813.3" font-family="Times-Roman" font-size="14.00">auth_clroptions</text> +</g> +<!-- auth_clroption --> +<g id="node22" class="node"> +<title>auth_clroption</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1745" rx="74.99" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1741.3" font-family="Times-Roman" font-size="14.00">auth_clroption</text> +</g> +<!-- auth_setpwd --> +<g id="node24" class="node"> +<title>auth_setpwd</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1997" rx="68.49" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1993.3" font-family="Times-Roman" font-size="14.00">auth_setpwd</text> +</g> +<!-- auth_check_expire->auth_setpwd --> +<g id="edge58" class="edge"> +<title>auth_check_expire->auth_setpwd</title> +<path fill="none" stroke="#739550" d="M1120.08,-2033C1204.16,-2033 1313.97,-2033 1313.97,-2033 1313.97,-2033 1313.97,-2025 1313.97,-2025"/> +<polygon fill="#739550" stroke="#739550" points="1317.47,-2025 1313.97,-2015 1310.47,-2025 1317.47,-2025"/> +</g> +<!-- auth_check_change --> +<g id="node27" class="node"> +<title>auth_check_change</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-1961" rx="99.38" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-1957.3" font-family="Times-Roman" font-size="14.00">auth_check_change</text> +</g> +<!-- auth_check_change->auth_setpwd --> +<g id="edge59" class="edge"> +<title>auth_check_change->auth_setpwd</title> +<path fill="none" stroke="#902d9d" d="M1125.3,-1961C1208.63,-1961 1313.97,-1961 1313.97,-1961 1313.97,-1961 1313.97,-1969 1313.97,-1969"/> +<polygon fill="#902d9d" stroke="#902d9d" points="1310.47,-1969 1313.97,-1979 1317.47,-1969 1310.47,-1969"/> +</g> +<!-- auth_call->auth_clrenv --> +<g id="edge63" class="edge"> +<title>auth_call->auth_clrenv</title> +<path fill="none" stroke="#a9e6c9" d="M1053.83,-1442C1082.56,-1442 1122.97,-1442 1122.97,-1442 1122.97,-1442 1122.97,-1373.75 1122.97,-1373.75 1122.97,-1373.75 1272.53,-1373.75 1272.53,-1373.75"/> +<polygon fill="#a9e6c9" stroke="#a9e6c9" points="1272.53,-1377.25 1282.53,-1373.75 1272.53,-1370.25 1272.53,-1377.25"/> +</g> +<!-- _auth_spool --> +<g id="node30" class="node"> +<title>_auth_spool</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1574" rx="63.89" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1570.3" font-family="Times-Roman" font-size="14.00">_auth_spool</text> +</g> +<!-- auth_call->_auth_spool --> +<g id="edge61" class="edge"> +<title>auth_call->_auth_spool</title> +<path fill="none" stroke="#a9e6c9" d="M1069.6,-1466C1093.79,-1466 1118.97,-1466 1118.97,-1466 1118.97,-1466 1118.97,-1569.5 1118.97,-1569.5 1118.97,-1569.5 1241.39,-1569.5 1241.39,-1569.5"/> +<polygon fill="#a9e6c9" stroke="#a9e6c9" points="1241.39,-1573 1251.39,-1569.5 1241.39,-1566 1241.39,-1573"/> +</g> +<!-- _add_rmlist --> +<g id="node31" class="node"> +<title>_add_rmlist</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1430" rx="61.99" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1426.3" font-family="Times-Roman" font-size="14.00">_add_rmlist</text> +</g> +<!-- auth_call->_add_rmlist --> +<g id="edge62" class="edge"> +<title>auth_call->_add_rmlist</title> +<path fill="none" stroke="#a9e6c9" d="M1063.56,-1445C1063.56,-1445 1268.72,-1445 1268.72,-1445"/> +<polygon fill="#a9e6c9" stroke="#a9e6c9" points="1268.72,-1448.5 1278.72,-1445 1268.72,-1441.5 1268.72,-1448.5"/> +</g> +<!-- _auth_next_arg --> +<g id="node32" class="node"> +<title>_auth_next_arg</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-1502" rx="79.09" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-1498.3" font-family="Times-Roman" font-size="14.00">_auth_next_arg</text> +</g> +<!-- auth_call->_auth_next_arg --> +<g id="edge60" class="edge"> +<title>auth_call->_auth_next_arg</title> +<path fill="none" stroke="#a9e6c9" d="M1076.55,-1457C1141.33,-1457 1244.97,-1457 1244.97,-1457 1244.97,-1457 1244.97,-1482.46 1244.97,-1482.46"/> +<polygon fill="#a9e6c9" stroke="#a9e6c9" points="1241.47,-1482.46 1244.97,-1492.46 1248.47,-1482.46 1241.47,-1482.46"/> +</g> +<!-- _recv_fd --> +<g id="node29" class="node"> +<title>_recv_fd</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-1574" rx="48.19" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-1570.3" font-family="Times-Roman" font-size="14.00">_recv_fd</text> +</g> +<!-- _auth_spool->_recv_fd --> +<g id="edge64" class="edge"> +<title>_auth_spool->_recv_fd</title> +<path fill="none" stroke="#977e1c" d="M1376.82,-1574C1376.82,-1574 1531.07,-1574 1531.07,-1574"/> +<polygon fill="#977e1c" stroke="#977e1c" points="1531.07,-1577.5 1541.07,-1574 1531.07,-1570.5 1531.07,-1577.5"/> +</g> +<!-- login_getstyle->login_getcapstr --> +<g id="edge65" class="edge"> +<title>login_getstyle->login_getcapstr</title> +<path fill="none" stroke="#51a344" d="M1094.6,-280.67C1172.31,-280.67 1287.97,-280.67 1287.97,-280.67 1287.97,-280.67 1287.97,-370.69 1287.97,-370.69"/> +<polygon fill="#51a344" stroke="#51a344" points="1284.47,-370.69 1287.97,-380.69 1291.47,-370.69 1284.47,-370.69"/> +</g> +<!-- login_getcaptime --> +<g id="node40" class="node"> +<title>login_getcaptime</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-34" rx="87.99" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-30.3" font-family="Times-Roman" font-size="14.00">login_getcaptime</text> +</g> +<!-- login_getcapnum --> +<g id="node41" class="node"> +<title>login_getcapnum</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-178" rx="88.28" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-174.3" font-family="Times-Roman" font-size="14.00">login_getcapnum</text> +</g> +<!-- login_getcapsize --> +<g id="node42" class="node"> +<title>login_getcapsize</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-106" rx="85.29" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-102.3" font-family="Times-Roman" font-size="14.00">login_getcapsize</text> +</g> +<!-- strtolimit --> +<g id="node52" class="node"> +<title>strtolimit</title> +<ellipse fill="none" stroke="black" cx="1853.39" cy="-106" rx="51.19" ry="18"/> +<text text-anchor="middle" x="1853.39" y="-102.3" font-family="Times-Roman" font-size="14.00">strtolimit</text> +</g> +<!-- login_getcapsize->strtolimit --> +<g id="edge66" class="edge"> +<title>login_getcapsize->strtolimit</title> +<path fill="none" stroke="#97959e" d="M1674.75,-106C1674.75,-106 1791.8,-106 1791.8,-106"/> +<polygon fill="#97959e" stroke="#97959e" points="1791.8,-109.5 1801.8,-106 1791.8,-102.5 1791.8,-109.5"/> +</g> +<!-- gsetrl --> +<g id="node45" class="node"> +<title>gsetrl</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-106" rx="36.29" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-102.3" font-family="Times-Roman" font-size="14.00">gsetrl</text> +</g> +<!-- gsetrl->login_getcaptime --> +<g id="edge67" class="edge"> +<title>gsetrl->login_getcaptime</title> +<path fill="none" stroke="#35d53a" d="M1313.97,-87.83C1313.97,-66.5 1313.97,-34 1313.97,-34 1313.97,-34 1491.5,-34 1491.5,-34"/> +<polygon fill="#35d53a" stroke="#35d53a" points="1491.5,-37.5 1501.5,-34 1491.5,-30.5 1491.5,-37.5"/> +</g> +<!-- gsetrl->login_getcapnum --> +<g id="edge69" class="edge"> +<title>gsetrl->login_getcapnum</title> +<path fill="none" stroke="#35d53a" d="M1313.97,-124.29C1313.97,-145.77 1313.97,-178.5 1313.97,-178.5 1313.97,-178.5 1490.74,-178.5 1490.74,-178.5"/> +<polygon fill="#35d53a" stroke="#35d53a" points="1490.74,-182 1500.74,-178.5 1490.74,-175 1490.74,-182"/> +</g> +<!-- gsetrl->login_getcapsize --> +<g id="edge68" class="edge"> +<title>gsetrl->login_getcapsize</title> +<path fill="none" stroke="#35d53a" d="M1349.48,-106C1349.48,-106 1494.05,-106 1494.05,-106"/> +<polygon fill="#35d53a" stroke="#35d53a" points="1494.05,-109.5 1504.05,-106 1494.05,-102.5 1494.05,-109.5"/> +</g> +<!-- setclasscontext --> +<g id="node46" class="node"> +<title>setclasscontext</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-215" rx="80.69" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-211.3" font-family="Times-Roman" font-size="14.00">setclasscontext</text> +</g> +<!-- setclasscontext->login_getclass --> +<g id="edge70" class="edge"> +<title>setclasscontext->login_getclass</title> +<path fill="none" stroke="#5b8e44" d="M1101.33,-208.67C1240.26,-208.67 1521.97,-208.67 1521.97,-208.67 1521.97,-208.67 1521.97,-376.51 1521.97,-376.51"/> +<polygon fill="#5b8e44" stroke="#5b8e44" points="1518.47,-376.51 1521.97,-386.51 1525.47,-376.51 1518.47,-376.51"/> +</g> +<!-- setclasscontext->login_close --> +<g id="edge72" class="edge"> +<title>setclasscontext->login_close</title> +<path fill="none" stroke="#5b8e44" d="M1103.14,-220.33C1225.05,-220.33 1447.97,-220.33 1447.97,-220.33 1447.97,-220.33 1447.97,-455.2 1447.97,-455.2 1447.97,-455.2 1531.52,-455.2 1531.52,-455.2"/> +<polygon fill="#5b8e44" stroke="#5b8e44" points="1531.52,-458.7 1541.52,-455.2 1531.52,-451.7 1531.52,-458.7"/> +</g> +<!-- setusercontext --> +<g id="node47" class="node"> +<title>setusercontext</title> +<ellipse fill="none" stroke="black" cx="1313.07" cy="-250" rx="77.19" ry="18"/> +<text text-anchor="middle" x="1313.07" y="-246.3" font-family="Times-Roman" font-size="14.00">setusercontext</text> +</g> +<!-- setclasscontext->setusercontext --> +<g id="edge71" class="edge"> +<title>setclasscontext->setusercontext</title> +<path fill="none" stroke="#5b8e44" d="M1102.97,-220.24C1102.97,-230.11 1102.97,-250.5 1102.97,-250.5 1102.97,-250.5 1225.73,-250.5 1225.73,-250.5"/> +<polygon fill="#5b8e44" stroke="#5b8e44" points="1225.73,-254 1235.73,-250.5 1225.73,-247 1225.73,-254"/> +</g> +<!-- setusercontext->login_getclass --> +<g id="edge73" class="edge"> +<title>setusercontext->login_getclass</title> +<path fill="none" stroke="#7eb75f" d="M1388.86,-253.6C1447.57,-253.6 1518.97,-253.6 1518.97,-253.6 1518.97,-253.6 1518.97,-378.32 1518.97,-378.32"/> +<polygon fill="#7eb75f" stroke="#7eb75f" points="1515.47,-378.32 1518.97,-388.32 1522.47,-378.32 1515.47,-378.32"/> +</g> +<!-- setusercontext->login_getcapnum --> +<g id="edge75" class="edge"> +<title>setusercontext->login_getcapnum</title> +<path fill="none" stroke="#7eb75f" d="M1375.06,-239.2C1432.45,-239.2 1508.97,-239.2 1508.97,-239.2 1508.97,-239.2 1508.97,-195.5 1508.97,-195.5"/> +<polygon fill="#7eb75f" stroke="#7eb75f" points="1512.47,-195.5 1508.97,-185.5 1505.47,-195.5 1512.47,-195.5"/> +</g> +<!-- setusercontext->login_close --> +<g id="edge74" class="edge"> +<title>setusercontext->login_close</title> +<path fill="none" stroke="#7eb75f" d="M1375.21,-260.8C1399.2,-260.8 1420.97,-260.8 1420.97,-260.8 1420.97,-260.8 1420.97,-462.4 1420.97,-462.4 1420.97,-462.4 1520.5,-462.4 1520.5,-462.4"/> +<polygon fill="#7eb75f" stroke="#7eb75f" points="1520.5,-465.9 1530.5,-462.4 1520.5,-458.9 1520.5,-465.9"/> +</g> +<!-- setuserpath --> +<g id="node48" class="node"> +<title>setuserpath</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-250" rx="64.99" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-246.3" font-family="Times-Roman" font-size="14.00">setuserpath</text> +</g> +<!-- setusercontext->setuserpath --> +<g id="edge77" class="edge"> +<title>setusercontext->setuserpath</title> +<path fill="none" stroke="#7eb75f" d="M1388.95,-246.4C1388.95,-246.4 1515.4,-246.4 1515.4,-246.4"/> +<polygon fill="#7eb75f" stroke="#7eb75f" points="1515.4,-249.9 1525.4,-246.4 1515.4,-242.9 1515.4,-249.9"/> +</g> +<!-- setuserenv --> +<g id="node49" class="node"> +<title>setuserenv</title> +<ellipse fill="none" stroke="black" cx="1589.41" cy="-322" rx="60.39" ry="18"/> +<text text-anchor="middle" x="1589.41" y="-318.3" font-family="Times-Roman" font-size="14.00">setuserenv</text> +</g> +<!-- setusercontext->setuserenv --> +<g id="edge76" class="edge"> +<title>setusercontext->setuserenv</title> +<path fill="none" stroke="#7eb75f" d="M1338.97,-267.18C1338.97,-288.62 1338.97,-322.5 1338.97,-322.5 1338.97,-322.5 1518.78,-322.5 1518.78,-322.5"/> +<polygon fill="#7eb75f" stroke="#7eb75f" points="1518.78,-326 1528.78,-322.5 1518.78,-319 1518.78,-326"/> +</g> +<!-- login_setenv --> +<g id="node50" class="node"> +<title>login_setenv</title> +<ellipse fill="none" stroke="black" cx="1853.39" cy="-286" rx="67.69" ry="18"/> +<text text-anchor="middle" x="1853.39" y="-282.3" font-family="Times-Roman" font-size="14.00">login_setenv</text> +</g> +<!-- setuserpath->login_setenv --> +<g id="edge78" class="edge"> +<title>setuserpath->login_setenv</title> +<path fill="none" stroke="#35ed7d" d="M1654.41,-250C1714.31,-250 1793.97,-250 1793.97,-250 1793.97,-250 1793.97,-267.29 1793.97,-267.29"/> +<polygon fill="#35ed7d" stroke="#35ed7d" points="1790.47,-267.29 1793.97,-277.29 1797.47,-267.29 1790.47,-267.29"/> +</g> +<!-- setuserenv->login_setenv --> +<g id="edge79" class="edge"> +<title>setuserenv->login_setenv</title> +<path fill="none" stroke="#a1aa6a" d="M1589.97,-303.72C1589.97,-297.61 1589.97,-292.33 1589.97,-292.33 1589.97,-292.33 1779.96,-292.33 1779.96,-292.33"/> +<polygon fill="#a1aa6a" stroke="#a1aa6a" points="1779.96,-295.83 1789.96,-292.33 1779.96,-288.83 1779.96,-295.83"/> +</g> +<!-- expandstr --> +<g id="node55" class="node"> +<title>expandstr</title> +<ellipse fill="none" stroke="black" cx="2085.53" cy="-286" rx="56.59" ry="18"/> +<text text-anchor="middle" x="2085.53" y="-282.3" font-family="Times-Roman" font-size="14.00">expandstr</text> +</g> +<!-- login_setenv->expandstr --> +<g id="edge80" class="edge"> +<title>login_setenv->expandstr</title> +<path fill="none" stroke="#502c54" d="M1921.16,-286C1921.16,-286 2018.66,-286 2018.66,-286"/> +<polygon fill="#502c54" stroke="#502c54" points="2018.66,-289.5 2028.66,-286 2018.66,-282.5 2018.66,-289.5"/> +</g> +<!-- strtosize --> +<g id="node51" class="node"> +<title>strtosize</title> +<ellipse fill="none" stroke="black" cx="2085.53" cy="-106" rx="49.29" ry="18"/> +<text text-anchor="middle" x="2085.53" y="-102.3" font-family="Times-Roman" font-size="14.00">strtosize</text> +</g> +<!-- strtosize->strtosize --> +<g id="edge82" class="edge"> +<title>strtosize->strtosize</title> +<path fill="none" stroke="#a61402" d="M2038.6,-100C2035.14,-100 2032.97,-100 2032.97,-100 2032.97,-100 2032.97,-70 2032.97,-70 2032.97,-70 2138.97,-70 2138.97,-70 2138.97,-70 2138.97,-112 2138.97,-112 2138.97,-112 2138.31,-112 2138.31,-112"/> +<polygon fill="#a61402" stroke="#a61402" points="2142.42,-108.5 2132.42,-112 2142.42,-115.5 2142.42,-108.5"/> +</g> +<!-- multiply --> +<g id="node53" class="node"> +<title>multiply</title> +<ellipse fill="none" stroke="black" cx="2297.52" cy="-106" rx="47.39" ry="18"/> +<text text-anchor="middle" x="2297.52" y="-102.3" font-family="Times-Roman" font-size="14.00">multiply</text> +</g> +<!-- strtosize->multiply --> +<g id="edge81" class="edge"> +<title>strtosize->multiply</title> +<path fill="none" stroke="#a61402" d="M2132.32,-100C2132.32,-100 2242.67,-100 2242.67,-100"/> +<polygon fill="#a61402" stroke="#a61402" points="2242.67,-103.5 2252.67,-100 2242.67,-96.5 2242.67,-103.5"/> +</g> +<!-- strtolimit->strtosize --> +<g id="edge83" class="edge"> +<title>strtolimit->strtosize</title> +<path fill="none" stroke="#c987ba" d="M1902.06,-112C1902.06,-112 2028.91,-112 2028.91,-112"/> +<polygon fill="#c987ba" stroke="#c987ba" points="2028.91,-115.5 2038.91,-112 2028.91,-108.5 2028.91,-115.5"/> +</g> +<!-- secure_path --> +<g id="node54" class="node"> +<title>secure_path</title> +<ellipse fill="none" stroke="black" cx="1025.69" cy="-143" rx="65.79" ry="18"/> +<text text-anchor="middle" x="1025.69" y="-139.3" font-family="Times-Roman" font-size="14.00">secure_path</text> +</g> +</g> +</svg> diff --git a/content/posts/how-bsd-authentication-works/index.org b/content/posts/how-bsd-authentication-works/index.org new file mode 100644 index 0000000..f0623c2 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/index.org @@ -0,0 +1,2811 @@ +#+TITLE: How BSD Authentication Works +#+DATE: 2021-10-18T17:27:13-04:00 +#+DRAFT: true +#+SHOWTOC: true +#+DESCRIPTION: A walkthrough of how OpenBSD's BSD Auth framework functions +#+TAGS[]: openbsd security +#+KEYWORDS[]: openbsd security +#+SLUG: +#+SUMMARY: + +#+ATTR_HTML: :title OpenBSD Internals +#+ATTR_HTML: :alt OpenBSD mascot cutaway view with spinning gears inside +[[file:openbsd_internals.gif]] + +* History +  :PROPERTIES: +  :CUSTOM_ID: history +  :END: + +  The way OpenBSD authenticates users is quite different from other +  Unix-like operating systems. Most other systems like AIX, Solaris, +  Linux, the other BSDs, and MacOS, use a framework called [[https://en.wikipedia.org/wiki/Pluggable_authentication_module][Pluggable +  Authentication Module]] (PAM). The two main implementations are [[http://www.linux-pam.org/][Linux +  PAM]] and [[https://www.openpam.org/][OpenPAM]]. PAM modules are created as dynamically loaded +  shared objects, which communicate using a combination of common and +  implementation specific interfaces ([[https://linux.die.net/man/3/pam][Linux-PAM]] and [[https://www.freebsd.org/cgi/man.cgi?query=pam&apropos=0&sektion=3&manpath=FreeBSD+12.1-RELEASE+and+Ports&arch=default&format=html][OpenPAM]]). It's +  configured using the [[https://linux.die.net/man/5/pam.d][pam.d]] directory and [[https://www.freebsd.org/cgi/man.cgi?query=pam.conf&sektion=5&apropos=0&manpath=FreeBSD+12.1-RELEASE+and+Ports][pam.conf]] file. While it can +  be flexible, it's highly complex and very easy to mis-configure, +  leaving you open to strange and hard to track down authentication +  bugs. On top of that, the fact that it's a shared library means that +  any vulnerability in a poorly vetted authentication module gives +  attackers direct access to the internals of your application. Author +  Michael W. Lucas said it best when he described PAM as +  [[https://www.youtube.com/watch?v=-CXp3byvI1g][unstandardized black magic]]. + +  OpenBSD on the other hand uses a mechanism called BSD +  Authentication. It was originally developed for a now-defunct +  proprietary operating system called [[https://en.wikipedia.org/wiki/BSD/OS][BSD/OS]] by [[https://en.wikipedia.org/wiki/Berkeley_Software_Design][Berkeley Software +  Design Inc.]], who later donated the system. It was then adopted by +  OpenBSD in release 2.9. BSD Auth is comparatively much simpler than +  PAM. Modules or, authentication "styles", are instead stand alone +  applications or scripts that communicate over IPC. The module has no +  ability to interfere with the parent and can very easily revoke +  permissions using [[https://man.openbsd.org/pledge][=pledge(2)=]] or [[https://man.openbsd.org/unveil][=unveil(2)=]]. The BSD Authentication +  system of configured through [[https://man.openbsd.org/login.conf][=login.conf(5)=]]. + +* Documentation +  :PROPERTIES: +  :CUSTOM_ID: documentation +  :END: + +  All of the high level authentication functions are described in +  [[https://man.openbsd.org/authenticate][=authenticate(3)=]], with the lower level functions being described in +  [[https://man.openbsd.org/auth_subr][=auth_subr(3)=]]. + +  Click on any function prototype in this post to see its definition. + +  I've also created a [[#graph][graph]] at the bottom of the post to help +  visualize the function calls. + +  All code snippets from this blog post belong to the OpenBSD +  contributors. Please see the [[#copyright][Copyright]] section for details. + +* BSD Auth Modules +  :PROPERTIES: +  :CUSTOM_ID: modules +  :END: + +  Modules are located in =/usr/libexec/auth/= with the naming +  convention =login_<style>=. They accept arguments in the following +  form. + +  #+BEGIN_SRC shell +  login_<style> [-s service] [-v key=value] user [class] +  #+END_SRC + +  - =<style>= is the authentication method. This could be =passwd=, +    =radius=, =skey=, =yubikey=, etc. There's more information about +    available styles in [[https://man.openbsd.org/login.conf][=login.conf(5)=]] under the [[https://man.openbsd.org/login.conf#AUTHENTICATION][=AUTHENTICATION=]] +    header. +  - =service= is the service type. Typically authentication methods +    will accept one of three values here: =login=, =challenge=, or +    =response=. =login= is the default if it's not specified, and is +    used to let the module know to interact with the user directly +    through =stdin= and =stdout=, while =challenge= and =response= are +    used to pass messages back and forth through the BSD Auth API. +    Each style's man page will have more details on these. +  - =-v key=value= is an optional argument. There can be more than one +    arguments in this style. This is used to pass extra data to the +    program under certain circumstances. +  - =user= is the name of the user to be authenticated. +  - =class= is optional and specifies the login class to use for the +    user. + +  =login= and =su= pass in extra data as =-v= flags. + +  #+CAPTION: Taken from [[https://man.openbsd.org/login.conf][=login.conf(5)=]] +  #+BEGIN_SRC +  The login(1) program provides the following through the -v option: + +     auth_type       The type of authentication to use. + +     fqdn            The hostname provided to login by the -h option. + +     hostname        The name login(1) will place in the utmp file for the +                     remote hostname. + +     local_addr      The local IP address given to login(1) by the -L option. + +     lastchance      Set to "yes" when a user's password has expired but the +                     user is being given one last chance to login and update +                     the password. + +     login           This is a new login session (as opposed to a simple +                     identity check). + +     remote_addr     The remote IP address given to login(1) by the -R option. + +     style           The style of authentication used for this user (see +                     approval scripts below). + +     The su(1) program provides the following through the -v option: + +     wheel           Set to either "yes" or "no" to indicate if the user is in +                     group wheel when they are trying to become root.  Some +                     authentication types require the user to be in group +                     wheel when using the su(1) program to become super user. +  #+END_SRC + +  The auth module communicates with its caller through what's called +  the "back channel" on file descriptor 3. This communication is +  covered in greater detail in the [[#auth_call][=auth_call=]] section. + +  Some modules require an extra file descriptor to be passed in for +  stateful challenge/response authentication. In these cases, an extra +  =-v fd=4= argument will be passed. Theoretically this =fd= can be +  any number, but in practice =fd=4= is hard-coded. + + +  Most modules also have a hidden flag =-d=, which sets the back +  channel do =stdio=, presumably for debugging purposes. + +  The simplest way to authenticate a user with BSD Auth is by using +  [[#auth_userokay][=auth_userokay=]]. + +  For cases where challenge / response authentication is required and +  the user can't interacting through =stdin= and =stdout=, +  [[#auth_userchallenge][=auth_userchallenge=]] and [[#auth_userresponse][=auth_userresponse=]] can be used. + +* Approval Scripts +  :PROPERTIES: +  :CUSTOM_ID: approval +  :END: + +  Approval scripts can be much simpler than the full login modules +  used by the other functions. They are given the same back channel as +  auth modules, but should not explicitly authenticate or revoke +  users. They should exit with a zero status for approval, or non-zero +  status to signal disapproval. + +  Approval scrips receive arguments in the following form. +  #+begin_src shell +  approve [-v name=value] username class service +  #+end_src + +  It can also receive extra key-value =-v= arguments in the same format as +  [[#modules][auth modules]]. More information is available in the [[https://man.openbsd.org/login.conf#APPROVAL][=APPROVAL=]] +  section of the =login.conf= man page. + +  Approval scripts are run using [[#auth_approval][=auth_approval=]]. + +* auth_userokay +  :PROPERTIES: +  :CUSTOM_ID: auth_userokay +  :END: +  [[https://man.openbsd.org/authenticate.3#auth_userokay][=auth_userokay=]] is the highest level function, and easiest to use. +  It takes four strings as arguments: =name=, =style=, =type=, and +  =password=. It returns either a =0= for failure, of a non-zero value +  for success. + +  @@html: <details> <summary> @@ +  #+BEGIN_SRC c +  int auth_userokay(char *name, char *style, char *type, char *password); +  #+END_SRC +  @@html: </summary> @@ +  #+begin_src c +  { +      auth_session_t *as; + +      as = auth_usercheck(name, style, type, password); + +      return (as != NULL ? auth_close(as) : 0); +  } +  #+end_src +  @@html: </details> @@ + +  - =name= is the name of the user to be authenticated +  - =style= is the login method to be used +    - If =style= is =NULL=, the user's default login style will be +      used. This is =passwd= on normal accounts. +    - The style can be one of the installed authentication methods, like +      =passwd=, =radius=, =skey=, =yubikey=, etc. +    - Styles can also be installed through BSD Auth module packages +  - =type= is the authentication type +    - Types are defined in =login.conf= and as a group of allowed auth +      styles +    - If =type= is =NULL=, use the auth type for the user's login +      class. The default type is =auth-default=, which allows +      =psaswd= and =skey= auth methods. +  - =password= is the password to test +    - If =password= is =NULL=, then the user is interactively +      prompted. This is required for auth styles using +      challenge-response methods. +    - If =password= is specified, then it's passed to the auth module +      as a =response= + +  =auth_userokay= is just a wrapper around [[#auth_usercheck][=auth_usercheck=]] that takes +  care of closing the session using [[#auth_close][=auth_close=]], and returning the +  resulting value. + +* auth_session_t +  :PROPERTIES: +  :CUSTOM_ID: auth_session_t +  :END: + +  =auth_session_t= is the main data structure used to represent the +  authentication session. + +  #+BEGIN_SRC c +  struct auth_session_t { +      char    *name;                 /* name of use being authenticated */ +      char    *style;                /* style of authentication used */ +      char    *class;                /* class of user */ +      char    *service;              /* type of service being performed */ +      char    *challenge;            /* last challenge issued */ +      int     flags;                 /* see below */ +      struct  passwd *pwd;           /* password entry for user */ +      struct  timeval now;           /* time of authentication */ + +      int     state;                 /* authenticated state */ + +      struct  rmfiles *rmlist;       /* list of files to remove on failure */ +      struct  authopts *optlist;     /* list of options to scripts */ +      struct  authdata *data;        /* additional data to send to scripts */ + +      char    spool[MAXSPOOLSIZE];   /* data returned from login script */ +      int     index;                 /* how much returned thus far */ + +      int     fd;                    /* connection to authenticator */ + +      va_list ap0;                   /* argument list to auth_call */ +      va_list ap;                    /* additional arguments to auth_call */ +  }; +  #+END_SRC + +  Where =MAXSPOOLSIZE=, =authdata=, =authopts=, and =rmfiles= are defined as + +  #+BEGIN_SRC c +  #define	MAXSPOOLSIZE	(8*1024)	/* Spool up to 8K of back info */ + +  struct rmfiles { +      struct rmfiles  *next; +      char            *file; +  }; + +  struct authopts { +      struct authopts *next; +      char            *opt; +  }; + +  struct authdata { +      struct  authdata *next; +      void    *ptr; +      size_t   len; +  }; +  #+END_SRC + +  There are several functions which get used to operate on +  =auth_session_t= to keep it opaque. +** auth_setdata +   :PROPERTIES: +   :CUSTOM_ID: auth_setdata +   :END: + +   @@html: <details> <summary> @@ +   #+begin_src c +   int auth_setdata(auth_session_t *as, void *ptr, size_t len) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct authdata *data, *dp; + +       if (len <= 0) +           return (0); + +       if ((data = malloc(sizeof(*data) + len)) == NULL) +           return (-1); + +       data->next = NULL; +       data->len = len; +       data->ptr = data + 1; +       memcpy(data->ptr, ptr, len); + +       if (as->data == NULL) +           as->data = data; +       else { +           for (dp = as->data; dp->next != NULL; dp = dp->next) +               ; +           dp->next = data; +       } +       return (0); +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setdata~2][=auth_setdata=]] allocates and initializes a new =authdata= struct, +   storing a copy of the data from =ptr= and =len=. It then point the +   =next= field on the last =authdata= struct in =as= to its +   location. It returns =0= on success. + +** auth_setitem / auth_getitem +   :PROPERTIES: +   :CUSTOM_ID: auth_setitem +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   int auth_setitem(auth_session_t *as, auth_item_t item, char *value) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       if (as == NULL) { +           errno = EINVAL; +           return (-1); +       } + +       switch (item) { +       case AUTHV_ALL: +           if (value != NULL) { +               errno = EINVAL; +               return (-1); +           } +           auth_setitem(as, AUTHV_CHALLENGE, NULL); +           auth_setitem(as, AUTHV_CLASS, NULL); +           auth_setitem(as, AUTHV_NAME, NULL); +           auth_setitem(as, AUTHV_SERVICE, NULL); +           auth_setitem(as, AUTHV_STYLE, NULL); +           auth_setitem(as, AUTHV_INTERACTIVE, NULL); +           return (0); + +       case AUTHV_CHALLENGE: +           if (value == as->challenge) +               return (0); +           if (value != NULL && (value = strdup(value)) == NULL) +               return (-1); +           free(as->challenge); +           as->challenge = value; +           return (0); + +       case AUTHV_CLASS: +           if (value == as->class) +               return (0); +           if (value != NULL && (value = strdup(value)) == NULL) +               return (-1); +           free(as->class); +           as->class = value; +           return (0); + +       case AUTHV_NAME: +           if (value == as->name) +               return (0); +           if (value != NULL && !_auth_validuser(value)) { +               errno = EINVAL; +               return (-1); +           } +           if (value != NULL && (value = strdup(value)) == NULL) +               return (-1); +           free(as->name); +           as->name = value; +           return (0); + +       case AUTHV_SERVICE: +           if (value == as->service) +               return (0); +           if (value == NULL || strcmp(value, defservice) == 0) +               value = defservice; +           else if ((value = strdup(value)) == NULL) +               return (-1); +           if (as->service && as->service != defservice) +               free(as->service); +           as->service = value; +           return (0); + +       case AUTHV_STYLE: +           if (value == as->style) +               return (0); +           if (value == NULL || strchr(value, '/') != NULL || +               (value = strdup(value)) == NULL) +               return (-1); +           free(as->style); +           as->style = value; +           return (0); + +       case AUTHV_INTERACTIVE: +           if (value == NULL) +               as->flags &= ~AF_INTERACTIVE; +           else +               as->flags |= ~AF_INTERACTIVE; +           return (0); + +       default: +           errno = EINVAL; +           return (-1); +       } +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setitem][=auth_setitem=]] is used to set one of several different fields of +   =as= to =value=. Depending on the value of =item= ([[#auth_item_t][=auth_item_t=]]), it +   can be the =challenge=, =class=, =name=, =service=, =style=, or +   =interactive= field. If =value= is =NULL=, it clears that field. If +   =item= is =AUTHV_ALL= and =value= is =NULL=, all fields are +   cleared. It returns =0= on success. + +   *Note*: As of writing, the man page displays the incorrect name for +   the constants. + +   #+CAPTION: Taken from [[https://man.openbsd.org/auth_subr.3#auth_getitem][=auth_subr(3)=]] +   #+begin_src text +   AUTH_CHALLENGE +       The latest challenge, if any, set for the session. + +   AUTH_CLASS +       The class of the user, as defined by the /etc/login.conf file. +       This value is not directly used by BSD Authentication, rather, it +       is passed to the login scripts for their possible use. + +   AUTH_INTERACTIVE +       If set to any value, then the session is tagged as interactive. If +       not set, the session is not interactive. When the value is +       requested it is always either NULL or “True”. The auth subroutines +       may choose to provide additional information to standard output or +       standard error when the session is interactive. There is no +       functional change in the operation of the subroutines. + +   AUTH_NAME +       The name of the user being authenticated. The name should include +       the instance, if any, that is being requested. + +   AUTH_SERVICE +       The service requesting the authentication. Initially it is set to +       the default service which provides the traditional interactive +       service. + +   AUTH_STYLE +       The style of authentication being performed, as defined by the +       /etc/login.conf file. The style determines which login script +       should actually be used. +   #+end_src + + +   @@html: <details> <summary> @@ +   #+begin_src c +   char *auth_getitem(auth_session_t *as, auth_item_t item) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       if (as != NULL) { +           switch (item) { +           case AUTHV_CHALLENGE: +               return (as->challenge); +           case AUTHV_CLASS: +               return (as->class); +           case AUTHV_NAME: +               return (as->name); +           case AUTHV_SERVICE: +               return (as->service ? as->service : defservice); +           case AUTHV_STYLE: +               return (as->style); +           case AUTHV_INTERACTIVE: +               return ((as->flags & AF_INTERACTIVE) ? "True" : NULL); +           default: +               break; +           } +       } +       return (NULL); +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_getitem][=auth_getitem=]] is used to return the value of the fields listed above. + +*** auth_item_t +    :PROPERTIES: +    :CUSTOM_ID: auth_item_t +    :END: + +    =auth_item_t= is an enum defined in =/include/bsd_auth.h=. + +    #+begin_src c +    typedef enum { +        AUTHV_ALL, +        AUTHV_CHALLENGE, +        AUTHV_CLASS, +        AUTHV_NAME, +        AUTHV_SERVICE, +        AUTHV_STYLE, +        AUTHV_INTERACTIVE +    } auth_item_t; +    #+end_src + +** auth_setoption +   :PROPERTIES: +   :CUSTOM_ID: auth_setoption +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   int auth_setoption(auth_session_t *as, char *n, char *v) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct authopts *opt; +       size_t len = strlen(n) + strlen(v) + 2; +       int ret; + +       if ((opt = malloc(sizeof(*opt) + len)) == NULL) +           return (-1); + +       opt->opt = (char *)(opt + 1); + +       ret = snprintf(opt->opt, len, "%s=%s", n, v); +       if (ret < 0 || ret >= len) { +           free(opt); +           errno = ENAMETOOLONG; +           return (-1); +       } +       opt->next = as->optlist; +       as->optlist = opt; +       return(0); +   } + +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setoption][=auth_setoption=]] initializes a new =authopts= struct, and sets the +   =opt= field to a string formatted as =sprintf("%s=%s", n, v)=. It +   then point the =next= field on the last =authopts= struct in =as= +   to its location. It returns =0= on success. + +** auth_setstate / auth_getstate +   :PROPERTIES: +   :CUSTOM_ID: auth_setstate +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   void	auth_setstate(auth_session_t *as, int s) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { as->state = s; } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setstate][=auth_setstate=]] sets the =state= of =as= to =s=. + +   @@html: <details> <summary> @@ +   #+begin_src c +   int	auth_getstate(auth_session_t *as) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { return (as->state); } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_getstate][=auth_getstate=]] return the =state= of =as=. + +** auth_setpwd / auth_getpwd +   :PROPERTIES: +   :CUSTOM_ID: auth_setpwd +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   int auth_setpwd(auth_session_t *as, struct passwd *pwd) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct passwd pwstore; +       char *instance, pwbuf[_PW_BUF_LEN]; + +       if (pwd == NULL && as->pwd == NULL && as->name == NULL) +           return (-1);		/* true failure */ + +       if (pwd == NULL) { +           /* +            * If we were not passed in a pwd structure we need to +            * go find one for ourself.  Always look up the username +            * (if it is defined) in the passwd database to see if there +            * is an entry for the user.  If not, either use the current +            * entry or simply return a 1 which implies there is +            * no user by that name here.  This is not a failure, just +            * a point of information. +            */ +           if (as->name == NULL) +               return (0); +           getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +           if (pwd == NULL) { +               instance = strchr(as->name, '/'); +               if (instance == NULL) +                   return (as->pwd ? 0 : 1); +               if (strcmp(instance, "/root") == 0) { +                   getpwnam_r(instance + 1, &pwstore, pwbuf, +                       sizeof(pwbuf), &pwd); +               } +               if (pwd == NULL) +                   return (as->pwd ? 0 : 1); +           } +       } +       if ((pwd = pw_dup(pwd)) == NULL) +           return (-1);		/* true failure */ +       if (as->pwd) { +           explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); +           free(as->pwd); +       } +       as->pwd = pwd; +       return (0); +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setpwd][=auth_setpwd=]] is used to retrieve and set the [[https://man.openbsd.org/man3/getpwnam.3][password database]] +   entry in =as= if one isn't already set. + +   If a passwd entry is passed in through =pwd=, it uses that to set +   =as->pwd=. If =pwd= is =NULL=, it tries to find the passwd entry +   associated with =as->name=. If it finds one, it sets =as->pwd= and +   returns =0=. If there is no entry with that username, it returns +   =1=. + +   @@html: <details> <summary> @@ +   #+begin_src c +   struct passwd *auth_getpwd(auth_session_t *as) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { return (as->pwd); } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_getpwd][=auth_getpwd=]] returns =as->pwd=. + +** auth_set_va_list +   :PROPERTIES: +   :CUSTOM_ID: auth_set_va_list +   :END: + +   @@html: <details> <summary> @@ +   #+begin_src c +   void auth_set_va_list(auth_session_t *as, va_list ap) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { va_copy(as->ap, ap); } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_set_va_list][=auth_set_va_list=]] copies =ap= to =as->ap=. + +** auth_clrenv +   :PROPERTIES: +   :CUSTOM_ID: auth_clrenv +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   void auth_clrenv(auth_session_t *as) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       char *line; + +       for (line = as->spool; line < as->spool + as->index;) { +           if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { +               if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { +                   line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; +               } +           } else +           if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { +               if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { +                   line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; +               } +           } +           while (*line++) +               ; +       } +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_clrenv][=auth_clrenv=]] removes all lines containing =BI_SETENV= and +   =BI_UNSETENV= from =as->spool=. This is explained under the +   [[#auth_call][=auth_call=]] section. + +** auth_clroption +   :PROPERTIES: +   :CUSTOM_ID: auth_clroption +   :END: + +   @@html: <details> <summary> @@ +   #+begin_src c +   void auth_clroption(auth_session_t *as, char *option) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct authopts *opt, *oopt; +       size_t len; + +       len = strlen(option); + +       if ((opt = as->optlist) == NULL) +           return; + +       if (strncmp(opt->opt, option, len) == 0 && +           (opt->opt[len] == '=' || opt->opt[len] == '\0')) { +           as->optlist = opt->next; +           free(opt); +           return; +       } + +       while ((oopt = opt->next) != NULL) { +           if (strncmp(oopt->opt, option, len) == 0 && +               (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { +               opt->next = oopt->next; +               free(oopt); +               return; +           } +           opt = oopt; +       } +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_clroption][=auth_clroption=]] removes the option named =option= from =as=. + +** auth_clroptions +   :PROPERTIES: +   :CUSTOM_ID: auth_clroptions +   :END: + +   @@html: <details> <summary> @@ +   #+begin_src c +   void auth_clroptions(auth_session_t *as) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct authopts *opt; + +       while ((opt = as->optlist) != NULL) { +           as->optlist = opt->next; +           free(opt); +       } +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_clroptions][=auth_clroptions=]] clears all options from =as=. + +** auth_setenv +   :PROPERTIES: +   :CUSTOM_ID: auth_setenv +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   void auth_setenv(auth_session_t *as) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       char *line, *name; + +       /* +        ,* Set any environment variables we were asked for +        ,*/ +           for (line = as->spool; line < as->spool + as->index;) { +           if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { +               if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { +                   /* only do it once! */ +                   line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; +                   line += sizeof(BI_SETENV) - 1; +                   for (name = line; +                       isblank((unsigned char)*name); ++name) +                       ; +                   for (line = name; +                       ,*line && !isblank((unsigned char)*line); +                       ++line) +                       ; +                   if (*line) +                       ,*line++ = '\0'; +                   for (; isblank((unsigned char)*line); ++line) +                       ; +                   if (*line != '\0' && setenv(name, line, 1)) +                       warn("setenv(%s, %s)", name, line); +               } +           } else +           if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { +               if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { +                   /* only do it once! */ +                   line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; +                   line += sizeof(BI_UNSETENV) - 1; +                   for (name = line; +                       isblank((unsigned char)*name); ++name) +                       ; +                   for (line = name; +                       ,*line && !isblank((unsigned char)*line); +                       ++line) +                       ; +                   if (*line) +                       ,*line++ = '\0'; +                   unsetenv(name); +               } +           } +           while (*line++) +               ; +       } +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_setenv][=auth_setenv=]] scans through =as->spool=, modifying the environment +   according to =BI_SETENV= and =BI_UNSETENV= instructions. + +** auth_getvalue +   :PROPERTIES: +   :CUSTOM_ID: auth_getvalue +   :END: +   @@html: <details> <summary> @@ +   #+BEGIN_SRC c +   char *auth_getvalue(auth_session_t *as, char *what) +   #+END_SRC +   @@html: </summary> @@ +   #+begin_src c +   { +       char *line, *v, *value; +       int n, len; + +       len = strlen(what); + +           for (line = as->spool; line < as->spool + as->index;) { +           if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) +               goto next; +           line += sizeof(BI_VALUE) - 1; + +           if (!isblank((unsigned char)*line)) +               goto next; + +           while (isblank((unsigned char)*++line)) +               ; + +           if (strncmp(line, what, len) != 0 || +               !isblank((unsigned char)line[len])) +               goto next; +           line += len; +           while (isblank((unsigned char)*++line)) +               ; +           value = strdup(line); +           if (value == NULL) +               return (NULL); + +           /* +            ,* XXX - There should be a more standardized +            ,* routine for doing this sort of thing. +            ,*/ +           for (line = v = value; *line; ++line) { +               if (*line == '\\') { +                   switch (*++line) { +                   case 'r': +                       ,*v++ = '\r'; +                       break; +                   case 'n': +                       ,*v++ = '\n'; +                       break; +                   case 't': +                       ,*v++ = '\t'; +                       break; +                   case '0': case '1': case '2': +                   case '3': case '4': case '5': +                   case '6': case '7': +                       n = *line - '0'; +                       if (isdigit((unsigned char)line[1])) { +                           ++line; +                           n <<= 3; +                           n |= *line-'0'; +                       } +                       if (isdigit((unsigned char)line[1])) { +                           ++line; +                           n <<= 3; +                           n |= *line-'0'; +                       } +                       break; +                   default: +                       ,*v++ = *line; +                       break; +                   } +               } else +                   ,*v++ = *line; +           } +           ,*v = '\0'; +           return (value); +   next: +           while (*line++) +               ; +       } +       return (NULL); +   } +   #+end_src +   @@html: </details> @@ + +   [[https://man.openbsd.org/auth_subr.3#auth_getvalue~2][=auth_getvalue=]] scans =as->spool= looking for lines beginning with +   =BI_VALUE=. It then checks if the next word is equal to =what=. + +   When it finds the desired line, it duplicates the string, converts +   escape sequences in the value, and returns the newly created +   string. + +   For convenience, the function [[#auth_mkvalue][=auth_mkvalue=]] can be used inside +   of the authentication module to create and return appropriately +   escaped value strings. + +** auth_getchallenge +   :PROPERTIES: +   :CUSTOM_ID: auth_getchallenge +   :END: + +   The [[https://man.openbsd.org/auth_subr.3#auth_getchallenge][=auth_subr(3)=]] man page claims this function exists, but I +   can't find it anywhere in the source code. I suspect this is an +   error. + +* auth_open +  :PROPERTIES: +  :CUSTOM_ID: auth_open +  :END: +  @@html: <details> <summary> @@ +  #+begin_src c +  auth_session_t *auth_open(void) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      auth_session_t *as; + +      if ((as = calloc(1, sizeof(auth_session_t))) != NULL) { +          as->service = defservice; +          as->fd = -1; +      } + +      return (as); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_open][=auth_open=]] is used by several functions to create a new auth +  session. It allocates an [[#auth_session_t][=auth_session_t=]] struct on the heap, sets +  its default =service= to that defined by =LOGIN_DEFSERVICE= in +  =/include/login_cap.h=, which is currently ="login"=. + +  #+begin_src c +  #define	LOGIN_DEFSERVICE	"login" +  #+end_src + +  It then sets the =fd= field to =-1=, and returns the pointer. + +* auth_usercheck +  :PROPERTIES: +  :CUSTOM_ID: auth_usercheck +  :END: + +  @@html: <details> <summary> @@ +  #+BEGIN_SRC c +  auth_session_t *auth_usercheck(char *name, char *style, char *type, char *password) +  #+END_SRC +  @@html: </summary> @@ +  #+begin_src c +  { +      char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; +      char pwbuf[_PW_BUF_LEN]; +      auth_session_t *as; +      login_cap_t *lc; +      struct passwd pwstore, *pwd = NULL; +      char *slash; + +      if (!_auth_validuser(name)) +          return (NULL); +      if (strlcpy(namebuf, name, sizeof(namebuf)) >= sizeof(namebuf)) +          return (NULL); +      name = namebuf; + +      /* +       ,* Split up user:style names if we were not given a style +       ,*/ +      if (style == NULL && (style = strchr(name, ':')) != NULL) +          ,*style++ = '\0'; + +      /* +       ,* Cope with user/instance.  We are only using this to get +       ,* the class so it is okay if we strip a /root instance +       ,* The actual login script will pay attention to the instance. +       ,*/ +      getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +      if (pwd == NULL) { +          if ((slash = strchr(name, '/')) != NULL) { +              ,*slash = '\0'; +              getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +              ,*slash = '/'; +          } +      } +      if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) +          return (NULL); + +      if ((style = login_getstyle(lc, style, type)) == NULL) { +          login_close(lc); +          return (NULL); +      } + +      if (password) { +          if ((as = auth_open()) == NULL) { +              login_close(lc); +              return (NULL); +          } +          auth_setitem(as, AUTHV_SERVICE, "response"); +          auth_setdata(as, "", 1); +          auth_setdata(as, password, strlen(password) + 1); +          explicit_bzero(password, strlen(password)); +      } else +          as = NULL; +      as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); +      login_close(lc); +      return (as); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_usercheck][=auth_usercheck=]] is very similar to [[#auth_userokay][=auth_userokay=]]. It takes the +  same arguments, except it returns the [[#auth_session_t][=auth_session_t=]] struct +  instead of just the status. + +  It first checks that =name= is valid according to [[#_auth_validuser][=_auth_validuser=]]. + +  If =style= is =NULL=, it checks if =name= is in the =user:style= +  format, and splits it accordingly. + +  It then gets the user's password database entry through +  [[https://man.openbsd.org/man3/getpwnam.3#getpwnam_r][=getpwman_r(3)=]], which operates on the [[https://man.openbsd.org/passwd.5][=passwd(5)=]] database. After +  it uses that to retrieve the user's login class using +  [[https://man.openbsd.org/login_getclass#login_getclass][=login_getclass(3)=]], which returns a =login_cap_t=. Login classes +  are stored in the [[https://man.openbsd.org/man5/login.conf.5][=login.conf(5)=]] database. + +  That struct is then passed into [[https://man.openbsd.org/login_getclass#login_getstyle][=login_getstyle(3)=]], which also +  received the =style= and =type=. If =type= is =NULL=, it returns +  the first available login style for that class. If =style= is +  specified, it is returned if available, otherwise =NULL= is +  returned, which causes =auth_usercheck= to return =NULL= as well. + +  It then creates a pointer =as= of type [[#auth_session_t][=auth_session_t=]], and handles +  it differently based on whether =password= is =NULL=. + +  - If the password is a string, it creates a new session using +    [[#auth_open][=auth_open=]] and assigns it to =as=. It then sets the session +    =service= to ="response"=, and adds the =password= string to the +    session's =data=. + +    #+BEGIN_SRC c +    auth_setitem(as, AUTHV_SERVICE, "response"); +    auth_setdata(as, "", 1); +    auth_setdata(as, password, strlen(password) + 1); +    #+END_SRC + +  - If =password= is =NULL=, it sets =as= to =NULL=. + +  It then passes the =auth_session_t= pointer (=as=), =name=, +  =style=, login class (=lc->lc_class=), and a =NULL= char pointer to +  [[#auth_verify][=auth_verify=]]. Finally it returns the auth session pointer. + +  #+begin_src c +  as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); +  // [...] some cleanup +  return (as); +  #+end_src + +* auth_verify +  :PROPERTIES: +  :CUSTOM_ID: auth_verify +  :END: +  @@html: <details> <summary> @@ +  #+BEGIN_SRC c +  auth_session_t *auth_verify(auth_session_t *as, char *style, char *name, ...) +  #+END_SRC +  @@html: </summary> @@ +  #+begin_src c +  { +      va_list ap; +      char path[PATH_MAX]; + +      if ((name == NULL || style == NULL) && as == NULL) +          return (NULL); + +      if (as == NULL && (as = auth_open()) == NULL) +          return (NULL); +      auth_setstate(as, 0); + +      if (style != NULL && auth_setitem(as, AUTHV_STYLE, style) < 0) +          return (as); + +      if (name != NULL && auth_setitem(as, AUTHV_NAME, name) < 0) +          return (as); + +      style = auth_getitem(as, AUTHV_STYLE); +      name = auth_getitem(as, AUTHV_NAME); +      if (!_auth_validuser(name)) +          return (as); + +      snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); +      va_start(ap, name); +      auth_set_va_list(as, ap); +      auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", +          auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); +      va_end(ap); +      return (as); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_verify][=auth_verify=]] is used as a frontend for [[#auth_call][=auth_call=]]. + +  It creates an auth session using =auth_open= if =as= is =NULL=. + +  The =state= of the session is set to =0=. + +  It sets the =name= and =style= of the session, if the +  =style= and/or =name= are non-=NULL=. + +  After that it constructs the path of the authentication module, +  placing it in the variable =path=. It is constructed by combining +  =_PATH_AUTHPROG=, which is defined in =login_cap.h= as +  =/usr/libexec/auth/login_=, and the authentication style. For the +  case of auth style =passwd=, it would result in the path +  =/usr/libexec/auth/login_passwd=. + +  #+begin_src c +  snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); +  #+end_src + +  It then copies its variable arguments to the auth session using +  [[#auth_set_va_list][=auth_set_va_list=]]. + +  Then =auth_call= is called with the session struct, the path to the +  auth module, the auth style, the =-s= flag followed by the service +  (=login=, =challenge=, or =response=), a double dash, the user name, +  and a =NULL= character pointer. The return value of =auth_call= is +  ignored and a pointer to the auth session is returned immediately +  afterwards. + +  #+BEGIN_SRC c +  va_start(ap, name); +  auth_set_va_list(as, ap); +  auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", +            auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); +  va_end(ap); +  return (as); +  #+END_SRC + +* auth_call +  :PROPERTIES: +  :CUSTOM_ID: auth_call +  :END: +  @@html: <details> <summary> @@ +  #+BEGIN_SRC c +  int auth_call(auth_session_t *as, char *path, ...) +  #+END_SRC +  @@html: </summary> @@ +  #+begin_src c +  { +      char *line; +      struct authdata *data; +      struct authopts *opt; +      pid_t pid; +      int status; +      int okay; +      int pfd[2]; +      int argc; +      char *argv[64];		/* 64 args should be more than enough */ +  #define	Nargc	(sizeof(argv)/sizeof(argv[0])) + +      va_start(as->ap0, path); + +      argc = 0; +      if ((argv[argc] = _auth_next_arg(as)) != NULL) +          ++argc; + +      if (as->fd != -1) { +          argv[argc++] = "-v"; +          argv[argc++] = "fd=4";		/* AUTH_FD, see below */ +      } +      /* XXX - fail if out of space in argv */ +      for (opt = as->optlist; opt != NULL; opt = opt->next) { +          if (argc < Nargc - 2) { +              argv[argc++] = "-v"; +              argv[argc++] = opt->opt; +          } else { +              syslog(LOG_ERR, "too many authentication options"); +              goto fail; +          } +      } +      while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) +          ++argc; + +      if (argc >= Nargc - 1 && _auth_next_arg(as)) { +          if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { +              va_end(as->ap0); +              explicit_bzero(&(as->ap0), sizeof(as->ap0)); +          } +          if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { +              va_end(as->ap); +              explicit_bzero(&(as->ap), sizeof(as->ap)); +          } +          syslog(LOG_ERR, "too many arguments"); +          goto fail; +      } + +      argv[argc] = NULL; + +      if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) == -1) { +          syslog(LOG_ERR, "unable to create backchannel %m"); +          warnx("internal resource failure"); +          goto fail; +      } + +      switch (pid = fork()) { +      case -1: +          syslog(LOG_ERR, "%s: %m", path); +          warnx("internal resource failure"); +          close(pfd[0]); +          close(pfd[1]); +          goto fail; +      case 0: +  #define	COMM_FD	3 +  #define	AUTH_FD	4 +          if (dup2(pfd[1], COMM_FD) == -1) +              err(1, "dup of backchannel"); +          if (as->fd != -1) { +              if (dup2(as->fd, AUTH_FD) == -1) +                  err(1, "dup of auth fd"); +              closefrom(AUTH_FD + 1); +          } else +              closefrom(COMM_FD + 1); +          execve(path, argv, auth_environ); +          syslog(LOG_ERR, "%s: %m", path); +          err(1, "%s", path); +      default: +          close(pfd[1]); +          if (as->fd != -1) { +              close(as->fd);		/* so child has only ref */ +              as->fd = -1; +          } +          while ((data = as->data) != NULL) { +              as->data = data->next; +              if (data->len > 0) { +                  write(pfd[0], data->ptr, data->len); +                  explicit_bzero(data->ptr, data->len); +              } +              free(data); +          } +          as->index = 0; +          _auth_spool(as, pfd[0]); +          close(pfd[0]); +          do { +              if (waitpid(pid, &status, 0) != -1) { +                  if (!WIFEXITED(status)) +                      goto fail; +                  break; +              } +              /* +               ,* could get ECHILD if it was waited for by +               ,* another thread or from a signal handler +               ,*/ +          } while (errno == EINTR); +      } + +      /* +       ,* Now scan the spooled data +       ,* It is easier to wait for all the data before starting +       ,* to scan it. +       ,*/ +          for (line = as->spool; line < as->spool + as->index;) { +          if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { +              line += sizeof(BI_REJECT) - 1; +              if (!*line || *line == ' ' || *line == '\t') { +                  while (*line == ' ' || *line == '\t') +                      ++line; +                  if (!strcasecmp(line, "silent")) { +                      as->state = AUTH_SILENT; +                      break; +                  } +                  if (!strcasecmp(line, "challenge")) { +                      as->state  = AUTH_CHALLENGE; +                      break; +                  } +                  if (!strcasecmp(line, "expired")) { +                      as->state  = AUTH_EXPIRED; +                      break; +                  } +                  if (!strcasecmp(line, "pwexpired")) { +                      as->state  = AUTH_PWEXPIRED; +                      break; +                  } +              } +              break; +          } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { +              line += sizeof(BI_AUTH) - 1; +              if (!*line || *line == ' ' || *line == '\t') { +                  while (*line == ' ' || *line == '\t') +                      ++line; +                  if (*line == '\0') +                      as->state |= AUTH_OKAY; +                  else if (!strcasecmp(line, "root")) +                      as->state |= AUTH_ROOTOKAY; +                  else if (!strcasecmp(line, "secure")) +                      as->state |= AUTH_SECURE; +              } +          } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { +              line += sizeof(BI_REMOVE) - 1; +              while (*line == ' ' || *line == '\t') +                  ++line; +              if (*line) +                  _add_rmlist(as, line); +          } +          while (*line++) +              ; +      } + +      if (WEXITSTATUS(status)) +          as->state &= ~AUTH_ALLOW; + +      okay = as->state & AUTH_ALLOW; + +      if (!okay) +          auth_clrenv(as); + +      if (0) { +  fail: +          auth_clrenv(as); +          as->state = 0; +          okay = -1; +      } + +      while ((data = as->data) != NULL) { +          as->data = data->next; +          free(data); +      } + +      if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { +          va_end(as->ap0); +          explicit_bzero(&(as->ap0), sizeof(as->ap0)); +      } + +      if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { +          va_end(as->ap); +          explicit_bzero(&(as->ap), sizeof(as->ap)); +      } +      return (okay); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_call~2][=auth_call=]] is responsible for setting up the environment, +  calling the modules, and communicating with them. + +  An array of char pointers called =argv= is allocated to hold the arguments for the +  auth module. + +  #+BEGIN_SRC c +  char *argv[64];		/* 64 args should be more than enough */ +  #+END_SRC + +  First, the variable arguments are placed in =as->ap0=. + +  [[#_auth_next_arg][=_auth_next_arg=]] is called once, with the result being set as the +  first element in =argv=. If =as->fd= is set, adds =-v= and =fd=4= to +  =argv=. + +  Then it loops through the =optlist= and appends =-v= followed the +  option for each of them. + +  After that the rest of the arguments are retrieved from +  =_auth_next_arg= and added to the end of =argv=. Finally a =NULL= is +  added to the end of =argv=. + +  Next a socket pair of type =PF_LOCAL, SOCK_STREAM= is created. This +  is called the "back channel", and is used to communicate with the +  authentication module. + +  The process then calls [[https://man.openbsd.org/man2/fork.2][=fork(2)=]]. + +  Here two constants are set for the back channel and optional +  authentication file descriptors. + +  #+begin_src c +  #define	COMM_FD	3 +  #define	AUTH_FD	4 +  #+end_src + +  In the child process, the back channel is set to file descriptor 3, +  or =COMM_FD= using [[https://man.openbsd.org/man2/dup.2#dup2][=dup2(3)=]]. If =as->fd=, is not =-1=, it is set to +  file descriptor 4, or =AUTH_FD=, also using =dup2(3)=. The remainder +  of the file descriptors are closed using [[https://man.openbsd.org/man2/closefrom.2][=closefrom(2)=]] by calling +  either =closefrom(COMM_FD + 1)= or =closefrom(AUTH_FD + 1)=, +  depending on whether or not =AUTH_FD= is used. + +  The child process then executes the module. + +  #+begin_src c +  execve(path, argv, auth_environ); +  #+end_src + +  =auth_environ= is defined at the top of the file as a very minimal +  environment. + +  #+BEGIN_SRC c +  static char *auth_environ[] = { +      "PATH=" _PATH_DEFPATH, +      "SHELL=" _PATH_BSHELL, +      NULL, +  }; +  #+END_SRC + +  Where both constants are defined in =/include/paths.h=. + +  #+BEGIN_SRC c +  #define	_PATH_DEFPATH	"/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin" +  #define	_PATH_BSHELL	"/bin/sh" +  #+END_SRC + +  In the parent process, the child's end of the back channel is +  closed, and so is the parent's copy of =as->fd= if it exists. + +  The data from =as->data= is then written to the back channel +  sequentially, zeroed, and freed. + +  Next =as->index= is set to =0=. + +  The response from the authentication module is then read from the +  back channel and put into =as->spool= with an optional received file +  descriptor placed in =as->fd=, using [[#_auth_spool][=_auth_spool=]]. + +  #+begin_src c +  _auth_spool(as, pfd[0]); +  #+end_src + +  Once the back channel data has finished spooling, it is scanned for +  key words defined in =login_cap.h=. + +  #+BEGIN_SRC c +  #define BI_AUTH         "authorize"         /* Accepted authentication */ +  #define BI_REJECT       "reject"            /* Rejected authentication */ +  #define BI_CHALLENGE    "reject challenge"  /* Reject with a challenge */ +  #define BI_SILENT       "reject silent"     /* Reject silently */ +  #define BI_REMOVE       "remove"            /* remove file on error */ +  #define BI_ROOTOKAY     "authorize root"    /* root authenticated */ +  #define BI_SECURE       "authorize secure"  /* okay on non-secure line */ +  #define BI_SETENV       "setenv"            /* set environment variable */ +  #define BI_UNSETENV     "unsetenv"          /* unset environment variable */ +  #define BI_VALUE        "value"             /* set local variable */ +  #define BI_EXPIRED      "reject expired"    /* account expired */ +  #define BI_PWEXPIRED    "reject pwexpired"  /* password expired */ +  #define BI_FDPASS       "fd"                /* child is passing an fd */ +  #+END_SRC + +  The [[https://man.openbsd.org/login.conf][=login.conf(5)=]] man page once again goes into greater detail on +  these values. + +  #+BEGIN_SRC +  authorize  The user has been authorized. + +  authorize secure +             The user has been authorized and root should be allowed to +             login even if this is not a secure terminal.  This should only +             be sent by authentication styles that are secure over insecure +             lines. + +  reject     Authorization is rejected.  This overrides any indication that +             the user was authorized (though one would question the wisdom +             in sending both a reject and an authorize command). + +  reject challenge +             Authorization was rejected and a challenge has been made +             available via the value challenge. + +  reject silent +             Authorization is rejected, but no error messages should be +             generated. + +  remove file +             If the login session fails for any reason, remove file before +             termination. + +  setenv name value +             If the login session succeeds, the environment variable name +             should be set to the specified value. + +  unsetenv name +             If the login session succeeds, the environment variable name +             should be removed. + +  value name value +             Set the internal variable name to the specified value.  The +             value should only contain printable characters.  Several \ +             sequences may be used to introduce non printing characters. +             These are: + +             \n      A newline. + +             \r      A carriage return. + +             \t      A tab. + +             \xxx    The character represented by the octal value xxx.  The +                     value may be one, two, or three octal digits. + +             \c      The string is replaced by the value of c.  This allows +                     quoting an initial space or the \ character itself. + + +             The following values are currently defined: + +             challenge +                     See section on challenges below. + +             errormsg +                     If set, the value is the reason authentication failed. +                     The calling program may choose to display this when +                     rejecting the user, but display is not required. + +  #+END_SRC + +  The scanner is looking for lines that begin with =BI_AUTH=, +  =BI_REJECT=, or =BI_REMOVE=. + +  Here =as->state= is set according to the values defined on +  =login_cap.h=. + +  #+BEGIN_SRC c +  /* +   * bits which can be returned by authenticate()/auth_scan() +   */ +  #define  AUTH_OKAY       0x01            /* user authenticated */ +  #define  AUTH_ROOTOKAY   0x02            /* authenticated as root */ +  #define  AUTH_SECURE     0x04            /* secure login */ +  #define  AUTH_SILENT     0x08            /* silent rejection */ +  #define  AUTH_CHALLENGE  0x10            /* a challenge was given */ +  #define  AUTH_EXPIRED    0x20            /* account expired */ +  #define  AUTH_PWEXPIRED  0x40            /* password expired */ +  #+END_SRC + +  If a rejection is received (any line starting with =BI_REJECT=), +  =as->state= is set according to the rejection, and the scanning is +  stopped. Rejections are final and take precedence over any +  authorizations. + +  If an authorization is received (any line starting with =BI_AUTH=), +  the appropriate state is bitwise =or=-ed onto =as->state=. This +  allows multiple authorizations, such as a case where both +  =BI_ROOTOKAY= and =BI_SECURE= are sent. This would result in a state +  of =AUTH_OKAY || AUTH_ROOTOKAY || AUTH_SECURE=. + +  For any lines beginning with =BI_REMOVE=, the file names after the +  key word are sent to [[#_add_rmlist][=_add_rmlist=]]. +  #+begin_src c +  _add_rmlist(as, line); +  #+end_src + +  After scanning is complete, the exit status of the process is +  checked. A non-zero exit status means the request will get denied. + +  An =okay= value is then defined by masking the state with the value +  =AUTH_ALLOW=. + +  #+begin_src c +  okay = as->state & AUTH_ALLOW; +  #+end_src + +  =AUTH_ALLOW= is defined in =login_cap.h=. + +  #+begin_src c +  #define	AUTH_ALLOW	(AUTH_OKAY | AUTH_ROOTOKAY | AUTH_SECURE) +  #+end_src + +  If the status results in a rejection, [[#auth_clrenv][=auth_clrenv=]] is called with +  =as=. This removes any requests the login script has made to set +  environment variables from =as->spool=. + +  =okay= is then returned to the caller. + +** _auth_next_arg +   :PROPERTIES: +   :CUSTOM_ID: _auth_next_arg +   :END: + +   @@html: <details> <summary> @@ +   #+BEGIN_SRC c +   static char *_auth_next_arg(auth_session_t *as) +   #+END_SRC +   @@html: </summary> @@ +   #+begin_src c +   { +       char *arg; + +       if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { +           if ((arg = va_arg(as->ap0, char *)) != NULL) +               return (arg); +           va_end(as->ap0); +           explicit_bzero(&(as->ap0), sizeof(as->ap0)); +       } +       if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { +           if ((arg = va_arg(as->ap, char *)) != NULL) +               return (arg); +           va_end(as->ap); +           explicit_bzero(&(as->ap), sizeof(as->ap)); +       } +       return (NULL); +   } +   #+end_src +   @@html: </details> @@ + +   Loops through =as->ap0= then =as->ap=, returning one argument per +   call. Calls =va_end= on each list once it finishes with them, then +   [[https://man.openbsd.org/man3/bzero.3#explicit_bzero][=explicit_bzero(3)=]]'s them. + +   Finally when it's gone through both lists, returns =NULL= + +** _auth_spool +   :PROPERTIES: +   :CUSTOM_ID: _auth_spool +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   static void _auth_spool(auth_session_t *as, int fd) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       ssize_t r; +       char *b, *s; + +       for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) { +           r = read(fd, as->spool + as->index, +               sizeof(as->spool) - as->index); +           if (r <= 0) { +               as->spool[as->index] = '\0'; +               return; +           } +           b = as->spool + as->index; +           as->index += r; +           /* +            ,* Convert newlines into NULs to allow easy scanning of the +            ,* file and receive an fd if there is a BI_FDPASS message. +            ,* XXX - checking for BI_FDPASS here is annoying but +            ,*       we need to avoid the read() slurping in control data. +            ,*/ +           while (r-- > 0) { +               if (*b++ == '\n') { +                   b[-1] = '\0'; +                   if (strcasecmp(s, BI_FDPASS) == 0) +                       _recv_fd(as, fd); +                   s = b; +               } +           } +       } + +       syslog(LOG_ERR, "Overflowed backchannel spool buffer"); +       errx(1, "System error in authentication program"); +   } +   #+end_src +   @@html: </details> @@ + +   =_auth_spool='s job is to read data from =fd= and place it in +   =as->spool=, and to update =as->index= with the length of the data +   on the spool. While spooling it converts newlines to =NUL='s in +   order to parse the output more easily. It also handles any file +   descriptors passed through the back channel by sending them to +   [[#_recv_fd][=_recv_fd=]]. + +   #+begin_src c +   // [...] +   if (strcasecmp(s, BI_FDPASS) == 0) +       _recv_fd(as, fd); +   #+end_src + +** _recv_fd +   :PROPERTIES: +   :CUSTOM_ID: _recv_fd +   :END: + +   @@html: <details> <summary> @@ +   #+begin_src c +   static void _recv_fd(auth_session_t *as, int fd) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct msghdr msg; +       struct cmsghdr *cmp; +       union { +           struct cmsghdr hdr; +           char buf[CMSG_SPACE(sizeof(int))]; +       } cmsgbuf; + +       memset(&msg, 0, sizeof(msg)); +       msg.msg_control = &cmsgbuf.buf; +       msg.msg_controllen = sizeof(cmsgbuf.buf); +       if (recvmsg(fd, &msg, 0) == -1) +           syslog(LOG_ERR, "recvmsg: %m"); +       else if (msg.msg_flags & MSG_TRUNC) +           syslog(LOG_ERR, "message truncated"); +       else if (msg.msg_flags & MSG_CTRUNC) +           syslog(LOG_ERR, "control message truncated"); +       else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL) +           syslog(LOG_ERR, "missing control message"); +       else { +           if (cmp->cmsg_level != SOL_SOCKET) +               syslog(LOG_ERR, "unexpected cmsg_level %d", +                   cmp->cmsg_level); +           else if (cmp->cmsg_type != SCM_RIGHTS) +               syslog(LOG_ERR, "unexpected cmsg_type %d", +                   cmp->cmsg_type); +           else if (cmp->cmsg_len != CMSG_LEN(sizeof(int))) +               syslog(LOG_ERR, "bad cmsg_len %d", +                   cmp->cmsg_len); +           else { +               if (as->fd != -1) +                   close(as->fd); +               as->fd = *(int *)CMSG_DATA(cmp); +           } +       } +   } +   #+end_src +   @@html: </details> @@ + +   =_recv_fd= reads control messages, also called ancillary data, from +   =fd= and tries to receive a file descriptor. It does this using the +   [[https://man.openbsd.org/CMSG_DATA.3][control message API]]. + +   If it receives one and =as->fd= is equal to =-1=, it sets it to the +   received file descriptor. Otherwise it closes the received file +   descriptor. + +** _add_rmlist +   :PROPERTIES: +   :CUSTOM_ID: _add_rmlist +   :END: +   @@html: <details> <summary> @@ +   #+begin_src c +   static void _add_rmlist(auth_session_t *as, char *file) +   #+end_src +   @@html: </summary> @@ +   #+begin_src c +   { +       struct rmfiles *rm; +       size_t i = strlen(file) + 1; + +       // XXX should rangecheck i since we are about to add? + +       if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { +           syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); +           return; +       } +       rm->file = (char *)(rm + 1); +       rm->next = as->rmlist; +       strlcpy(rm->file, file, i); +       as->rmlist = rm; +   } +   #+end_src +   @@html: </details> @@ + +   =_add_rmlist= is used to add to the list of files to be removed +   after authentication is complete + +   A =rmfiles= struct is allocated and appended to the end of the +   =as->rmlist= linked list. + +* auth_close +  :PROPERTIES: +  :CUSTOM_ID: auth_close +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  int auth_close(auth_session_t *as) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      struct rmfiles *rm; +      struct authopts *opt; +      struct authdata *data; +      int s; + +      /* +       ,* Save our return value +       ,*/ +      s = as->state & AUTH_ALLOW; + +      if (s == 0) +          as->index = 0; + +      auth_setenv(as); + + +      /* +       ,* Clean out the rmlist and remove specified files if the +       ,* authentication failed +       ,*/ +      while ((rm = as->rmlist) != NULL) { +          as->rmlist = rm->next; +          if (s == 0) +              unlink(rm->file); +          free(rm); +      } + +      /* +       ,* Clean out the opt list +       ,*/ +      while ((opt = as->optlist) != NULL) { +          as->optlist = opt->next; +          free(opt); +      } + +      /* +       ,* Clean out data +       ,*/ +      while ((data = as->data) != NULL) { +          if (as->data->len) +              explicit_bzero(as->data->ptr, as->data->len); +          as->data = data->next; +          free(data); +      } + +      if (as->pwd != NULL) { +          explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); +          free(as->pwd); +          as->pwd = NULL; +      } + +      /* +       ,* Clean up random variables +       ,*/ +      if (as->service && as->service != defservice) +          free(as->service); +      free(as->challenge); +      free(as->class); +      free(as->style); +      free(as->name); + +      free(as); +      return (s); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_close][=auth_close=]] is responsible for setting the environment variables, +  removing any files requested by the authentication module, and +  freeing =as=. + +  First it saves the allow state of =as->state= in a variable =s=. + +  #+begin_src c +  s = as->state & AUTH_ALLOW; +  #+end_src + +  If =s= is equal to =0= (failure), =as->index= is set to =0=, truncating +  =as->spool= so that no further functions will be able to read from +  it. + +  It then modifies the environment using [[#auth_setenv][=auth_setenv=]] + +  #+begin_src c +  auth_setenv(as); +  #+end_src + +  All =as->rmlist= structs are checked. If =s= is equal to =0=, the +  files are deleted. All =rmlist= structs are then freed. + +  All =as->optlist= structs are freed. + +  All =as->data= structs are [[https://man.openbsd.org/man3/bzero.3#explicit_bzero][=explicit_bzero(3)=]]'d and then freed. + +  =as->pwd= is =explicit_bzero='d and freed. + +  All remaining structs referenced by =as= are freed. + +  =as= is freed. + +  =s= is returned. + +* auth_userchallenge +  :PROPERTIES: +  :CUSTOM_ID: auth_userchallenge +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  auth_session_t *auth_userchallenge(char *name, char *style, char *type, char **challengep) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; +      auth_session_t *as; +      login_cap_t *lc; +      struct passwd pwstore, *pwd = NULL; +      char *slash, pwbuf[_PW_BUF_LEN]; + +      if (!_auth_validuser(name)) +          return (NULL); +      if (strlen(name) >= sizeof(namebuf)) +          return (NULL); +      strlcpy(namebuf, name, sizeof namebuf); +      name = namebuf; + +      /* +       ,* Split up user:style names if we were not given a style +       ,*/ +      if (style == NULL && (style = strchr(name, ':')) != NULL) +          ,*style++ = '\0'; + +      /* +       ,* Cope with user/instance.  We are only using this to get +       ,* the class so it is okay if we strip a /root instance +       ,* The actual login script will pay attention to the instance. +       ,*/ +      getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +      if (pwd == NULL) { +          if ((slash = strchr(name, '/')) != NULL) { +              ,*slash = '\0'; +              getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +              ,*slash = '/'; +          } +      } +      if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) +          return (NULL); + +      if ((style = login_getstyle(lc, style, type)) == NULL || +          (as = auth_open()) == NULL) { +          login_close(lc); +          return (NULL); +      } +      if (auth_setitem(as, AUTHV_STYLE, style) < 0 || +          auth_setitem(as, AUTHV_NAME, name) < 0 || +          auth_setitem(as, AUTHV_CLASS, lc->lc_class) < 0) { +          auth_close(as); +          login_close(lc); +          return (NULL); +      } +      login_close(lc); +      ,*challengep = auth_challenge(as); +      return (as); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_userchallenge][=auth_userchallenge=]] is used when the authentication style requires +  that the user be presented with a challenge, but the user cannot be +  directly interacted with over the terminal. As an example, this +  might be used in cases where the user is using S/KEY authentication +  over SSH. + +  A fair portion of this function is very similar to +  [[#auth_usercheck][=auth_usercheck=]]. Instead of having a password argument however, it +  has a pointer to string, which is used to return the challenge to +  the calling function. + +  It first checks that =name= is a valid username using [[#_auth_validuser][=_auth_validuser=]]. + +  If =style= is =NULL=, it checks if =name= is in the =user:style= +  format, and splits it accordingly. + +  It then gets the user's password database entry through +  [[https://man.openbsd.org/man3/getpwnam.3#getpwnam_r][=getpwman_r(3)=]], which operates on the [[https://man.openbsd.org/passwd.5][=passwd(5)=]] database. It then +  uses that to retrieve the user's login class using +  [[https://man.openbsd.org/login_getclass#login_getclass][=login_getclass(3)=]], which returns a =login_cap_t=. Login classes +  are stored in the [[https://man.openbsd.org/man5/login.conf.5][=login.conf(5)=]] database. + +  That struct is then passed into [[https://man.openbsd.org/login_getclass#login_getstyle][=login_getstyle(3)=]], which also +  received the =style= and =type=. If =type= is =NULL=, it returns +  the first available login style for that class. If =style= is +  specified, it is returned if available, otherwise =NULL= is +  returned, which causes =auth_userchallenge= to return =NULL= as +  well. + +  This is where =auth_userchallenge= and [[#auth_usercheck][=auth_usercheck=]] begin to diverge. + +  It creates a new auth session using [[#auth_open][=auth_open=]] as variable =as=. + +  The =style=, =name= and =class= properties of the session are then +  set using [[#auth_setitem][=auth_setitem=]]. + +  It then calls [[#auth_challenge][=auth_challenge=]] with =as= as the argument. The return +  value from that call is used to set =challengep=, and =as= is +  returned. + +  #+begin_src c +  *challengep = auth_challenge(as); +  return (as); +  #+end_src + +* auth_challenge +  :PROPERTIES: +  :CUSTOM_ID: auth_challenge +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  char *auth_challenge(auth_session_t *as) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      char path[PATH_MAX]; +      int len; + +      if (as == NULL || as->style == NULL || as->name == NULL || +          !_auth_validuser(as->name)) +          return (NULL); + +      len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); +      if (len < 0 || len >= sizeof(path)) +          return (NULL); + +      as->state = 0; + +      free(as->challenge); +      as->challenge = NULL; + +      auth_call(as, path, as->style, "-s", "challenge", "--", as->name, +          as->class, (char *)NULL); +      if (as->state & AUTH_CHALLENGE) +          as->challenge = auth_getvalue(as, "challenge"); +      as->state = 0; +      as->index = 0;	/* toss our data */ +      return (as->challenge); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_challenge][=auth_challenge=]], much like [[#auth_verify][=auth_verify=]] is a function that acts as +  a front-end for [[#auth_call][=auth_call=]], except used specifically for +  challenges. + +  First the session =as= is checked. If it's =NULL=, or =as->style= is +  =NULL=, =as->name= is =NULL=, or if the username begins with a +  hyphen, or has a length of zero, the function returns =NULL=. + +  Then the path to the auth module is created. + +  #+begin_src c +  snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); +  #+end_src + +  =as->state= and =as->challenge= are then reset, in case they were +  already set. + +  Then [[#auth_call][=auth_call=]] is called, with the challenge style set. + +  #+begin_src c +  auth_call(as, path, as->style, "-s", "challenge", "--", as->name, as->class, (char *)NULL); +  #+end_src + +  =as->state= is checked for the =AUTH_CHALLENGE= bit, indicating the +  auth module has returned a challenge. If it's present, the challenge +  is extracted from the back channel output, and used to set +  =as->challenge=. + +  #+begin_src c +  if (as->state & AUTH_CHALLENGE) +		as->challenge = auth_getvalue(as, "challenge"); +  #+end_src + +  =as->state= and =as->index= are then set to zero, discarding the +  data. + +  =as->challenge= is then returned. + +* auth_userresponse +  :PROPERTIES: +  :CUSTOM_ID: auth_userresponse +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  int auth_userresponse(auth_session_t *as, char *response, int more) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      char path[PATH_MAX]; +      char *style, *name, *challenge, *class; +      int len; + +      if (as == NULL) +          return (0); + +      auth_setstate(as, 0); + +      if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL || +          (name = auth_getitem(as, AUTHV_NAME)) == NULL || +          !_auth_validuser(name)) { +          if (more == 0) +              return (auth_close(as)); +          return(0); +      } + +      len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); +      if (len < 0 || len >= sizeof(path)) { +          if (more == 0) +              return (auth_close(as)); +          return (0); +      } + +      challenge = auth_getitem(as, AUTHV_CHALLENGE); +      class = auth_getitem(as, AUTHV_CLASS); + +      if (challenge) +          auth_setdata(as, challenge, strlen(challenge) + 1); +      else +          auth_setdata(as, "", 1); +      if (response) { +          auth_setdata(as, response, strlen(response) + 1); +          explicit_bzero(response, strlen(response)); +      } else +          auth_setdata(as, "", 1); + +      auth_call(as, path, style, "-s", "response", "--", name, +                class, (char *)NULL); + +      /* +       * If they authenticated then make sure they did not expire +       */ +      if (auth_getstate(as) & AUTH_ALLOW) +          auth_check_expire(as); +      if (more == 0) +          return (auth_close(as)); +      return (auth_getstate(as) & AUTH_ALLOW); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_userresponse][=auth_userresponse=]] is used to pass the user's response from +  [[#auth_userchallenge][=auth_userchallenge=]] back to the authentication module. Similar to +  =auth_userchallenge=, it is also a front-end for [[#auth_call][=auth_call=]]. + +  If =as= is =NULL=, =0= is returned. + +  The state of =as= is then set to =0=. +  #+begin_src c +  auth_setstate(as, 0); +  #+end_src + +  =as= is then checked to ensure all the required items are set. It +  checks if =as->style= or =as->name= are =NULL=, or if the username +  is invalid using [[#_auth_validuser][=_auth_validuser=]]. If any of those checks fail, and +  =more= is equal to =0=, then the session is closed using +  [[#auth_close][=auth_close=]], and the return value of that returned. Otherwise =0= +  is returned. + +  Then the path to the [[#modules][auth module]] is created similarly to how it is +  created in [[#auth_verify][=auth_verify=]]. + +  The challenge and class of the session are extracted and stored in +  variables =challenge= and =class= respectively. + +  If =challenge= contains data, its contents are added to the +  =as->data= spool, otherwise an empty string is added to the spool. + +  If =response= contains data, it is added to the data spool as well, +  and then =respose= is =explicit_bzero='d. Otherwise an empty string +  is added to the data spool. + +  Next [[#auth_call][=auth_call=]] is used to call the auth module with service type +  =response=. + +  #+begin_src c +  auth_call(as, path, style, "-s", "response", "--", name, +          class, (char *)NULL); +  #+end_src + +  If the request is allowed, it's checked to make sure it's not +  expired using [[#auth_check_expire][=auth_check_expire=]]. + +  If =more= is equal to =0=, the session is closed using [[#auth_close][=auth_close=]] +  and the return value from it is returned. + +  The allow state of the session is then returned. + +  #+begin_src c +  return (auth_getstate(as) & AUTH_ALLOW); +  #+end_src + +* auth_approval +  :PROPERTIES: +  :CUSTOM_ID: auth_approval +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  int auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      int close_on_exit, close_lc_on_exit, len; +      struct passwd pwstore, *pwd; +      char *approve, *s, path[PATH_MAX], pwbuf[_PW_BUF_LEN]; + +      pwd = NULL; +      close_on_exit = as == NULL; +      close_lc_on_exit = lc == NULL; + +      if (as != NULL && name == NULL) +          name = auth_getitem(as, AUTHV_NAME); + +      if (as != NULL) +          pwd = auth_getpwd(as); + +      if (pwd == NULL) { +          if (name != NULL) { +              if (!_auth_validuser(name)) { +                  warnx("cannot approve who we don't recognize"); +                  return (0); +              } +              getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +          } else { +              getpwuid_r(getuid(), &pwstore, pwbuf, sizeof(pwbuf), +                  &pwd); +              if (pwd == NULL) { +                  syslog(LOG_ERR, "no such user id %u", getuid()); +                  warnx("cannot approve who we don't recognize"); +                  return (0); +              } +              name = pwd->pw_name; +          } +      } + +      if (name == NULL) +          name = pwd->pw_name; + +      if (lc == NULL) { +          if (strlen(name) >= PATH_MAX) { +              syslog(LOG_ERR, "username to login %.*s...", +                  PATH_MAX, name); +              warnx("username too long"); +              return (0); +          } +          if (pwd == NULL && (approve = strchr(name, '.')) != NULL) { +              strlcpy(path, name, sizeof path); +              path[approve - name] = '\0'; +              getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); +          } +          lc = login_getclass(pwd ? pwd->pw_class : NULL); +          if (lc == NULL) { +              warnx("unable to classify user"); +              return (0); +          } +      } + +      if (!type) +          type = LOGIN_DEFSERVICE; +      else { +          if (strncmp(type, "approve-", 8) == 0) +              type += 8; + +          len = snprintf(path, sizeof(path), "approve-%s", type); +          if (len < 0 || len >= sizeof(path)) { +              if (close_lc_on_exit) +                  login_close(lc); +              syslog(LOG_ERR, "approval path too long %.*s...", +                  PATH_MAX, type); +              warnx("approval script path too long"); +              return (0); +          } +      } + +      if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL) +          approve = login_getcapstr(lc, s = "approve", NULL, NULL); + +      if (approve && approve[0] != '/') { +          if (close_lc_on_exit) +              login_close(lc); +          syslog(LOG_ERR, "Invalid %s script: %s", s, approve); +          warnx("invalid path to approval script"); +          free(approve); +          return (0); +      } + +      if (as == NULL && (as = auth_open()) == NULL) { +          if (close_lc_on_exit) +              login_close(lc); +          syslog(LOG_ERR, "%m"); +          warn(NULL); +          free(approve); +          return (0); +      } + +      auth_setstate(as, AUTH_OKAY); +      if (auth_setitem(as, AUTHV_NAME, name) < 0) { +          syslog(LOG_ERR, "%m"); +          warn(NULL); +          goto out; +      } +      if (auth_check_expire(as) < 0)	/* is this account expired */ +          goto out; +      if (_auth_checknologin(lc, +          auth_getitem(as, AUTHV_INTERACTIVE) != NULL)) { +          auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); +          goto out; +      } +      if (login_getcapbool(lc, "requirehome", 0) && pwd && pwd->pw_dir && +          pwd->pw_dir[0]) { +          struct stat sb; + +          if (stat(pwd->pw_dir, &sb) == -1 || !S_ISDIR(sb.st_mode) || +              (pwd->pw_uid && sb.st_uid == pwd->pw_uid && +              (sb.st_mode & S_IXUSR) == 0)) { +              auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); +              goto out; +          } +      } +      if (approve) +          auth_call(as, approve, strrchr(approve, '/') + 1, "--", name, +              lc->lc_class, type, (char *)NULL); + +  out: +      free(approve); +      if (close_lc_on_exit) +          login_close(lc); + +      if (close_on_exit) +          return (auth_close(as)); +      return (auth_getstate(as) & AUTH_ALLOW); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_approval][=auth_approval=]] is used to check a user against the [[#approval][approval script]] +  for service =type=. It is a front end for [[#auth_call][=auth_call=]]. Approval +  script types all begin with =approval-=. + +  Before running the scripts, first the validity of the account is +  checked. This is done first using [[#auth_check_expired][=auth_check_expired=]], then +  [[#_auth_checknologin][=_auth_checknologin=]], and finally [[https://man.openbsd.org/login_getcapbool#login_getcapbool][=login_getcapbool=]] to ensure the +  user has a home directory if one is required by their login class. + +  If =type= doesn't begin with =approval-= it will be prepended +  internally. + +  if =as= is =NULL=, an auth session will be created and destroyed +  inside the function. + +  If =lc= is =NULL=, it will be retrieved internally by looking up +  =name=. + +  If =type= is =NULL=, the default of =LOGIN_DEFSERVICE= is used. This +  is defined in =login_cap.h= as =login=. This should call the default +  =approval= script, according to the [[https://man.openbsd.org/login.conf#CAPABILITIES][=CAPABILITIES=]] section of the +  =login.conf= man page. + +  It returns either =0= for disapproval, or non-zero for approval. + +* auth_check_expire +  :PROPERTIES: +  :CUSTOM_ID: auth_check_expire +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  quad_t auth_check_expire(auth_session_t *as) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { +          as->state &= ~AUTH_ALLOW; +          as->state |= AUTH_EXPIRED;	/* XXX */ +          return (-1); +      } + +      if (as->pwd == NULL) +          return (0); + +      if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { +          if (as->now.tv_sec == 0) +              WRAP(gettimeofday)(&as->now, NULL); +          if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { +              as->state &= ~AUTH_ALLOW; +              as->state |= AUTH_EXPIRED; +          } +          if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) +              return (-1); +          return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); +      } +      return (0); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_check_expire][=auth_check_expire=]] is used to check if the account used for a +  session is expired. If an account is valid, it returns =0=. +  Otherwise it returns a negative number representing the number of +  seconds elapsed since the account expired. If there's no account +  associated with the session, it will return =-1=. + +  It first checks if =as->pwd= is set, and if it isn't it tries to set +  it using [[#auth_setpwd][=auth_setpwd=]]. If both of those fail, then it returns =-1= +  and removes the =AUTH_ALLOW= bitmask from =as->state=, and adds the +  bitmask for =AUTH_EXPIRED=. + +* auth_check_change +  :PROPERTIES: +  :CUSTOM_ID: auth_check_change +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  quad_t auth_check_change(auth_session_t *as) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { +          as->state &= ~AUTH_ALLOW; +          as->state |= AUTH_PWEXPIRED;	/* XXX */ +          return (-1); +      } + +      if (as->pwd == NULL) +          return (0); + +      if (as->pwd && (quad_t)as->pwd->pw_change) { +          if (as->now.tv_sec == 0) +              WRAP(gettimeofday)(&as->now, NULL); +          if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { +              as->state &= ~AUTH_ALLOW; +              as->state |= AUTH_PWEXPIRED; +          } +          if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) +              return (-1); +          return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); +      } +      return (0); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/auth_subr.3#auth_check_change][=auth_check_change=]] is used to check if the password associated with +  an account is expired. If the password isn't expired, it returns +  =0=. Otherwise it returns a negative number representing the number +  of seconds elapsed since the password expired. If there's no account +  associated with the session, it will return =-1=. + +  It operates very similarly to [[#auth_check_expire][=auth_check_expire=]]. + +* auth_checknologin +  :PROPERTIES: +  :CUSTOM_ID: auth_checknologin +  :END: +  @@html: <details> <summary> @@ +  #+begin_src c +  void auth_checknologin(login_cap_t *lc) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      if (_auth_checknologin(lc, 1)) +          exit(1); +  } + +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/authenticate.3#auth_checknologin][=auth_checknologin=]] is a simple wrapper around the internal +  [[#_auth_checknologin][=_auth_checknologin=]]. If the user is now allowed to login, it prints +  the reason and calls =exit(1)=. + +* auth_cat +  :PROPERTIES: +  :CUSTOM_ID: auth_cat +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  int auth_cat(char *file) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      int fd, nchars; +      char tbuf[8192]; + +      if ((fd = open(file, O_RDONLY, 0)) == -1) +          return (0); +      while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) +          (void)write(fileno(stdout), tbuf, nchars); +      (void)close(fd); +      return (1); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/man3/authenticate.3#auth_cat][=auth_cat=]] is a helper function that will write the contents of a +  =file= to =stdout=. It returns =0= on failure or =1= on success. + +* auth_mkvalue +  :PROPERTIES: +  :CUSTOM_ID: auth_mkvalue +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  char *auth_mkvalue(char *value) +  #+end_src + +  @@html: </summary> @@ +  #+begin_src c +  { +  	char *big, *p; + +  	big = malloc(strlen(value) * 4 + 1); +  	if (big == NULL) +  		return (NULL); +  	/* +  	 ,* XXX - There should be a more standardized +  	 ,* routine for doing this sort of thing. +  	 ,*/ +  	for (p = big; *value; ++value) { +  		switch (*value) { +  		case '\r': +  			,*p++ = '\\'; +  			,*p++ = 'r'; +  			break; +  		case '\n': +  			,*p++ = '\\'; +  			,*p++ = 'n'; +  			break; +  		case '\\': +  			,*p++ = '\\'; +  			,*p++ = *value; +  			break; +  		case '\t': +  		case ' ': +  			if (p == big) +  				,*p++ = '\\'; +  			,*p++ = *value; +  			break; +  		default: +  			if (!isprint((unsigned char)*value)) { +  				,*p++ = '\\'; +  				,*p++ = ((*value >> 6) & 0x3) + '0'; +  				,*p++ = ((*value >> 3) & 0x7) + '0'; +  				,*p++ = ((*value     ) & 0x7) + '0'; +  			} else +  				,*p++ = *value; +  			break; +  		} +  	} +  	,*p = '\0'; +  	return (big); +  } +  #+end_src +  @@html: </details> @@ + +  [[https://man.openbsd.org/authenticate.3#auth_mkvalue][=auth_mkvalue=]] creates an escaped string which can be decoded by [[#auth_getvalue][=auth_getvalue=]]. + +* _auth_validuser +  :PROPERTIES: +  :CUSTOM_ID: _auth_validuser +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  int _auth_validuser(const char *name) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      /* User name must be specified and may not start with a '-'. */ +      if (*name == '\0' || *name == '-') { +          syslog(LOG_ERR, "invalid user name %s", name); +          return 0; +      } +      return 1; +  } +  #+end_src +  @@html: </details> @@ + +  =_auth_validuser= is a small helper function used to check if a +  username passes some very basic validity criteria. Those being that +  it must not be an empty sting, and that it doesn't start with a +  hyphen. + +  If a username is invalid, it is logged in the syslog. + +  It returns =1= if the username is valid, otherwise it returns =0=. + +* _auth_checknologin +  :PROPERTIES: +  :CUSTOM_ID: _auth_checknologin +  :END: + +  @@html: <details> <summary> @@ +  #+begin_src c +  static int _auth_checknologin(login_cap_t *lc, int print) +  #+end_src +  @@html: </summary> @@ +  #+begin_src c +  { +      struct stat sb; +      char *nologin; +      int mustfree; + +      if (login_getcapbool(lc, "ignorenologin", 0)) +          return (0); + +      /* +       ,* If we fail to get the nologin file due to a database error, +       ,* assume there should have been one... +       ,*/ +      nologin = login_getcapstr(lc, "nologin", "", NULL); +      mustfree = nologin && *nologin != '\0'; +      if (nologin == NULL) +          goto print_nologin; + +      /* First try the nologin file specified in login.conf. */ +      if (*nologin != '\0' && stat(nologin, &sb) == 0) +          goto print_nologin; +      if (mustfree) { +          free(nologin); +          mustfree = 0; +      } + +      /* If that doesn't exist try _PATH_NOLOGIN. */ +      if (stat(_PATH_NOLOGIN, &sb) == 0) { +          nologin = _PATH_NOLOGIN; +          goto print_nologin; +      } + +      /* Couldn't stat any nologin files, must be OK to login. */ +      return (0); + +  print_nologin: +      if (print) { +          if (!nologin || *nologin == '\0' || auth_cat(nologin) == 0) { +              puts("Logins are not allowed at this time."); +              fflush(stdout); +          } +      } +      if (mustfree) +          free(nologin); +      return (-1); +  } +  #+end_src +  @@html: </details> @@ + +  =_auth_checknologin= is a helper function in =authenticate.c=. It is +  used to check the =nologin= status of the account. If =print= is +  non-zero, it will print the reason for the failure, and print the +  contents of the nologin file using [[#auth_cat][=auth_cat=]]. + +  It returns =0= if the user is allowed to login, and =-1= otherwise. + +* Call Graph +  :PROPERTIES: +  :CUSTOM_ID: graph +  :END: + +  #+ATTR_HTML: :title Authentication call graph +  #+ATTR_HTML: :title Authentication call graph +  [[file:graph.svg]] + +  @@html: <details> <summary> @@ +  *Click here* to see the code that generates the call graph. +  @@html: </summary> @@ +#+INCLUDE: "gen_dot.rb" src ruby +  @@html: </details> @@ + +* Copyright +  :PROPERTIES: +  :CUSTOM_ID: copyright +  :END: + +  @@html: <details> <summary> @@ +  *Click here to expand copyright notices* +  @@html: </summary> @@ +  #+CAPTION: From [[https://github.com/openbsd/src/blob/master/lib/libc/gen/authenticate.c][=authenticate.c=]] +  #+begin_src text +  /*	$OpenBSD: authenticate.c,v 1.28 2019/12/04 06:25:45 deraadt Exp $	*/ + +  /*- +   ,* Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. +   ,* +   ,* Redistribution and use in source and binary forms, with or without +   ,* modification, are permitted provided that the following conditions +   ,* are met: +   ,* 1. Redistributions of source code must retain the above copyright +   ,*    notice, this list of conditions and the following disclaimer. +   ,* 2. Redistributions in binary form must reproduce the above copyright +   ,*    notice, this list of conditions and the following disclaimer in the +   ,*    documentation and/or other materials provided with the distribution. +   ,* 3. All advertising materials mentioning features or use of this software +   ,*    must display the following acknowledgement: +   ,*	This product includes software developed by Berkeley Software Design, +   ,*	Inc. +   ,* 4. The name of Berkeley Software Design, Inc.  may not be used to endorse +   ,*    or promote products derived from this software without specific prior +   ,*    written permission. +   ,* +   ,* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND +   ,* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +   ,* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +   ,* ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE +   ,* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +   ,* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +   ,* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +   ,* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +   ,* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +   ,* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +   ,* SUCH DAMAGE. +   ,* +   ,*	BSDI $From: authenticate.c,v 2.21 1999/09/08 22:33:26 prb Exp $ +   ,*/ +  #+end_src + +  #+CAPTION: From [[https://github.com/openbsd/src/blob/master/lib/libc/gen/auth_subr.c][=auth_subr.c=]] +  #+begin_src text +  /*	$OpenBSD: auth_subr.c,v 1.56 2020/10/13 04:42:28 guenther Exp $	*/ + +  /* +   ,* Copyright (c) 2000-2002,2004 Todd C. Miller <millert@openbsd.org> +   ,* +   ,* Permission to use, copy, modify, and distribute this software for any +   ,* purpose with or without fee is hereby granted, provided that the above +   ,* copyright notice and this permission notice appear in all copies. +   ,* +   ,* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +   ,* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +   ,* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +   ,* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +   ,* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +   ,* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +   ,* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +   ,*/ +  /*- +   ,* Copyright (c) 1995,1996,1997 Berkeley Software Design, Inc. +   ,* All rights reserved. +   ,* +   ,* Redistribution and use in source and binary forms, with or without +   ,* modification, are permitted provided that the following conditions +   ,* are met: +   ,* 1. Redistributions of source code must retain the above copyright +   ,*    notice, this list of conditions and the following disclaimer. +   ,* 2. Redistributions in binary form must reproduce the above copyright +   ,*    notice, this list of conditions and the following disclaimer in the +   ,*    documentation and/or other materials provided with the distribution. +   ,* 3. All advertising materials mentioning features or use of this software +   ,*    must display the following acknowledgement: +   ,*	This product includes software developed by Berkeley Software Design, +   ,*	Inc. +   ,* 4. The name of Berkeley Software Design, Inc.  may not be used to endorse +   ,*    or promote products derived from this software without specific prior +   ,*    written permission. +   ,* +   ,* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND +   ,* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +   ,* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +   ,* ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE +   ,* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +   ,* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +   ,* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +   ,* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +   ,* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +   ,* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +   ,* SUCH DAMAGE. +   ,* +   ,*	BSDI $From: auth_subr.c,v 2.4 1999/09/08 04:10:40 prb Exp $ +   ,*/ +  #+end_src +  @@html: </details> @@ + +#+begin_export html +<style> +  details > summary { +    list-style: none; +  } +  details > summary::-webkit-details-marker { +    display: none; +  } +</style> +#+end_export diff --git a/content/posts/how-bsd-authentication-works/notes.org b/content/posts/how-bsd-authentication-works/notes.org new file mode 100644 index 0000000..9bd67d4 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/notes.org @@ -0,0 +1,83 @@ +* Notes +  https://web.archive.org/web/20170327150148/http://www.penzin.net/bsdauth/ +  - In the man page for [[https://man.openbsd.org/auth_subr.3#auth_call][=auth_call=]] it says +    #+begin_src text +    path    The full path name of the login script to run.  The call will +                 fail if path does not pass the requirements of the secure_path(3) +                 function. +    #+end_src + +    However I don't see this enforced anywhere, I even wrote a small test +    script to prove it. + +    #+CAPTION: =authfail.c= +    #+begin_src c +    #include <sys/types.h> +    #include <login_cap.h> +    #include <bsd_auth.h> +    #include <stdio.h> + +    int main(void) { +        auth_session_t *as; + +        as = auth_open(); +        auth_call(as, "/home/dante/auth_tests/authtest/test", "hello", NULL); +        auth_close(as); +    } +    #+end_src + +    Changing ="/home/dante/auth_tests/authtest/test"= to the location +    of the =test= binary. + +    #+CAPTION: =test.c= +    #+begin_src c +    #include <stdio.h> + +    int main(void) { +        printf("Hello! I don't have a secure path!\n"); +        return 0; +    } +    #+end_src + +    #+CAPTION: =Makefile= +    #+begin_src makefile +    CFLAGS = -Wall -Wextra + +    run: authfail test +        ./authfail + +    authfail: authfail.c +        $(CC) -o $@ $(CFLAGS) $< + +    test: test.c +        $(CC) -o $@ $(CFLAGS) $< +    #+end_src + +    Which results in the following: + +    #+begin_src text +    $ pwd && ls -l && make +    /home/dante/auth_tests/authtest +    total 12 +    -rw-r--r--  1 dante  dante  143 May 30 19:20 Makefile +    -rw-r--r--  1 dante  dante  248 May 29 19:30 authfail.c +    -rw-r--r--  1 dante  dante  115 May 29 19:22 test.c +    cc -o authfail -Wall -Wextra authfail.c +    cc -o test -Wall -Wextra test.c +    ./authfail +    Hello! I don't have a secure path! +    #+end_src + +  - The manpage also says the path is limited to =/bin/= and =/usr/bin=, +    which is also not the case. + +  - The man page describes the interface for =auth_getitem= is in the +    format of =AUTH_<item>=, but in reality it is =AUTHV_<item>=. + + # Ask jcs about the file descriptor situation, I don't understand it + # after reading both the man page and source. + + - The [[#auth_getchallenge][=auth_getchallenge=]] function in the [[https://man.openbsd.org/auth_subr.3#auth_getchallenge][=auth_subr(3)=]] man page +   doesn't seem to exist in the source code. + +** TODO How are these configured in login.conf? diff --git a/content/posts/how-bsd-authentication-works/openbsd_internals.gif b/content/posts/how-bsd-authentication-works/openbsd_internals.gifBinary files differ new file mode 100644 index 0000000..5088082 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/openbsd_internals.gif | 
