From 894afa96cd14a84cd1a1bcfb9523f10210aebb7c Mon Sep 17 00:00:00 2001
From: Dante Catalfamo
Date: Mon, 18 Oct 2021 17:36:47 -0400
Subject: bsd-auth: no longer WIP

---
 .../WIP-how-bsd-authentication-works/gen_dot.rb    |   99 -
 .../WIP-how-bsd-authentication-works/graph.dot     |  152 --
 .../WIP-how-bsd-authentication-works/graph.svg     |  856 ------
 .../WIP-how-bsd-authentication-works/index.org     | 2811 --------------------
 .../WIP-how-bsd-authentication-works/notes.org     |   83 -
 .../openbsd_internals.gif                          |  Bin 690203 -> 0 bytes
 .../posts/how-bsd-authentication-works/gen_dot.rb  |   99 +
 .../posts/how-bsd-authentication-works/graph.dot   |  152 ++
 .../posts/how-bsd-authentication-works/graph.svg   |  856 ++++++
 .../posts/how-bsd-authentication-works/index.org   | 2811 ++++++++++++++++++++
 .../posts/how-bsd-authentication-works/notes.org   |   83 +
 .../openbsd_internals.gif                          |  Bin 0 -> 690203 bytes
 12 files changed, 4001 insertions(+), 4001 deletions(-)
 delete mode 100755 content/posts/WIP-how-bsd-authentication-works/gen_dot.rb
 delete mode 100644 content/posts/WIP-how-bsd-authentication-works/graph.dot
 delete mode 100644 content/posts/WIP-how-bsd-authentication-works/graph.svg
 delete mode 100644 content/posts/WIP-how-bsd-authentication-works/index.org
 delete mode 100644 content/posts/WIP-how-bsd-authentication-works/notes.org
 delete mode 100644 content/posts/WIP-how-bsd-authentication-works/openbsd_internals.gif
 create mode 100755 content/posts/how-bsd-authentication-works/gen_dot.rb
 create mode 100644 content/posts/how-bsd-authentication-works/graph.dot
 create mode 100644 content/posts/how-bsd-authentication-works/graph.svg
 create mode 100644 content/posts/how-bsd-authentication-works/index.org
 create mode 100644 content/posts/how-bsd-authentication-works/notes.org
 create mode 100644 content/posts/how-bsd-authentication-works/openbsd_internals.gif

diff --git a/content/posts/WIP-how-bsd-authentication-works/gen_dot.rb b/content/posts/WIP-how-bsd-authentication-works/gen_dot.rb
deleted file mode 100755
index 9f71876..0000000
--- a/content/posts/WIP-how-bsd-authentication-works/gen_dot.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/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/WIP-how-bsd-authentication-works/graph.dot b/content/posts/WIP-how-bsd-authentication-works/graph.dot
deleted file mode 100644
index 9a2be3c..0000000
--- a/content/posts/WIP-how-bsd-authentication-works/graph.dot
+++ /dev/null
@@ -1,152 +0,0 @@
-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/WIP-how-bsd-authentication-works/graph.svg b/content/posts/WIP-how-bsd-authentication-works/graph.svg
deleted file mode 100644
index 94f22d2..0000000
--- a/content/posts/WIP-how-bsd-authentication-works/graph.svg
+++ /dev/null
@@ -1,856 +0,0 @@
-<?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&#45;&gt;_auth_checknologin -->
-<g id="edge1" class="edge">
-<title>auth_checknologin&#45;&gt;_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&#45;&gt;auth_cat -->
-<g id="edge4" class="edge">
-<title>_auth_checknologin&#45;&gt;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&#45;&gt;login_getcapstr -->
-<g id="edge3" class="edge">
-<title>_auth_checknologin&#45;&gt;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&#45;&gt;login_getcapbool -->
-<g id="edge2" class="edge">
-<title>_auth_checknologin&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge7" class="edge">
-<title>auth_approval&#45;&gt;_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&#45;&gt;auth_open -->
-<g id="edge11" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_close -->
-<g id="edge17" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_getitem -->
-<g id="edge5" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_setitem -->
-<g id="edge13" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_check_expire -->
-<g id="edge14" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_call -->
-<g id="edge16" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_setstate -->
-<g id="edge12" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_getstate -->
-<g id="edge18" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;auth_getpwd -->
-<g id="edge6" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;login_getclass -->
-<g id="edge8" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;login_getcapstr -->
-<g id="edge10" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;login_getcapbool -->
-<g id="edge15" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;login_close -->
-<g id="edge9" class="edge">
-<title>auth_approval&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge19" class="edge">
-<title>auth_usercheck&#45;&gt;_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&#45;&gt;auth_verify -->
-<g id="edge26" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_open -->
-<g id="edge23" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_setitem -->
-<g id="edge24" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_setdata -->
-<g id="edge25" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;login_getclass -->
-<g id="edge20" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;login_getstyle -->
-<g id="edge21" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;login_close -->
-<g id="edge22" class="edge">
-<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_usercheck -->
-<g id="edge27" class="edge">
-<title>auth_userokay&#45;&gt;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&#45;&gt;auth_close -->
-<g id="edge28" class="edge">
-<title>auth_userokay&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge29" class="edge">
-<title>auth_userchallenge&#45;&gt;_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&#45;&gt;auth_open -->
-<g id="edge32" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_close -->
-<g id="edge35" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_challenge -->
-<g id="edge36" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_setitem -->
-<g id="edge34" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_getclass -->
-<g id="edge30" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_getstyle -->
-<g id="edge31" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_close -->
-<g id="edge33" class="edge">
-<title>auth_userchallenge&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge39" class="edge">
-<title>auth_userresponse&#45;&gt;_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&#45;&gt;auth_close -->
-<g id="edge40" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_getitem -->
-<g id="edge38" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_setdata -->
-<g id="edge41" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_check_expire -->
-<g id="edge43" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_setstate -->
-<g id="edge37" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_getstate -->
-<g id="edge42" class="edge">
-<title>auth_userresponse&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge48" class="edge">
-<title>auth_verify&#45;&gt;_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&#45;&gt;auth_open -->
-<g id="edge44" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_getitem -->
-<g id="edge47" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_setitem -->
-<g id="edge46" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_call -->
-<g id="edge50" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_setstate -->
-<g id="edge45" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_set_va_list -->
-<g id="edge49" class="edge">
-<title>auth_verify&#45;&gt;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&#45;&gt;auth_clrenv -->
-<g id="edge51" class="edge">
-<title>auth_clean&#45;&gt;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&#45;&gt;auth_setitem -->
-<g id="edge52" class="edge">
-<title>auth_clean&#45;&gt;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&#45;&gt;auth_setenv -->
-<g id="edge53" class="edge">
-<title>auth_close&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge54" class="edge">
-<title>auth_challenge&#45;&gt;_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&#45;&gt;auth_getvalue -->
-<g id="edge55" class="edge">
-<title>auth_challenge&#45;&gt;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&#45;&gt;_auth_validuser -->
-<g id="edge57" class="edge">
-<title>auth_setitem&#45;&gt;_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&#45;&gt;auth_setitem -->
-<g id="edge56" class="edge">
-<title>auth_setitem&#45;&gt;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&#45;&gt;auth_setpwd -->
-<g id="edge58" class="edge">
-<title>auth_check_expire&#45;&gt;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&#45;&gt;auth_setpwd -->
-<g id="edge59" class="edge">
-<title>auth_check_change&#45;&gt;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&#45;&gt;auth_clrenv -->
-<g id="edge63" class="edge">
-<title>auth_call&#45;&gt;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&#45;&gt;_auth_spool -->
-<g id="edge61" class="edge">
-<title>auth_call&#45;&gt;_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&#45;&gt;_add_rmlist -->
-<g id="edge62" class="edge">
-<title>auth_call&#45;&gt;_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&#45;&gt;_auth_next_arg -->
-<g id="edge60" class="edge">
-<title>auth_call&#45;&gt;_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&#45;&gt;_recv_fd -->
-<g id="edge64" class="edge">
-<title>_auth_spool&#45;&gt;_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&#45;&gt;login_getcapstr -->
-<g id="edge65" class="edge">
-<title>login_getstyle&#45;&gt;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&#45;&gt;strtolimit -->
-<g id="edge66" class="edge">
-<title>login_getcapsize&#45;&gt;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&#45;&gt;login_getcaptime -->
-<g id="edge67" class="edge">
-<title>gsetrl&#45;&gt;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&#45;&gt;login_getcapnum -->
-<g id="edge69" class="edge">
-<title>gsetrl&#45;&gt;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&#45;&gt;login_getcapsize -->
-<g id="edge68" class="edge">
-<title>gsetrl&#45;&gt;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&#45;&gt;login_getclass -->
-<g id="edge70" class="edge">
-<title>setclasscontext&#45;&gt;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&#45;&gt;login_close -->
-<g id="edge72" class="edge">
-<title>setclasscontext&#45;&gt;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&#45;&gt;setusercontext -->
-<g id="edge71" class="edge">
-<title>setclasscontext&#45;&gt;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&#45;&gt;login_getclass -->
-<g id="edge73" class="edge">
-<title>setusercontext&#45;&gt;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&#45;&gt;login_getcapnum -->
-<g id="edge75" class="edge">
-<title>setusercontext&#45;&gt;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&#45;&gt;login_close -->
-<g id="edge74" class="edge">
-<title>setusercontext&#45;&gt;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&#45;&gt;setuserpath -->
-<g id="edge77" class="edge">
-<title>setusercontext&#45;&gt;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&#45;&gt;setuserenv -->
-<g id="edge76" class="edge">
-<title>setusercontext&#45;&gt;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&#45;&gt;login_setenv -->
-<g id="edge78" class="edge">
-<title>setuserpath&#45;&gt;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&#45;&gt;login_setenv -->
-<g id="edge79" class="edge">
-<title>setuserenv&#45;&gt;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&#45;&gt;expandstr -->
-<g id="edge80" class="edge">
-<title>login_setenv&#45;&gt;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&#45;&gt;strtosize -->
-<g id="edge82" class="edge">
-<title>strtosize&#45;&gt;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&#45;&gt;multiply -->
-<g id="edge81" class="edge">
-<title>strtosize&#45;&gt;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&#45;&gt;strtosize -->
-<g id="edge83" class="edge">
-<title>strtolimit&#45;&gt;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/WIP-how-bsd-authentication-works/index.org b/content/posts/WIP-how-bsd-authentication-works/index.org
deleted file mode 100644
index f0623c2..0000000
--- a/content/posts/WIP-how-bsd-authentication-works/index.org
+++ /dev/null
@@ -1,2811 +0,0 @@
-#+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/WIP-how-bsd-authentication-works/notes.org b/content/posts/WIP-how-bsd-authentication-works/notes.org
deleted file mode 100644
index 9bd67d4..0000000
--- a/content/posts/WIP-how-bsd-authentication-works/notes.org
+++ /dev/null
@@ -1,83 +0,0 @@
-* 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/WIP-how-bsd-authentication-works/openbsd_internals.gif b/content/posts/WIP-how-bsd-authentication-works/openbsd_internals.gif
deleted file mode 100644
index 5088082..0000000
Binary files a/content/posts/WIP-how-bsd-authentication-works/openbsd_internals.gif and /dev/null differ
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&#45;&gt;_auth_checknologin -->
+<g id="edge1" class="edge">
+<title>auth_checknologin&#45;&gt;_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&#45;&gt;auth_cat -->
+<g id="edge4" class="edge">
+<title>_auth_checknologin&#45;&gt;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&#45;&gt;login_getcapstr -->
+<g id="edge3" class="edge">
+<title>_auth_checknologin&#45;&gt;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&#45;&gt;login_getcapbool -->
+<g id="edge2" class="edge">
+<title>_auth_checknologin&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge7" class="edge">
+<title>auth_approval&#45;&gt;_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&#45;&gt;auth_open -->
+<g id="edge11" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_close -->
+<g id="edge17" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_getitem -->
+<g id="edge5" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_setitem -->
+<g id="edge13" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_check_expire -->
+<g id="edge14" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_call -->
+<g id="edge16" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_setstate -->
+<g id="edge12" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_getstate -->
+<g id="edge18" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;auth_getpwd -->
+<g id="edge6" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;login_getclass -->
+<g id="edge8" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;login_getcapstr -->
+<g id="edge10" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;login_getcapbool -->
+<g id="edge15" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;login_close -->
+<g id="edge9" class="edge">
+<title>auth_approval&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge19" class="edge">
+<title>auth_usercheck&#45;&gt;_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&#45;&gt;auth_verify -->
+<g id="edge26" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_open -->
+<g id="edge23" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_setitem -->
+<g id="edge24" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_setdata -->
+<g id="edge25" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;login_getclass -->
+<g id="edge20" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;login_getstyle -->
+<g id="edge21" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;login_close -->
+<g id="edge22" class="edge">
+<title>auth_usercheck&#45;&gt;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&#45;&gt;auth_usercheck -->
+<g id="edge27" class="edge">
+<title>auth_userokay&#45;&gt;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&#45;&gt;auth_close -->
+<g id="edge28" class="edge">
+<title>auth_userokay&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge29" class="edge">
+<title>auth_userchallenge&#45;&gt;_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&#45;&gt;auth_open -->
+<g id="edge32" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_close -->
+<g id="edge35" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_challenge -->
+<g id="edge36" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;auth_setitem -->
+<g id="edge34" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_getclass -->
+<g id="edge30" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_getstyle -->
+<g id="edge31" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;login_close -->
+<g id="edge33" class="edge">
+<title>auth_userchallenge&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge39" class="edge">
+<title>auth_userresponse&#45;&gt;_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&#45;&gt;auth_close -->
+<g id="edge40" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_getitem -->
+<g id="edge38" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_setdata -->
+<g id="edge41" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_check_expire -->
+<g id="edge43" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_setstate -->
+<g id="edge37" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;auth_getstate -->
+<g id="edge42" class="edge">
+<title>auth_userresponse&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge48" class="edge">
+<title>auth_verify&#45;&gt;_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&#45;&gt;auth_open -->
+<g id="edge44" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_getitem -->
+<g id="edge47" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_setitem -->
+<g id="edge46" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_call -->
+<g id="edge50" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_setstate -->
+<g id="edge45" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_set_va_list -->
+<g id="edge49" class="edge">
+<title>auth_verify&#45;&gt;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&#45;&gt;auth_clrenv -->
+<g id="edge51" class="edge">
+<title>auth_clean&#45;&gt;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&#45;&gt;auth_setitem -->
+<g id="edge52" class="edge">
+<title>auth_clean&#45;&gt;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&#45;&gt;auth_setenv -->
+<g id="edge53" class="edge">
+<title>auth_close&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge54" class="edge">
+<title>auth_challenge&#45;&gt;_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&#45;&gt;auth_getvalue -->
+<g id="edge55" class="edge">
+<title>auth_challenge&#45;&gt;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&#45;&gt;_auth_validuser -->
+<g id="edge57" class="edge">
+<title>auth_setitem&#45;&gt;_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&#45;&gt;auth_setitem -->
+<g id="edge56" class="edge">
+<title>auth_setitem&#45;&gt;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&#45;&gt;auth_setpwd -->
+<g id="edge58" class="edge">
+<title>auth_check_expire&#45;&gt;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&#45;&gt;auth_setpwd -->
+<g id="edge59" class="edge">
+<title>auth_check_change&#45;&gt;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&#45;&gt;auth_clrenv -->
+<g id="edge63" class="edge">
+<title>auth_call&#45;&gt;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&#45;&gt;_auth_spool -->
+<g id="edge61" class="edge">
+<title>auth_call&#45;&gt;_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&#45;&gt;_add_rmlist -->
+<g id="edge62" class="edge">
+<title>auth_call&#45;&gt;_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&#45;&gt;_auth_next_arg -->
+<g id="edge60" class="edge">
+<title>auth_call&#45;&gt;_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&#45;&gt;_recv_fd -->
+<g id="edge64" class="edge">
+<title>_auth_spool&#45;&gt;_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&#45;&gt;login_getcapstr -->
+<g id="edge65" class="edge">
+<title>login_getstyle&#45;&gt;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&#45;&gt;strtolimit -->
+<g id="edge66" class="edge">
+<title>login_getcapsize&#45;&gt;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&#45;&gt;login_getcaptime -->
+<g id="edge67" class="edge">
+<title>gsetrl&#45;&gt;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&#45;&gt;login_getcapnum -->
+<g id="edge69" class="edge">
+<title>gsetrl&#45;&gt;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&#45;&gt;login_getcapsize -->
+<g id="edge68" class="edge">
+<title>gsetrl&#45;&gt;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&#45;&gt;login_getclass -->
+<g id="edge70" class="edge">
+<title>setclasscontext&#45;&gt;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&#45;&gt;login_close -->
+<g id="edge72" class="edge">
+<title>setclasscontext&#45;&gt;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&#45;&gt;setusercontext -->
+<g id="edge71" class="edge">
+<title>setclasscontext&#45;&gt;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&#45;&gt;login_getclass -->
+<g id="edge73" class="edge">
+<title>setusercontext&#45;&gt;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&#45;&gt;login_getcapnum -->
+<g id="edge75" class="edge">
+<title>setusercontext&#45;&gt;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&#45;&gt;login_close -->
+<g id="edge74" class="edge">
+<title>setusercontext&#45;&gt;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&#45;&gt;setuserpath -->
+<g id="edge77" class="edge">
+<title>setusercontext&#45;&gt;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&#45;&gt;setuserenv -->
+<g id="edge76" class="edge">
+<title>setusercontext&#45;&gt;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&#45;&gt;login_setenv -->
+<g id="edge78" class="edge">
+<title>setuserpath&#45;&gt;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&#45;&gt;login_setenv -->
+<g id="edge79" class="edge">
+<title>setuserenv&#45;&gt;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&#45;&gt;expandstr -->
+<g id="edge80" class="edge">
+<title>login_setenv&#45;&gt;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&#45;&gt;strtosize -->
+<g id="edge82" class="edge">
+<title>strtosize&#45;&gt;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&#45;&gt;multiply -->
+<g id="edge81" class="edge">
+<title>strtosize&#45;&gt;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&#45;&gt;strtosize -->
+<g id="edge83" class="edge">
+<title>strtolimit&#45;&gt;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.gif
new file mode 100644
index 0000000..5088082
Binary files /dev/null and b/content/posts/how-bsd-authentication-works/openbsd_internals.gif differ
-- 
cgit v1.2.3