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 @@ - - - - - - -G - - -cluster_authenticate - -authenticate.c - - -cluster_auth_subr - -auth_subr.c - - -cluster_login_cap - -login_cap.c - - - -auth_mkvalue - -auth_mkvalue - - - -auth_checknologin - -auth_checknologin - - - -_auth_checknologin - -_auth_checknologin - - - -auth_checknologin->_auth_checknologin - - - - - -auth_cat - -auth_cat - - - -_auth_checknologin->auth_cat - - - - - -login_getcapstr - -login_getcapstr - - - -_auth_checknologin->login_getcapstr - - - - - -login_getcapbool - -login_getcapbool - - - -_auth_checknologin->login_getcapbool - - - - - -_auth_validuser - -_auth_validuser - - - -auth_approval - -auth_approval - - - -auth_approval->_auth_validuser - - - - - -auth_open - -auth_open - - - -auth_approval->auth_open - - - - - -auth_close - -auth_close - - - -auth_approval->auth_close - - - - - -auth_getitem - -auth_getitem - - - -auth_approval->auth_getitem - - - - - -auth_setitem - -auth_setitem - - - -auth_approval->auth_setitem - - - - - -auth_check_expire - -auth_check_expire - - - -auth_approval->auth_check_expire - - - - - -auth_call - -auth_call - - - -auth_approval->auth_call - - - - - -auth_setstate - -auth_setstate - - - -auth_approval->auth_setstate - - - - - -auth_getstate - -auth_getstate - - - -auth_approval->auth_getstate - - - - - -auth_getpwd - -auth_getpwd - - - -auth_approval->auth_getpwd - - - - - -login_getclass - -login_getclass - - - -auth_approval->login_getclass - - - - - -auth_approval->login_getcapstr - - - - - -auth_approval->login_getcapbool - - - - - -login_close - -login_close - - - -auth_approval->login_close - - - - - -auth_usercheck - -auth_usercheck - - - -auth_usercheck->_auth_validuser - - - - - -auth_verify - -auth_verify - - - -auth_usercheck->auth_verify - - - - - -auth_usercheck->auth_open - - - - - -auth_usercheck->auth_setitem - - - - - -auth_setdata - -auth_setdata - - - -auth_usercheck->auth_setdata - - - - - -auth_usercheck->login_getclass - - - - - -login_getstyle - -login_getstyle - - - -auth_usercheck->login_getstyle - - - - - -auth_usercheck->login_close - - - - - -auth_userokay - -auth_userokay - - - -auth_userokay->auth_usercheck - - - - - -auth_userokay->auth_close - - - - - -auth_userchallenge - -auth_userchallenge - - - -auth_userchallenge->_auth_validuser - - - - - -auth_userchallenge->auth_open - - - - - -auth_userchallenge->auth_close - - - - - -auth_challenge - -auth_challenge - - - -auth_userchallenge->auth_challenge - - - - - -auth_userchallenge->auth_setitem - - - - - -auth_userchallenge->login_getclass - - - - - -auth_userchallenge->login_getstyle - - - - - -auth_userchallenge->login_close - - - - - -auth_userresponse - -auth_userresponse - - - -auth_userresponse->_auth_validuser - - - - - -auth_userresponse->auth_close - - - - - -auth_userresponse->auth_getitem - - - - - -auth_userresponse->auth_setdata - - - - - -auth_userresponse->auth_check_expire - - - - - -auth_userresponse->auth_setstate - - - - - -auth_userresponse->auth_getstate - - - - - -auth_verify->_auth_validuser - - - - - -auth_verify->auth_open - - - - - -auth_verify->auth_getitem - - - - - -auth_verify->auth_setitem - - - - - -auth_verify->auth_call - - - - - -auth_verify->auth_setstate - - - - - -auth_set_va_list - -auth_set_va_list - - - -auth_verify->auth_set_va_list - - - - - -auth_clean - -auth_clean - - - -auth_clrenv - -auth_clrenv - - - -auth_clean->auth_clrenv - - - - - -auth_clean->auth_setitem - - - - - -auth_setenv - -auth_setenv - - - -auth_close->auth_setenv - - - - - -auth_challenge->_auth_validuser - - - - - -auth_getvalue - -auth_getvalue - - - -auth_challenge->auth_getvalue - - - - - -auth_setitem->_auth_validuser - - - - - -auth_setitem->auth_setitem - - - - - -auth_setoption - -auth_setoption - - - -auth_clroptions - -auth_clroptions - - - -auth_clroption - -auth_clroption - - - -auth_setpwd - -auth_setpwd - - - -auth_check_expire->auth_setpwd - - - - - -auth_check_change - -auth_check_change - - - -auth_check_change->auth_setpwd - - - - - -auth_call->auth_clrenv - - - - - -_auth_spool - -_auth_spool - - - -auth_call->_auth_spool - - - - - -_add_rmlist - -_add_rmlist - - - -auth_call->_add_rmlist - - - - - -_auth_next_arg - -_auth_next_arg - - - -auth_call->_auth_next_arg - - - - - -_recv_fd - -_recv_fd - - - -_auth_spool->_recv_fd - - - - - -login_getstyle->login_getcapstr - - - - - -login_getcaptime - -login_getcaptime - - - -login_getcapnum - -login_getcapnum - - - -login_getcapsize - -login_getcapsize - - - -strtolimit - -strtolimit - - - -login_getcapsize->strtolimit - - - - - -gsetrl - -gsetrl - - - -gsetrl->login_getcaptime - - - - - -gsetrl->login_getcapnum - - - - - -gsetrl->login_getcapsize - - - - - -setclasscontext - -setclasscontext - - - -setclasscontext->login_getclass - - - - - -setclasscontext->login_close - - - - - -setusercontext - -setusercontext - - - -setclasscontext->setusercontext - - - - - -setusercontext->login_getclass - - - - - -setusercontext->login_getcapnum - - - - - -setusercontext->login_close - - - - - -setuserpath - -setuserpath - - - -setusercontext->setuserpath - - - - - -setuserenv - -setuserenv - - - -setusercontext->setuserenv - - - - - -login_setenv - -login_setenv - - - -setuserpath->login_setenv - - - - - -setuserenv->login_setenv - - - - - -expandstr - -expandstr - - - -login_setenv->expandstr - - - - - -strtosize - -strtosize - - - -strtosize->strtosize - - - - - -multiply - -multiply - - - -strtosize->multiply - - - - - -strtolimit->strtosize - - - - - -secure_path - -secure_path - - - 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_ -#+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 - #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/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 @@ + + + + + + +G + + +cluster_authenticate + +authenticate.c + + +cluster_auth_subr + +auth_subr.c + + +cluster_login_cap + +login_cap.c + + + +auth_mkvalue + +auth_mkvalue + + + +auth_checknologin + +auth_checknologin + + + +_auth_checknologin + +_auth_checknologin + + + +auth_checknologin->_auth_checknologin + + + + + +auth_cat + +auth_cat + + + +_auth_checknologin->auth_cat + + + + + +login_getcapstr + +login_getcapstr + + + +_auth_checknologin->login_getcapstr + + + + + +login_getcapbool + +login_getcapbool + + + +_auth_checknologin->login_getcapbool + + + + + +_auth_validuser + +_auth_validuser + + + +auth_approval + +auth_approval + + + +auth_approval->_auth_validuser + + + + + +auth_open + +auth_open + + + +auth_approval->auth_open + + + + + +auth_close + +auth_close + + + +auth_approval->auth_close + + + + + +auth_getitem + +auth_getitem + + + +auth_approval->auth_getitem + + + + + +auth_setitem + +auth_setitem + + + +auth_approval->auth_setitem + + + + + +auth_check_expire + +auth_check_expire + + + +auth_approval->auth_check_expire + + + + + +auth_call + +auth_call + + + +auth_approval->auth_call + + + + + +auth_setstate + +auth_setstate + + + +auth_approval->auth_setstate + + + + + +auth_getstate + +auth_getstate + + + +auth_approval->auth_getstate + + + + + +auth_getpwd + +auth_getpwd + + + +auth_approval->auth_getpwd + + + + + +login_getclass + +login_getclass + + + +auth_approval->login_getclass + + + + + +auth_approval->login_getcapstr + + + + + +auth_approval->login_getcapbool + + + + + +login_close + +login_close + + + +auth_approval->login_close + + + + + +auth_usercheck + +auth_usercheck + + + +auth_usercheck->_auth_validuser + + + + + +auth_verify + +auth_verify + + + +auth_usercheck->auth_verify + + + + + +auth_usercheck->auth_open + + + + + +auth_usercheck->auth_setitem + + + + + +auth_setdata + +auth_setdata + + + +auth_usercheck->auth_setdata + + + + + +auth_usercheck->login_getclass + + + + + +login_getstyle + +login_getstyle + + + +auth_usercheck->login_getstyle + + + + + +auth_usercheck->login_close + + + + + +auth_userokay + +auth_userokay + + + +auth_userokay->auth_usercheck + + + + + +auth_userokay->auth_close + + + + + +auth_userchallenge + +auth_userchallenge + + + +auth_userchallenge->_auth_validuser + + + + + +auth_userchallenge->auth_open + + + + + +auth_userchallenge->auth_close + + + + + +auth_challenge + +auth_challenge + + + +auth_userchallenge->auth_challenge + + + + + +auth_userchallenge->auth_setitem + + + + + +auth_userchallenge->login_getclass + + + + + +auth_userchallenge->login_getstyle + + + + + +auth_userchallenge->login_close + + + + + +auth_userresponse + +auth_userresponse + + + +auth_userresponse->_auth_validuser + + + + + +auth_userresponse->auth_close + + + + + +auth_userresponse->auth_getitem + + + + + +auth_userresponse->auth_setdata + + + + + +auth_userresponse->auth_check_expire + + + + + +auth_userresponse->auth_setstate + + + + + +auth_userresponse->auth_getstate + + + + + +auth_verify->_auth_validuser + + + + + +auth_verify->auth_open + + + + + +auth_verify->auth_getitem + + + + + +auth_verify->auth_setitem + + + + + +auth_verify->auth_call + + + + + +auth_verify->auth_setstate + + + + + +auth_set_va_list + +auth_set_va_list + + + +auth_verify->auth_set_va_list + + + + + +auth_clean + +auth_clean + + + +auth_clrenv + +auth_clrenv + + + +auth_clean->auth_clrenv + + + + + +auth_clean->auth_setitem + + + + + +auth_setenv + +auth_setenv + + + +auth_close->auth_setenv + + + + + +auth_challenge->_auth_validuser + + + + + +auth_getvalue + +auth_getvalue + + + +auth_challenge->auth_getvalue + + + + + +auth_setitem->_auth_validuser + + + + + +auth_setitem->auth_setitem + + + + + +auth_setoption + +auth_setoption + + + +auth_clroptions + +auth_clroptions + + + +auth_clroption + +auth_clroption + + + +auth_setpwd + +auth_setpwd + + + +auth_check_expire->auth_setpwd + + + + + +auth_check_change + +auth_check_change + + + +auth_check_change->auth_setpwd + + + + + +auth_call->auth_clrenv + + + + + +_auth_spool + +_auth_spool + + + +auth_call->_auth_spool + + + + + +_add_rmlist + +_add_rmlist + + + +auth_call->_add_rmlist + + + + + +_auth_next_arg + +_auth_next_arg + + + +auth_call->_auth_next_arg + + + + + +_recv_fd + +_recv_fd + + + +_auth_spool->_recv_fd + + + + + +login_getstyle->login_getcapstr + + + + + +login_getcaptime + +login_getcaptime + + + +login_getcapnum + +login_getcapnum + + + +login_getcapsize + +login_getcapsize + + + +strtolimit + +strtolimit + + + +login_getcapsize->strtolimit + + + + + +gsetrl + +gsetrl + + + +gsetrl->login_getcaptime + + + + + +gsetrl->login_getcapnum + + + + + +gsetrl->login_getcapsize + + + + + +setclasscontext + +setclasscontext + + + +setclasscontext->login_getclass + + + + + +setclasscontext->login_close + + + + + +setusercontext + +setusercontext + + + +setclasscontext->setusercontext + + + + + +setusercontext->login_getclass + + + + + +setusercontext->login_getcapnum + + + + + +setusercontext->login_close + + + + + +setuserpath + +setuserpath + + + +setusercontext->setuserpath + + + + + +setuserenv + +setuserenv + + + +setusercontext->setuserenv + + + + + +login_setenv + +login_setenv + + + +setuserpath->login_setenv + + + + + +setuserenv->login_setenv + + + + + +expandstr + +expandstr + + + +login_setenv->expandstr + + + + + +strtosize + +strtosize + + + +strtosize->strtosize + + + + + +multiply + +multiply + + + +strtosize->multiply + + + + + +strtolimit->strtosize + + + + + +secure_path + +secure_path + + + 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