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 @@ + + + + + + +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