summaryrefslogtreecommitdiffstats
path: root/content/posts/how-bsd-authentication-works
diff options
context:
space:
mode:
authorDante Catalfamo2020-07-07 22:28:28 -0400
committerDante Catalfamo2020-07-07 22:28:28 -0400
commit118b0019658890837ab8e19608ce0779cb6f4d9a (patch)
treef1146c980182a3fa19dcc37553a2932583e193f3 /content/posts/how-bsd-authentication-works
parenta3bf350943b99c475a281b00a8354654fe4b26f9 (diff)
downloadblog-118b0019658890837ab8e19608ce0779cb6f4d9a.tar.gz
blog-118b0019658890837ab8e19608ce0779cb6f4d9a.tar.bz2
blog-118b0019658890837ab8e19608ce0779cb6f4d9a.zip
Move WIP posts to WIP folders
Diffstat (limited to 'content/posts/how-bsd-authentication-works')
-rw-r--r--content/posts/how-bsd-authentication-works/graph.dot67
-rw-r--r--content/posts/how-bsd-authentication-works/index.org473
2 files changed, 0 insertions, 540 deletions
diff --git a/content/posts/how-bsd-authentication-works/graph.dot b/content/posts/how-bsd-authentication-works/graph.dot
deleted file mode 100644
index 6414b11..0000000
--- a/content/posts/how-bsd-authentication-works/graph.dot
+++ /dev/null
@@ -1,67 +0,0 @@
-digraph G {
- subgraph cluster_authenticate {
- label = "authenticate.c"
- auth_userokay;
- auth_usercheck;
- auth_verify;
- }
-
- subgraph cluster_auth_subr {
- label = "auth_subr.c"
- auth_open;
- auth_call;
- auth_close;
- // auth_setitem;
- // auth_setdata;
- // auth_setopts;
- auth_set[label="auth_set*"];
- auth_setstate;
- // _auth_spool;
- }
-
- subgraph cluster_login_cap {
- label = "libc/login_cap.c"
- login_getclass
- login_getstyle
- }
-
- subgraph cluster_getpwent {
- label = "libc/getpwent.c"
- getpwnam_r;
- }
-
- subgraph cluster_exec {
- login[label="login_*"];
- execve;
- }
-
-
- start -> auth_userokay;
- auth_userokay -> auth_usercheck;
- auth_usercheck -> getpwnam_r;
- auth_usercheck -> login_getclass;
- auth_usercheck -> login_getstyle;
- // if password given
- auth_usercheck -> auth_open;
- // auth_usercheck -> auth_setitem;
- // auth_usercheck -> auth_setdata;
- auth_usercheck -> auth_set;
- // fi
- auth_usercheck -> auth_verify;
-
- auth_verify -> auth_setstate;
- auth_verify -> auth_call;
-
- auth_call -> execve[label="fork()"];
- // auth_call -> _auth_spool;
-
- execve -> login;
- login -> auth_call[label="back channel"];
- // login -> _auth_spool[label="back channel"];
-
-
- // auth_usercheck -> { auth_setitem auth_setdata auth_setopts }
-
- // auth_call -> auth_userokay;
- auth_userokay -> auth_close;
-}
diff --git a/content/posts/how-bsd-authentication-works/index.org b/content/posts/how-bsd-authentication-works/index.org
deleted file mode 100644
index 4126284..0000000
--- a/content/posts/how-bsd-authentication-works/index.org
+++ /dev/null
@@ -1,473 +0,0 @@
-#+TITLE: How BSD Authentication Works
-#+DATE: 2020-06-26T18:31:36-04:00
-#+DRAFT: true
-#+DESCRIPTION:
-#+TAGS[]: openbsd
-#+KEYWORDS[]: openbsd
-#+SLUG:
-#+SUMMARY:
-#+SHOWTOC: true
-
-[[https://web.archive.org/web/20170327150148/http://www.penzin.net/bsdauth/]]
-* History
-
- OpenBSD is quite different from many other Unix-like operating systems
- in many ways, but one way which I find interesting is the
- authentication system. Most systems from AIX, Solaris, and Linux to
- most BSDs including MacOS use some form of a system called Pluggable
- Authentication Module (PAM). The two main implementations of PAM are
- [[http://www.linux-pam.org/][Linux PAM]] and [[https://www.openpam.org/][OpenPAM]]. PAM modules are created a dynamically loaded
- shared objects, which communicate using a set of standard
- 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]]). PAM is 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]].
-
- OpenBSD on the other hand uses a mechanism called BSD
- Authentication. It was originally developed for a 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 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 (=PF_LOCAL, SOCK_STREAM=,
- specifically). The program or script 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)=]].
-
-* Why
-
- This one is pretty difficult, since there seems to be very little
- information about how BSD Auth works apart from the source code
- itself. This is my best attempt to understand the flow of BSD Auth
- from what I've read.
-
-* BSD Auth Modules
-
- These programs or scripts are located in =/usr/libexec/auth/= with the
- naming convention =login_<style>=. They take arguments in the form of
-
- #+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
- usually the right choice. Read the style's man page for details.
- - =-v key=value= is an optional argument. There is no limit to the
- number of =-v= arguments. 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 class of the user to be
- authenticated.
-
- =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 file descriptor 3.
-
-* Documentation
-
- 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)=]].
-
-* auth_userokay
-
- The highest level function, and easiest to use is =auth_userokay=. It
- takes four character arrays as arguments, =name=, =style=, =type=, and
- =password=. It returns either a =0= for failure, of a non-zero value
- for success.
-
- This function lives inside =/lib/libc/gen/authenticate.c=
-
- #+BEGIN_SRC c
- int auth_userokay(char *name, char *style, char *type, char *password);
- #+END_SRC
-
- - =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. By default this is =passwd= on normal accounts.
- - The style can be one of the installed authentication methods, like
- =passwd=, =radius=, =skey=, =yubikey=, etc.
- - There's more information about available styles in =login.conf(5)=
- - Styles can also be installed through BSD Auth module packages
- - =type= is the authentication type
- - Types are defined in =login.conf= and define 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.
- - There's more information about how to add methods in =login.conf(5)=
- - =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 non-interactively tested
-
- =auth_userokay= is just a wrapper around =auth_usercheck=, which
- returns a finished auth session of type =auth_session_t=. It closes
- the auth session using =auth_close= and returns the value returned
- from closing.
-
-* auth_session_t
-
- #+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 =authdata=, =authopts=, and =rmfiles= are defined as
-
- #+BEGIN_SRC c
- 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
-
-** auth_setdata
-
-** auth_setitem
-
-** auth_setoption
-
-** auth_setstate
-* auth_open
-
- The =auth_open= function is used by several functions to create a
- new auth session. It allocates an =auth_session_t= struct on the
- heap, sets its default =service= to =login=, and it's =fd= to =-1=,
- and returns the pointer.
-
-* auth_usercheck
-
- #+BEGIN_SRC c
- auth_session_t *auth_usercheck(char *name, char *style, char *type, char *password)
- #+END_SRC
-
- =auth_usercheck= checks the user name against the passwd db. It also
- checks the login class against the =login.conf= db, along with
- confirming the login styles available.
-
- If the password is non-=NULL=, then it creates a new session using
- =auth_open=. With the new session, =auth_usercheck= calls (with =as=
- as the session struct)
-
- #+BEGIN_SRC c
- auth_setitem(as, AUTHV_SERVICE, "response");
- auth_setdata(as, "", 1);
- auth_setdata(as, password, strlen(password) + 1);
- #+END_SRC
-
- setting the service protocol to =response=, adding an empty line to
- the session data, then adding the password as data. If the password is
- =NULL=, it sets the =auth_session_t= pointer to =NULL=. It then passes
- the user name, style, login class, and =NULL= char pointer to
- =auth_verify=. The last two variables are received as variable
- arguments. It then returns the auth session pointer the call
- returns.
-
-* auth_verify
-
- #+BEGIN_SRC c
- auth_session_t *auth_verify(auth_session_t *as, char *style, char *name, ...)
- #+END_SRC
-
- =auth_verify= creates an auth session using =auth_open= if =as= is
- =NULL=. It then sets the user name and style of the session, if the
- respective arguments are non-=NULL=. It then copies its variable
- arguments to the auth session's =va_list ap=, which is used inside
- of =auth_call=.
-
- After that it constructs the path of the authentication module 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=.
-
- Then =auth_call= is called with the struct, the path to the auth
- module, the auth style, the "-s" flag followed by the service (login,
- challenge, 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
- auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s",
- auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL);
- #+END_SRC
-
-* auth_call
-
- #+BEGIN_SRC c
- int auth_call(auth_session_t *as, char *path, ...)
- #+END_SRC
-
- <<here>>
-
- ---
- note: In the man page auth_subr it says
- #+begin_quote
- 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_quote
- However I don't see this enforced anywhere, I even wrote a small test
- script to prove that's the case on =vfwall ~/authtest=.
-
- The manpage also says the path is limited to =/bin/= and =/usr/bin=,
- which is also not the case.
-
- Ask jcs about the file descriptor situation, I don't understand it
- after reading both the man page and source.
- ---
-
- Inside of =auth_call=, 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 forks,
- calling ~execve(path, argv, auth_environ)~, where the =argv= is
- everything after =path= in the =auth_call= arguments. Any =authopts=
- set in the auth session are also passed as arguments in the format =-v
- opt1 -v opt2 -v opt3=, etc. =auth_environ= is defined at the top of
- the file as
-
- #+BEGIN_SRC c
- static char *auth_environ[] = {
- "PATH=" _PATH_DEFPATH,
- "SHELL=" _PATH_BSHELL,
- NULL,
- };
- #+END_SRC
-
- Where both constants are defined in =paths.h= as
-
- #+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
-
-
- The =exec='d process then listens on FD 3, which is one half of the
- =sockpair= that was created earlier.
-
- In the non-exec'd process, first the contents of the auth session's
- =*data= are read in one at a time.
-
- The data received through the back channel is then put into the
- =spool= of the auth session using =_auth_spool(as, pfd[0])=. After
- that the spooled data 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
-
-
- It is looking for lines that start with either =BI_AUTH=
- (=authorize=), or =BI_REJECT= (=reject=). If the line is still longer,
- it continues to scan for any other qualifiers such as =pwexpired= or
- =silent=. The struct's =state= is set to one using the =AUTH_= values
- from =login_cap.h= accordingly.
-
- #+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
-
-** _auth_spool
-
-** _recv_fd
-
-* auth_close
- =auth_close= is the function responsible for cleaning up the session
- and taking care of the values returned though the back channel.
-
- It first sets the environment variables returned through the back
- channel by passing the auth session to =auth_setenv=. It then goes
- through the =rmlist= of the session, deleting the files if the
- session reported a failure. It then zeroes out all sensitive
- information, and frees the various structs associated with the current
- =auth_session_t=, and then the session itself. Finally it returns
- the session's state =&='ed with =AUTH_ALLOW=.
-* grapgh?
-# Setting env on auth_close(as)
-# partual rewrite below
-
-The call graph for =auth_userokay= looks something like this:
-
-#+BEGIN_SRC c
-int auth_userokay(char *name, char *style, char *type, char *password)
-#+END_SRC
-
-calls ~auth_usercheck~ and then calls ~auth_close~ on the returned
-~auth_session_t~. The value returned from ~auth_close~ is then
-returned.
-
-#+BEGIN_SRC c
-auth_session_t *auth_usercheck(char *name, char *style, char *type, char *password)
-#+END_SRC
-
-Validates the checks that the user exists, gets the user's login
-class, verifies the auth type, and that the auth style can be used.
-
-It creates an auth session struct.
-
-If the password is provided it sets the service type to =response=,
-and adds the adds the password to the auth data. Otherwise it
-leaves it empty.