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
---
.../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
6 files changed, 4001 insertions(+)
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
(limited to 'content/posts/how-bsd-authentication-works')
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 @@
+
+
+
+
+
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_
+#+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
+ #include
+ #include
+ #include
+
+ 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
+
+ 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_- =, but in reality it is =AUTHV_
- =.
+
+ # 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