diff options
author | Dante Catalfamo | 2021-10-18 17:36:47 -0400 |
---|---|---|
committer | Dante Catalfamo | 2021-10-18 17:36:47 -0400 |
commit | 894afa96cd14a84cd1a1bcfb9523f10210aebb7c (patch) | |
tree | f8e30e801d3dec12c23933883bbf75765502b755 /content/posts/WIP-how-bsd-authentication-works/index.org | |
parent | 230c8996ec91714db52a8593e8dac24939955438 (diff) | |
download | blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.tar.gz blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.tar.bz2 blog-894afa96cd14a84cd1a1bcfb9523f10210aebb7c.zip |
bsd-auth: no longer WIP
Diffstat (limited to 'content/posts/WIP-how-bsd-authentication-works/index.org')
-rw-r--r-- | content/posts/WIP-how-bsd-authentication-works/index.org | 2811 |
1 files changed, 0 insertions, 2811 deletions
diff --git a/content/posts/WIP-how-bsd-authentication-works/index.org b/content/posts/WIP-how-bsd-authentication-works/index.org deleted file mode 100644 index f0623c2..0000000 --- a/content/posts/WIP-how-bsd-authentication-works/index.org +++ /dev/null @@ -1,2811 +0,0 @@ -#+TITLE: How BSD Authentication Works -#+DATE: 2021-10-18T17:27:13-04:00 -#+DRAFT: true -#+SHOWTOC: true -#+DESCRIPTION: A walkthrough of how OpenBSD's BSD Auth framework functions -#+TAGS[]: openbsd security -#+KEYWORDS[]: openbsd security -#+SLUG: -#+SUMMARY: - -#+ATTR_HTML: :title OpenBSD Internals -#+ATTR_HTML: :alt OpenBSD mascot cutaway view with spinning gears inside -[[file:openbsd_internals.gif]] - -* History - :PROPERTIES: - :CUSTOM_ID: history - :END: - - The way OpenBSD authenticates users is quite different from other - Unix-like operating systems. Most other systems like AIX, Solaris, - Linux, the other BSDs, and MacOS, use a framework called [[https://en.wikipedia.org/wiki/Pluggable_authentication_module][Pluggable - Authentication Module]] (PAM). The two main implementations are [[http://www.linux-pam.org/][Linux - PAM]] and [[https://www.openpam.org/][OpenPAM]]. PAM modules are created as dynamically loaded - shared objects, which communicate using a combination of common and - implementation specific interfaces ([[https://linux.die.net/man/3/pam][Linux-PAM]] and [[https://www.freebsd.org/cgi/man.cgi?query=pam&apropos=0&sektion=3&manpath=FreeBSD+12.1-RELEASE+and+Ports&arch=default&format=html][OpenPAM]]). It's - configured using the [[https://linux.die.net/man/5/pam.d][pam.d]] directory and [[https://www.freebsd.org/cgi/man.cgi?query=pam.conf&sektion=5&apropos=0&manpath=FreeBSD+12.1-RELEASE+and+Ports][pam.conf]] file. While it can - be flexible, it's highly complex and very easy to mis-configure, - leaving you open to strange and hard to track down authentication - bugs. On top of that, the fact that it's a shared library means that - any vulnerability in a poorly vetted authentication module gives - attackers direct access to the internals of your application. Author - Michael W. Lucas said it best when he described PAM as - [[https://www.youtube.com/watch?v=-CXp3byvI1g][unstandardized black magic]]. - - OpenBSD on the other hand uses a mechanism called BSD - Authentication. It was originally developed for a now-defunct - proprietary operating system called [[https://en.wikipedia.org/wiki/BSD/OS][BSD/OS]] by [[https://en.wikipedia.org/wiki/Berkeley_Software_Design][Berkeley Software - Design Inc.]], who later donated the system. It was then adopted by - OpenBSD in release 2.9. BSD Auth is comparatively much simpler than - PAM. Modules or, authentication "styles", are instead stand alone - applications or scripts that communicate over IPC. The module has no - ability to interfere with the parent and can very easily revoke - permissions using [[https://man.openbsd.org/pledge][=pledge(2)=]] or [[https://man.openbsd.org/unveil][=unveil(2)=]]. The BSD Authentication - system of configured through [[https://man.openbsd.org/login.conf][=login.conf(5)=]]. - -* Documentation - :PROPERTIES: - :CUSTOM_ID: documentation - :END: - - All of the high level authentication functions are described in - [[https://man.openbsd.org/authenticate][=authenticate(3)=]], with the lower level functions being described in - [[https://man.openbsd.org/auth_subr][=auth_subr(3)=]]. - - Click on any function prototype in this post to see its definition. - - I've also created a [[#graph][graph]] at the bottom of the post to help - visualize the function calls. - - All code snippets from this blog post belong to the OpenBSD - contributors. Please see the [[#copyright][Copyright]] section for details. - -* BSD Auth Modules - :PROPERTIES: - :CUSTOM_ID: modules - :END: - - Modules are located in =/usr/libexec/auth/= with the naming - convention =login_<style>=. They accept arguments in the following - form. - - #+BEGIN_SRC shell - login_<style> [-s service] [-v key=value] user [class] - #+END_SRC - - - =<style>= is the authentication method. This could be =passwd=, - =radius=, =skey=, =yubikey=, etc. There's more information about - available styles in [[https://man.openbsd.org/login.conf][=login.conf(5)=]] under the [[https://man.openbsd.org/login.conf#AUTHENTICATION][=AUTHENTICATION=]] - header. - - =service= is the service type. Typically authentication methods - will accept one of three values here: =login=, =challenge=, or - =response=. =login= is the default if it's not specified, and is - used to let the module know to interact with the user directly - through =stdin= and =stdout=, while =challenge= and =response= are - used to pass messages back and forth through the BSD Auth API. - Each style's man page will have more details on these. - - =-v key=value= is an optional argument. There can be more than one - arguments in this style. This is used to pass extra data to the - program under certain circumstances. - - =user= is the name of the user to be authenticated. - - =class= is optional and specifies the login class to use for the - user. - - =login= and =su= pass in extra data as =-v= flags. - - #+CAPTION: Taken from [[https://man.openbsd.org/login.conf][=login.conf(5)=]] - #+BEGIN_SRC - The login(1) program provides the following through the -v option: - - auth_type The type of authentication to use. - - fqdn The hostname provided to login by the -h option. - - hostname The name login(1) will place in the utmp file for the - remote hostname. - - local_addr The local IP address given to login(1) by the -L option. - - lastchance Set to "yes" when a user's password has expired but the - user is being given one last chance to login and update - the password. - - login This is a new login session (as opposed to a simple - identity check). - - remote_addr The remote IP address given to login(1) by the -R option. - - style The style of authentication used for this user (see - approval scripts below). - - The su(1) program provides the following through the -v option: - - wheel Set to either "yes" or "no" to indicate if the user is in - group wheel when they are trying to become root. Some - authentication types require the user to be in group - wheel when using the su(1) program to become super user. - #+END_SRC - - The auth module communicates with its caller through what's called - the "back channel" on file descriptor 3. This communication is - covered in greater detail in the [[#auth_call][=auth_call=]] section. - - Some modules require an extra file descriptor to be passed in for - stateful challenge/response authentication. In these cases, an extra - =-v fd=4= argument will be passed. Theoretically this =fd= can be - any number, but in practice =fd=4= is hard-coded. - - - Most modules also have a hidden flag =-d=, which sets the back - channel do =stdio=, presumably for debugging purposes. - - The simplest way to authenticate a user with BSD Auth is by using - [[#auth_userokay][=auth_userokay=]]. - - For cases where challenge / response authentication is required and - the user can't interacting through =stdin= and =stdout=, - [[#auth_userchallenge][=auth_userchallenge=]] and [[#auth_userresponse][=auth_userresponse=]] can be used. - -* Approval Scripts - :PROPERTIES: - :CUSTOM_ID: approval - :END: - - Approval scripts can be much simpler than the full login modules - used by the other functions. They are given the same back channel as - auth modules, but should not explicitly authenticate or revoke - users. They should exit with a zero status for approval, or non-zero - status to signal disapproval. - - Approval scrips receive arguments in the following form. - #+begin_src shell - approve [-v name=value] username class service - #+end_src - - It can also receive extra key-value =-v= arguments in the same format as - [[#modules][auth modules]]. More information is available in the [[https://man.openbsd.org/login.conf#APPROVAL][=APPROVAL=]] - section of the =login.conf= man page. - - Approval scripts are run using [[#auth_approval][=auth_approval=]]. - -* auth_userokay - :PROPERTIES: - :CUSTOM_ID: auth_userokay - :END: - [[https://man.openbsd.org/authenticate.3#auth_userokay][=auth_userokay=]] is the highest level function, and easiest to use. - It takes four strings as arguments: =name=, =style=, =type=, and - =password=. It returns either a =0= for failure, of a non-zero value - for success. - - @@html: <details> <summary> @@ - #+BEGIN_SRC c - int auth_userokay(char *name, char *style, char *type, char *password); - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - auth_session_t *as; - - as = auth_usercheck(name, style, type, password); - - return (as != NULL ? auth_close(as) : 0); - } - #+end_src - @@html: </details> @@ - - - =name= is the name of the user to be authenticated - - =style= is the login method to be used - - If =style= is =NULL=, the user's default login style will be - used. This is =passwd= on normal accounts. - - The style can be one of the installed authentication methods, like - =passwd=, =radius=, =skey=, =yubikey=, etc. - - Styles can also be installed through BSD Auth module packages - - =type= is the authentication type - - Types are defined in =login.conf= and as a group of allowed auth - styles - - If =type= is =NULL=, use the auth type for the user's login - class. The default type is =auth-default=, which allows - =psaswd= and =skey= auth methods. - - =password= is the password to test - - If =password= is =NULL=, then the user is interactively - prompted. This is required for auth styles using - challenge-response methods. - - If =password= is specified, then it's passed to the auth module - as a =response= - - =auth_userokay= is just a wrapper around [[#auth_usercheck][=auth_usercheck=]] that takes - care of closing the session using [[#auth_close][=auth_close=]], and returning the - resulting value. - -* auth_session_t - :PROPERTIES: - :CUSTOM_ID: auth_session_t - :END: - - =auth_session_t= is the main data structure used to represent the - authentication session. - - #+BEGIN_SRC c - struct auth_session_t { - char *name; /* name of use being authenticated */ - char *style; /* style of authentication used */ - char *class; /* class of user */ - char *service; /* type of service being performed */ - char *challenge; /* last challenge issued */ - int flags; /* see below */ - struct passwd *pwd; /* password entry for user */ - struct timeval now; /* time of authentication */ - - int state; /* authenticated state */ - - struct rmfiles *rmlist; /* list of files to remove on failure */ - struct authopts *optlist; /* list of options to scripts */ - struct authdata *data; /* additional data to send to scripts */ - - char spool[MAXSPOOLSIZE]; /* data returned from login script */ - int index; /* how much returned thus far */ - - int fd; /* connection to authenticator */ - - va_list ap0; /* argument list to auth_call */ - va_list ap; /* additional arguments to auth_call */ - }; - #+END_SRC - - Where =MAXSPOOLSIZE=, =authdata=, =authopts=, and =rmfiles= are defined as - - #+BEGIN_SRC c - #define MAXSPOOLSIZE (8*1024) /* Spool up to 8K of back info */ - - struct rmfiles { - struct rmfiles *next; - char *file; - }; - - struct authopts { - struct authopts *next; - char *opt; - }; - - struct authdata { - struct authdata *next; - void *ptr; - size_t len; - }; - #+END_SRC - - There are several functions which get used to operate on - =auth_session_t= to keep it opaque. -** auth_setdata - :PROPERTIES: - :CUSTOM_ID: auth_setdata - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int auth_setdata(auth_session_t *as, void *ptr, size_t len) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct authdata *data, *dp; - - if (len <= 0) - return (0); - - if ((data = malloc(sizeof(*data) + len)) == NULL) - return (-1); - - data->next = NULL; - data->len = len; - data->ptr = data + 1; - memcpy(data->ptr, ptr, len); - - if (as->data == NULL) - as->data = data; - else { - for (dp = as->data; dp->next != NULL; dp = dp->next) - ; - dp->next = data; - } - return (0); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setdata~2][=auth_setdata=]] allocates and initializes a new =authdata= struct, - storing a copy of the data from =ptr= and =len=. It then point the - =next= field on the last =authdata= struct in =as= to its - location. It returns =0= on success. - -** auth_setitem / auth_getitem - :PROPERTIES: - :CUSTOM_ID: auth_setitem - :END: - @@html: <details> <summary> @@ - #+begin_src c - int auth_setitem(auth_session_t *as, auth_item_t item, char *value) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - if (as == NULL) { - errno = EINVAL; - return (-1); - } - - switch (item) { - case AUTHV_ALL: - if (value != NULL) { - errno = EINVAL; - return (-1); - } - auth_setitem(as, AUTHV_CHALLENGE, NULL); - auth_setitem(as, AUTHV_CLASS, NULL); - auth_setitem(as, AUTHV_NAME, NULL); - auth_setitem(as, AUTHV_SERVICE, NULL); - auth_setitem(as, AUTHV_STYLE, NULL); - auth_setitem(as, AUTHV_INTERACTIVE, NULL); - return (0); - - case AUTHV_CHALLENGE: - if (value == as->challenge) - return (0); - if (value != NULL && (value = strdup(value)) == NULL) - return (-1); - free(as->challenge); - as->challenge = value; - return (0); - - case AUTHV_CLASS: - if (value == as->class) - return (0); - if (value != NULL && (value = strdup(value)) == NULL) - return (-1); - free(as->class); - as->class = value; - return (0); - - case AUTHV_NAME: - if (value == as->name) - return (0); - if (value != NULL && !_auth_validuser(value)) { - errno = EINVAL; - return (-1); - } - if (value != NULL && (value = strdup(value)) == NULL) - return (-1); - free(as->name); - as->name = value; - return (0); - - case AUTHV_SERVICE: - if (value == as->service) - return (0); - if (value == NULL || strcmp(value, defservice) == 0) - value = defservice; - else if ((value = strdup(value)) == NULL) - return (-1); - if (as->service && as->service != defservice) - free(as->service); - as->service = value; - return (0); - - case AUTHV_STYLE: - if (value == as->style) - return (0); - if (value == NULL || strchr(value, '/') != NULL || - (value = strdup(value)) == NULL) - return (-1); - free(as->style); - as->style = value; - return (0); - - case AUTHV_INTERACTIVE: - if (value == NULL) - as->flags &= ~AF_INTERACTIVE; - else - as->flags |= ~AF_INTERACTIVE; - return (0); - - default: - errno = EINVAL; - return (-1); - } - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setitem][=auth_setitem=]] is used to set one of several different fields of - =as= to =value=. Depending on the value of =item= ([[#auth_item_t][=auth_item_t=]]), it - can be the =challenge=, =class=, =name=, =service=, =style=, or - =interactive= field. If =value= is =NULL=, it clears that field. If - =item= is =AUTHV_ALL= and =value= is =NULL=, all fields are - cleared. It returns =0= on success. - - *Note*: As of writing, the man page displays the incorrect name for - the constants. - - #+CAPTION: Taken from [[https://man.openbsd.org/auth_subr.3#auth_getitem][=auth_subr(3)=]] - #+begin_src text - AUTH_CHALLENGE - The latest challenge, if any, set for the session. - - AUTH_CLASS - The class of the user, as defined by the /etc/login.conf file. - This value is not directly used by BSD Authentication, rather, it - is passed to the login scripts for their possible use. - - AUTH_INTERACTIVE - If set to any value, then the session is tagged as interactive. If - not set, the session is not interactive. When the value is - requested it is always either NULL or “True”. The auth subroutines - may choose to provide additional information to standard output or - standard error when the session is interactive. There is no - functional change in the operation of the subroutines. - - AUTH_NAME - The name of the user being authenticated. The name should include - the instance, if any, that is being requested. - - AUTH_SERVICE - The service requesting the authentication. Initially it is set to - the default service which provides the traditional interactive - service. - - AUTH_STYLE - The style of authentication being performed, as defined by the - /etc/login.conf file. The style determines which login script - should actually be used. - #+end_src - - - @@html: <details> <summary> @@ - #+begin_src c - char *auth_getitem(auth_session_t *as, auth_item_t item) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - if (as != NULL) { - switch (item) { - case AUTHV_CHALLENGE: - return (as->challenge); - case AUTHV_CLASS: - return (as->class); - case AUTHV_NAME: - return (as->name); - case AUTHV_SERVICE: - return (as->service ? as->service : defservice); - case AUTHV_STYLE: - return (as->style); - case AUTHV_INTERACTIVE: - return ((as->flags & AF_INTERACTIVE) ? "True" : NULL); - default: - break; - } - } - return (NULL); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_getitem][=auth_getitem=]] is used to return the value of the fields listed above. - -*** auth_item_t - :PROPERTIES: - :CUSTOM_ID: auth_item_t - :END: - - =auth_item_t= is an enum defined in =/include/bsd_auth.h=. - - #+begin_src c - typedef enum { - AUTHV_ALL, - AUTHV_CHALLENGE, - AUTHV_CLASS, - AUTHV_NAME, - AUTHV_SERVICE, - AUTHV_STYLE, - AUTHV_INTERACTIVE - } auth_item_t; - #+end_src - -** auth_setoption - :PROPERTIES: - :CUSTOM_ID: auth_setoption - :END: - @@html: <details> <summary> @@ - #+begin_src c - int auth_setoption(auth_session_t *as, char *n, char *v) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct authopts *opt; - size_t len = strlen(n) + strlen(v) + 2; - int ret; - - if ((opt = malloc(sizeof(*opt) + len)) == NULL) - return (-1); - - opt->opt = (char *)(opt + 1); - - ret = snprintf(opt->opt, len, "%s=%s", n, v); - if (ret < 0 || ret >= len) { - free(opt); - errno = ENAMETOOLONG; - return (-1); - } - opt->next = as->optlist; - as->optlist = opt; - return(0); - } - - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setoption][=auth_setoption=]] initializes a new =authopts= struct, and sets the - =opt= field to a string formatted as =sprintf("%s=%s", n, v)=. It - then point the =next= field on the last =authopts= struct in =as= - to its location. It returns =0= on success. - -** auth_setstate / auth_getstate - :PROPERTIES: - :CUSTOM_ID: auth_setstate - :END: - @@html: <details> <summary> @@ - #+begin_src c - void auth_setstate(auth_session_t *as, int s) - #+end_src - @@html: </summary> @@ - #+begin_src c - { as->state = s; } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setstate][=auth_setstate=]] sets the =state= of =as= to =s=. - - @@html: <details> <summary> @@ - #+begin_src c - int auth_getstate(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { return (as->state); } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_getstate][=auth_getstate=]] return the =state= of =as=. - -** auth_setpwd / auth_getpwd - :PROPERTIES: - :CUSTOM_ID: auth_setpwd - :END: - @@html: <details> <summary> @@ - #+begin_src c - int auth_setpwd(auth_session_t *as, struct passwd *pwd) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct passwd pwstore; - char *instance, pwbuf[_PW_BUF_LEN]; - - if (pwd == NULL && as->pwd == NULL && as->name == NULL) - return (-1); /* true failure */ - - if (pwd == NULL) { - /* - * If we were not passed in a pwd structure we need to - * go find one for ourself. Always look up the username - * (if it is defined) in the passwd database to see if there - * is an entry for the user. If not, either use the current - * entry or simply return a 1 which implies there is - * no user by that name here. This is not a failure, just - * a point of information. - */ - if (as->name == NULL) - return (0); - getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - if (pwd == NULL) { - instance = strchr(as->name, '/'); - if (instance == NULL) - return (as->pwd ? 0 : 1); - if (strcmp(instance, "/root") == 0) { - getpwnam_r(instance + 1, &pwstore, pwbuf, - sizeof(pwbuf), &pwd); - } - if (pwd == NULL) - return (as->pwd ? 0 : 1); - } - } - if ((pwd = pw_dup(pwd)) == NULL) - return (-1); /* true failure */ - if (as->pwd) { - explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); - free(as->pwd); - } - as->pwd = pwd; - return (0); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setpwd][=auth_setpwd=]] is used to retrieve and set the [[https://man.openbsd.org/man3/getpwnam.3][password database]] - entry in =as= if one isn't already set. - - If a passwd entry is passed in through =pwd=, it uses that to set - =as->pwd=. If =pwd= is =NULL=, it tries to find the passwd entry - associated with =as->name=. If it finds one, it sets =as->pwd= and - returns =0=. If there is no entry with that username, it returns - =1=. - - @@html: <details> <summary> @@ - #+begin_src c - struct passwd *auth_getpwd(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { return (as->pwd); } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_getpwd][=auth_getpwd=]] returns =as->pwd=. - -** auth_set_va_list - :PROPERTIES: - :CUSTOM_ID: auth_set_va_list - :END: - - @@html: <details> <summary> @@ - #+begin_src c - void auth_set_va_list(auth_session_t *as, va_list ap) - #+end_src - @@html: </summary> @@ - #+begin_src c - { va_copy(as->ap, ap); } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_set_va_list][=auth_set_va_list=]] copies =ap= to =as->ap=. - -** auth_clrenv - :PROPERTIES: - :CUSTOM_ID: auth_clrenv - :END: - @@html: <details> <summary> @@ - #+begin_src c - void auth_clrenv(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - char *line; - - for (line = as->spool; line < as->spool + as->index;) { - if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { - if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { - line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; - } - } else - if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { - if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { - line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; - } - } - while (*line++) - ; - } - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_clrenv][=auth_clrenv=]] removes all lines containing =BI_SETENV= and - =BI_UNSETENV= from =as->spool=. This is explained under the - [[#auth_call][=auth_call=]] section. - -** auth_clroption - :PROPERTIES: - :CUSTOM_ID: auth_clroption - :END: - - @@html: <details> <summary> @@ - #+begin_src c - void auth_clroption(auth_session_t *as, char *option) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct authopts *opt, *oopt; - size_t len; - - len = strlen(option); - - if ((opt = as->optlist) == NULL) - return; - - if (strncmp(opt->opt, option, len) == 0 && - (opt->opt[len] == '=' || opt->opt[len] == '\0')) { - as->optlist = opt->next; - free(opt); - return; - } - - while ((oopt = opt->next) != NULL) { - if (strncmp(oopt->opt, option, len) == 0 && - (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { - opt->next = oopt->next; - free(oopt); - return; - } - opt = oopt; - } - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_clroption][=auth_clroption=]] removes the option named =option= from =as=. - -** auth_clroptions - :PROPERTIES: - :CUSTOM_ID: auth_clroptions - :END: - - @@html: <details> <summary> @@ - #+begin_src c - void auth_clroptions(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct authopts *opt; - - while ((opt = as->optlist) != NULL) { - as->optlist = opt->next; - free(opt); - } - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_clroptions][=auth_clroptions=]] clears all options from =as=. - -** auth_setenv - :PROPERTIES: - :CUSTOM_ID: auth_setenv - :END: - @@html: <details> <summary> @@ - #+begin_src c - void auth_setenv(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - char *line, *name; - - /* - ,* Set any environment variables we were asked for - ,*/ - for (line = as->spool; line < as->spool + as->index;) { - if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) { - if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) { - /* only do it once! */ - line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; - line += sizeof(BI_SETENV) - 1; - for (name = line; - isblank((unsigned char)*name); ++name) - ; - for (line = name; - ,*line && !isblank((unsigned char)*line); - ++line) - ; - if (*line) - ,*line++ = '\0'; - for (; isblank((unsigned char)*line); ++line) - ; - if (*line != '\0' && setenv(name, line, 1)) - warn("setenv(%s, %s)", name, line); - } - } else - if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) { - if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) { - /* only do it once! */ - line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; - line += sizeof(BI_UNSETENV) - 1; - for (name = line; - isblank((unsigned char)*name); ++name) - ; - for (line = name; - ,*line && !isblank((unsigned char)*line); - ++line) - ; - if (*line) - ,*line++ = '\0'; - unsetenv(name); - } - } - while (*line++) - ; - } - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_setenv][=auth_setenv=]] scans through =as->spool=, modifying the environment - according to =BI_SETENV= and =BI_UNSETENV= instructions. - -** auth_getvalue - :PROPERTIES: - :CUSTOM_ID: auth_getvalue - :END: - @@html: <details> <summary> @@ - #+BEGIN_SRC c - char *auth_getvalue(auth_session_t *as, char *what) - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - char *line, *v, *value; - int n, len; - - len = strlen(what); - - for (line = as->spool; line < as->spool + as->index;) { - if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0) - goto next; - line += sizeof(BI_VALUE) - 1; - - if (!isblank((unsigned char)*line)) - goto next; - - while (isblank((unsigned char)*++line)) - ; - - if (strncmp(line, what, len) != 0 || - !isblank((unsigned char)line[len])) - goto next; - line += len; - while (isblank((unsigned char)*++line)) - ; - value = strdup(line); - if (value == NULL) - return (NULL); - - /* - ,* XXX - There should be a more standardized - ,* routine for doing this sort of thing. - ,*/ - for (line = v = value; *line; ++line) { - if (*line == '\\') { - switch (*++line) { - case 'r': - ,*v++ = '\r'; - break; - case 'n': - ,*v++ = '\n'; - break; - case 't': - ,*v++ = '\t'; - break; - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': - n = *line - '0'; - if (isdigit((unsigned char)line[1])) { - ++line; - n <<= 3; - n |= *line-'0'; - } - if (isdigit((unsigned char)line[1])) { - ++line; - n <<= 3; - n |= *line-'0'; - } - break; - default: - ,*v++ = *line; - break; - } - } else - ,*v++ = *line; - } - ,*v = '\0'; - return (value); - next: - while (*line++) - ; - } - return (NULL); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_getvalue~2][=auth_getvalue=]] scans =as->spool= looking for lines beginning with - =BI_VALUE=. It then checks if the next word is equal to =what=. - - When it finds the desired line, it duplicates the string, converts - escape sequences in the value, and returns the newly created - string. - - For convenience, the function [[#auth_mkvalue][=auth_mkvalue=]] can be used inside - of the authentication module to create and return appropriately - escaped value strings. - -** auth_getchallenge - :PROPERTIES: - :CUSTOM_ID: auth_getchallenge - :END: - - The [[https://man.openbsd.org/auth_subr.3#auth_getchallenge][=auth_subr(3)=]] man page claims this function exists, but I - can't find it anywhere in the source code. I suspect this is an - error. - -* auth_open - :PROPERTIES: - :CUSTOM_ID: auth_open - :END: - @@html: <details> <summary> @@ - #+begin_src c - auth_session_t *auth_open(void) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - auth_session_t *as; - - if ((as = calloc(1, sizeof(auth_session_t))) != NULL) { - as->service = defservice; - as->fd = -1; - } - - return (as); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_open][=auth_open=]] is used by several functions to create a new auth - session. It allocates an [[#auth_session_t][=auth_session_t=]] struct on the heap, sets - its default =service= to that defined by =LOGIN_DEFSERVICE= in - =/include/login_cap.h=, which is currently ="login"=. - - #+begin_src c - #define LOGIN_DEFSERVICE "login" - #+end_src - - It then sets the =fd= field to =-1=, and returns the pointer. - -* auth_usercheck - :PROPERTIES: - :CUSTOM_ID: auth_usercheck - :END: - - @@html: <details> <summary> @@ - #+BEGIN_SRC c - auth_session_t *auth_usercheck(char *name, char *style, char *type, char *password) - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; - char pwbuf[_PW_BUF_LEN]; - auth_session_t *as; - login_cap_t *lc; - struct passwd pwstore, *pwd = NULL; - char *slash; - - if (!_auth_validuser(name)) - return (NULL); - if (strlcpy(namebuf, name, sizeof(namebuf)) >= sizeof(namebuf)) - return (NULL); - name = namebuf; - - /* - ,* Split up user:style names if we were not given a style - ,*/ - if (style == NULL && (style = strchr(name, ':')) != NULL) - ,*style++ = '\0'; - - /* - ,* Cope with user/instance. We are only using this to get - ,* the class so it is okay if we strip a /root instance - ,* The actual login script will pay attention to the instance. - ,*/ - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - if (pwd == NULL) { - if ((slash = strchr(name, '/')) != NULL) { - ,*slash = '\0'; - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - ,*slash = '/'; - } - } - if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) - return (NULL); - - if ((style = login_getstyle(lc, style, type)) == NULL) { - login_close(lc); - return (NULL); - } - - if (password) { - if ((as = auth_open()) == NULL) { - login_close(lc); - return (NULL); - } - auth_setitem(as, AUTHV_SERVICE, "response"); - auth_setdata(as, "", 1); - auth_setdata(as, password, strlen(password) + 1); - explicit_bzero(password, strlen(password)); - } else - as = NULL; - as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); - login_close(lc); - return (as); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_usercheck][=auth_usercheck=]] is very similar to [[#auth_userokay][=auth_userokay=]]. It takes the - same arguments, except it returns the [[#auth_session_t][=auth_session_t=]] struct - instead of just the status. - - It first checks that =name= is valid according to [[#_auth_validuser][=_auth_validuser=]]. - - If =style= is =NULL=, it checks if =name= is in the =user:style= - format, and splits it accordingly. - - It then gets the user's password database entry through - [[https://man.openbsd.org/man3/getpwnam.3#getpwnam_r][=getpwman_r(3)=]], which operates on the [[https://man.openbsd.org/passwd.5][=passwd(5)=]] database. After - it uses that to retrieve the user's login class using - [[https://man.openbsd.org/login_getclass#login_getclass][=login_getclass(3)=]], which returns a =login_cap_t=. Login classes - are stored in the [[https://man.openbsd.org/man5/login.conf.5][=login.conf(5)=]] database. - - That struct is then passed into [[https://man.openbsd.org/login_getclass#login_getstyle][=login_getstyle(3)=]], which also - received the =style= and =type=. If =type= is =NULL=, it returns - the first available login style for that class. If =style= is - specified, it is returned if available, otherwise =NULL= is - returned, which causes =auth_usercheck= to return =NULL= as well. - - It then creates a pointer =as= of type [[#auth_session_t][=auth_session_t=]], and handles - it differently based on whether =password= is =NULL=. - - - If the password is a string, it creates a new session using - [[#auth_open][=auth_open=]] and assigns it to =as=. It then sets the session - =service= to ="response"=, and adds the =password= string to the - session's =data=. - - #+BEGIN_SRC c - auth_setitem(as, AUTHV_SERVICE, "response"); - auth_setdata(as, "", 1); - auth_setdata(as, password, strlen(password) + 1); - #+END_SRC - - - If =password= is =NULL=, it sets =as= to =NULL=. - - It then passes the =auth_session_t= pointer (=as=), =name=, - =style=, login class (=lc->lc_class=), and a =NULL= char pointer to - [[#auth_verify][=auth_verify=]]. Finally it returns the auth session pointer. - - #+begin_src c - as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); - // [...] some cleanup - return (as); - #+end_src - -* auth_verify - :PROPERTIES: - :CUSTOM_ID: auth_verify - :END: - @@html: <details> <summary> @@ - #+BEGIN_SRC c - auth_session_t *auth_verify(auth_session_t *as, char *style, char *name, ...) - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - va_list ap; - char path[PATH_MAX]; - - if ((name == NULL || style == NULL) && as == NULL) - return (NULL); - - if (as == NULL && (as = auth_open()) == NULL) - return (NULL); - auth_setstate(as, 0); - - if (style != NULL && auth_setitem(as, AUTHV_STYLE, style) < 0) - return (as); - - if (name != NULL && auth_setitem(as, AUTHV_NAME, name) < 0) - return (as); - - style = auth_getitem(as, AUTHV_STYLE); - name = auth_getitem(as, AUTHV_NAME); - if (!_auth_validuser(name)) - return (as); - - snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); - va_start(ap, name); - auth_set_va_list(as, ap); - auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", - auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); - va_end(ap); - return (as); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_verify][=auth_verify=]] is used as a frontend for [[#auth_call][=auth_call=]]. - - It creates an auth session using =auth_open= if =as= is =NULL=. - - The =state= of the session is set to =0=. - - It sets the =name= and =style= of the session, if the - =style= and/or =name= are non-=NULL=. - - After that it constructs the path of the authentication module, - placing it in the variable =path=. It is constructed by combining - =_PATH_AUTHPROG=, which is defined in =login_cap.h= as - =/usr/libexec/auth/login_=, and the authentication style. For the - case of auth style =passwd=, it would result in the path - =/usr/libexec/auth/login_passwd=. - - #+begin_src c - snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); - #+end_src - - It then copies its variable arguments to the auth session using - [[#auth_set_va_list][=auth_set_va_list=]]. - - Then =auth_call= is called with the session struct, the path to the - auth module, the auth style, the =-s= flag followed by the service - (=login=, =challenge=, or =response=), a double dash, the user name, - and a =NULL= character pointer. The return value of =auth_call= is - ignored and a pointer to the auth session is returned immediately - afterwards. - - #+BEGIN_SRC c - va_start(ap, name); - auth_set_va_list(as, ap); - auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", - auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); - va_end(ap); - return (as); - #+END_SRC - -* auth_call - :PROPERTIES: - :CUSTOM_ID: auth_call - :END: - @@html: <details> <summary> @@ - #+BEGIN_SRC c - int auth_call(auth_session_t *as, char *path, ...) - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - char *line; - struct authdata *data; - struct authopts *opt; - pid_t pid; - int status; - int okay; - int pfd[2]; - int argc; - char *argv[64]; /* 64 args should be more than enough */ - #define Nargc (sizeof(argv)/sizeof(argv[0])) - - va_start(as->ap0, path); - - argc = 0; - if ((argv[argc] = _auth_next_arg(as)) != NULL) - ++argc; - - if (as->fd != -1) { - argv[argc++] = "-v"; - argv[argc++] = "fd=4"; /* AUTH_FD, see below */ - } - /* XXX - fail if out of space in argv */ - for (opt = as->optlist; opt != NULL; opt = opt->next) { - if (argc < Nargc - 2) { - argv[argc++] = "-v"; - argv[argc++] = opt->opt; - } else { - syslog(LOG_ERR, "too many authentication options"); - goto fail; - } - } - while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as))) - ++argc; - - if (argc >= Nargc - 1 && _auth_next_arg(as)) { - if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { - va_end(as->ap0); - explicit_bzero(&(as->ap0), sizeof(as->ap0)); - } - if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { - va_end(as->ap); - explicit_bzero(&(as->ap), sizeof(as->ap)); - } - syslog(LOG_ERR, "too many arguments"); - goto fail; - } - - argv[argc] = NULL; - - if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) == -1) { - syslog(LOG_ERR, "unable to create backchannel %m"); - warnx("internal resource failure"); - goto fail; - } - - switch (pid = fork()) { - case -1: - syslog(LOG_ERR, "%s: %m", path); - warnx("internal resource failure"); - close(pfd[0]); - close(pfd[1]); - goto fail; - case 0: - #define COMM_FD 3 - #define AUTH_FD 4 - if (dup2(pfd[1], COMM_FD) == -1) - err(1, "dup of backchannel"); - if (as->fd != -1) { - if (dup2(as->fd, AUTH_FD) == -1) - err(1, "dup of auth fd"); - closefrom(AUTH_FD + 1); - } else - closefrom(COMM_FD + 1); - execve(path, argv, auth_environ); - syslog(LOG_ERR, "%s: %m", path); - err(1, "%s", path); - default: - close(pfd[1]); - if (as->fd != -1) { - close(as->fd); /* so child has only ref */ - as->fd = -1; - } - while ((data = as->data) != NULL) { - as->data = data->next; - if (data->len > 0) { - write(pfd[0], data->ptr, data->len); - explicit_bzero(data->ptr, data->len); - } - free(data); - } - as->index = 0; - _auth_spool(as, pfd[0]); - close(pfd[0]); - do { - if (waitpid(pid, &status, 0) != -1) { - if (!WIFEXITED(status)) - goto fail; - break; - } - /* - ,* could get ECHILD if it was waited for by - ,* another thread or from a signal handler - ,*/ - } while (errno == EINTR); - } - - /* - ,* Now scan the spooled data - ,* It is easier to wait for all the data before starting - ,* to scan it. - ,*/ - for (line = as->spool; line < as->spool + as->index;) { - if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) { - line += sizeof(BI_REJECT) - 1; - if (!*line || *line == ' ' || *line == '\t') { - while (*line == ' ' || *line == '\t') - ++line; - if (!strcasecmp(line, "silent")) { - as->state = AUTH_SILENT; - break; - } - if (!strcasecmp(line, "challenge")) { - as->state = AUTH_CHALLENGE; - break; - } - if (!strcasecmp(line, "expired")) { - as->state = AUTH_EXPIRED; - break; - } - if (!strcasecmp(line, "pwexpired")) { - as->state = AUTH_PWEXPIRED; - break; - } - } - break; - } else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) { - line += sizeof(BI_AUTH) - 1; - if (!*line || *line == ' ' || *line == '\t') { - while (*line == ' ' || *line == '\t') - ++line; - if (*line == '\0') - as->state |= AUTH_OKAY; - else if (!strcasecmp(line, "root")) - as->state |= AUTH_ROOTOKAY; - else if (!strcasecmp(line, "secure")) - as->state |= AUTH_SECURE; - } - } else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) { - line += sizeof(BI_REMOVE) - 1; - while (*line == ' ' || *line == '\t') - ++line; - if (*line) - _add_rmlist(as, line); - } - while (*line++) - ; - } - - if (WEXITSTATUS(status)) - as->state &= ~AUTH_ALLOW; - - okay = as->state & AUTH_ALLOW; - - if (!okay) - auth_clrenv(as); - - if (0) { - fail: - auth_clrenv(as); - as->state = 0; - okay = -1; - } - - while ((data = as->data) != NULL) { - as->data = data->next; - free(data); - } - - if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { - va_end(as->ap0); - explicit_bzero(&(as->ap0), sizeof(as->ap0)); - } - - if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { - va_end(as->ap); - explicit_bzero(&(as->ap), sizeof(as->ap)); - } - return (okay); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_call~2][=auth_call=]] is responsible for setting up the environment, - calling the modules, and communicating with them. - - An array of char pointers called =argv= is allocated to hold the arguments for the - auth module. - - #+BEGIN_SRC c - char *argv[64]; /* 64 args should be more than enough */ - #+END_SRC - - First, the variable arguments are placed in =as->ap0=. - - [[#_auth_next_arg][=_auth_next_arg=]] is called once, with the result being set as the - first element in =argv=. If =as->fd= is set, adds =-v= and =fd=4= to - =argv=. - - Then it loops through the =optlist= and appends =-v= followed the - option for each of them. - - After that the rest of the arguments are retrieved from - =_auth_next_arg= and added to the end of =argv=. Finally a =NULL= is - added to the end of =argv=. - - Next a socket pair of type =PF_LOCAL, SOCK_STREAM= is created. This - is called the "back channel", and is used to communicate with the - authentication module. - - The process then calls [[https://man.openbsd.org/man2/fork.2][=fork(2)=]]. - - Here two constants are set for the back channel and optional - authentication file descriptors. - - #+begin_src c - #define COMM_FD 3 - #define AUTH_FD 4 - #+end_src - - In the child process, the back channel is set to file descriptor 3, - or =COMM_FD= using [[https://man.openbsd.org/man2/dup.2#dup2][=dup2(3)=]]. If =as->fd=, is not =-1=, it is set to - file descriptor 4, or =AUTH_FD=, also using =dup2(3)=. The remainder - of the file descriptors are closed using [[https://man.openbsd.org/man2/closefrom.2][=closefrom(2)=]] by calling - either =closefrom(COMM_FD + 1)= or =closefrom(AUTH_FD + 1)=, - depending on whether or not =AUTH_FD= is used. - - The child process then executes the module. - - #+begin_src c - execve(path, argv, auth_environ); - #+end_src - - =auth_environ= is defined at the top of the file as a very minimal - environment. - - #+BEGIN_SRC c - static char *auth_environ[] = { - "PATH=" _PATH_DEFPATH, - "SHELL=" _PATH_BSHELL, - NULL, - }; - #+END_SRC - - Where both constants are defined in =/include/paths.h=. - - #+BEGIN_SRC c - #define _PATH_DEFPATH "/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin" - #define _PATH_BSHELL "/bin/sh" - #+END_SRC - - In the parent process, the child's end of the back channel is - closed, and so is the parent's copy of =as->fd= if it exists. - - The data from =as->data= is then written to the back channel - sequentially, zeroed, and freed. - - Next =as->index= is set to =0=. - - The response from the authentication module is then read from the - back channel and put into =as->spool= with an optional received file - descriptor placed in =as->fd=, using [[#_auth_spool][=_auth_spool=]]. - - #+begin_src c - _auth_spool(as, pfd[0]); - #+end_src - - Once the back channel data has finished spooling, it is scanned for - key words defined in =login_cap.h=. - - #+BEGIN_SRC c - #define BI_AUTH "authorize" /* Accepted authentication */ - #define BI_REJECT "reject" /* Rejected authentication */ - #define BI_CHALLENGE "reject challenge" /* Reject with a challenge */ - #define BI_SILENT "reject silent" /* Reject silently */ - #define BI_REMOVE "remove" /* remove file on error */ - #define BI_ROOTOKAY "authorize root" /* root authenticated */ - #define BI_SECURE "authorize secure" /* okay on non-secure line */ - #define BI_SETENV "setenv" /* set environment variable */ - #define BI_UNSETENV "unsetenv" /* unset environment variable */ - #define BI_VALUE "value" /* set local variable */ - #define BI_EXPIRED "reject expired" /* account expired */ - #define BI_PWEXPIRED "reject pwexpired" /* password expired */ - #define BI_FDPASS "fd" /* child is passing an fd */ - #+END_SRC - - The [[https://man.openbsd.org/login.conf][=login.conf(5)=]] man page once again goes into greater detail on - these values. - - #+BEGIN_SRC - authorize The user has been authorized. - - authorize secure - The user has been authorized and root should be allowed to - login even if this is not a secure terminal. This should only - be sent by authentication styles that are secure over insecure - lines. - - reject Authorization is rejected. This overrides any indication that - the user was authorized (though one would question the wisdom - in sending both a reject and an authorize command). - - reject challenge - Authorization was rejected and a challenge has been made - available via the value challenge. - - reject silent - Authorization is rejected, but no error messages should be - generated. - - remove file - If the login session fails for any reason, remove file before - termination. - - setenv name value - If the login session succeeds, the environment variable name - should be set to the specified value. - - unsetenv name - If the login session succeeds, the environment variable name - should be removed. - - value name value - Set the internal variable name to the specified value. The - value should only contain printable characters. Several \ - sequences may be used to introduce non printing characters. - These are: - - \n A newline. - - \r A carriage return. - - \t A tab. - - \xxx The character represented by the octal value xxx. The - value may be one, two, or three octal digits. - - \c The string is replaced by the value of c. This allows - quoting an initial space or the \ character itself. - - - The following values are currently defined: - - challenge - See section on challenges below. - - errormsg - If set, the value is the reason authentication failed. - The calling program may choose to display this when - rejecting the user, but display is not required. - - #+END_SRC - - The scanner is looking for lines that begin with =BI_AUTH=, - =BI_REJECT=, or =BI_REMOVE=. - - Here =as->state= is set according to the values defined on - =login_cap.h=. - - #+BEGIN_SRC c - /* - * bits which can be returned by authenticate()/auth_scan() - */ - #define AUTH_OKAY 0x01 /* user authenticated */ - #define AUTH_ROOTOKAY 0x02 /* authenticated as root */ - #define AUTH_SECURE 0x04 /* secure login */ - #define AUTH_SILENT 0x08 /* silent rejection */ - #define AUTH_CHALLENGE 0x10 /* a challenge was given */ - #define AUTH_EXPIRED 0x20 /* account expired */ - #define AUTH_PWEXPIRED 0x40 /* password expired */ - #+END_SRC - - If a rejection is received (any line starting with =BI_REJECT=), - =as->state= is set according to the rejection, and the scanning is - stopped. Rejections are final and take precedence over any - authorizations. - - If an authorization is received (any line starting with =BI_AUTH=), - the appropriate state is bitwise =or=-ed onto =as->state=. This - allows multiple authorizations, such as a case where both - =BI_ROOTOKAY= and =BI_SECURE= are sent. This would result in a state - of =AUTH_OKAY || AUTH_ROOTOKAY || AUTH_SECURE=. - - For any lines beginning with =BI_REMOVE=, the file names after the - key word are sent to [[#_add_rmlist][=_add_rmlist=]]. - #+begin_src c - _add_rmlist(as, line); - #+end_src - - After scanning is complete, the exit status of the process is - checked. A non-zero exit status means the request will get denied. - - An =okay= value is then defined by masking the state with the value - =AUTH_ALLOW=. - - #+begin_src c - okay = as->state & AUTH_ALLOW; - #+end_src - - =AUTH_ALLOW= is defined in =login_cap.h=. - - #+begin_src c - #define AUTH_ALLOW (AUTH_OKAY | AUTH_ROOTOKAY | AUTH_SECURE) - #+end_src - - If the status results in a rejection, [[#auth_clrenv][=auth_clrenv=]] is called with - =as=. This removes any requests the login script has made to set - environment variables from =as->spool=. - - =okay= is then returned to the caller. - -** _auth_next_arg - :PROPERTIES: - :CUSTOM_ID: _auth_next_arg - :END: - - @@html: <details> <summary> @@ - #+BEGIN_SRC c - static char *_auth_next_arg(auth_session_t *as) - #+END_SRC - @@html: </summary> @@ - #+begin_src c - { - char *arg; - - if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { - if ((arg = va_arg(as->ap0, char *)) != NULL) - return (arg); - va_end(as->ap0); - explicit_bzero(&(as->ap0), sizeof(as->ap0)); - } - if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { - if ((arg = va_arg(as->ap, char *)) != NULL) - return (arg); - va_end(as->ap); - explicit_bzero(&(as->ap), sizeof(as->ap)); - } - return (NULL); - } - #+end_src - @@html: </details> @@ - - Loops through =as->ap0= then =as->ap=, returning one argument per - call. Calls =va_end= on each list once it finishes with them, then - [[https://man.openbsd.org/man3/bzero.3#explicit_bzero][=explicit_bzero(3)=]]'s them. - - Finally when it's gone through both lists, returns =NULL= - -** _auth_spool - :PROPERTIES: - :CUSTOM_ID: _auth_spool - :END: - @@html: <details> <summary> @@ - #+begin_src c - static void _auth_spool(auth_session_t *as, int fd) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - ssize_t r; - char *b, *s; - - for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) { - r = read(fd, as->spool + as->index, - sizeof(as->spool) - as->index); - if (r <= 0) { - as->spool[as->index] = '\0'; - return; - } - b = as->spool + as->index; - as->index += r; - /* - ,* Convert newlines into NULs to allow easy scanning of the - ,* file and receive an fd if there is a BI_FDPASS message. - ,* XXX - checking for BI_FDPASS here is annoying but - ,* we need to avoid the read() slurping in control data. - ,*/ - while (r-- > 0) { - if (*b++ == '\n') { - b[-1] = '\0'; - if (strcasecmp(s, BI_FDPASS) == 0) - _recv_fd(as, fd); - s = b; - } - } - } - - syslog(LOG_ERR, "Overflowed backchannel spool buffer"); - errx(1, "System error in authentication program"); - } - #+end_src - @@html: </details> @@ - - =_auth_spool='s job is to read data from =fd= and place it in - =as->spool=, and to update =as->index= with the length of the data - on the spool. While spooling it converts newlines to =NUL='s in - order to parse the output more easily. It also handles any file - descriptors passed through the back channel by sending them to - [[#_recv_fd][=_recv_fd=]]. - - #+begin_src c - // [...] - if (strcasecmp(s, BI_FDPASS) == 0) - _recv_fd(as, fd); - #+end_src - -** _recv_fd - :PROPERTIES: - :CUSTOM_ID: _recv_fd - :END: - - @@html: <details> <summary> @@ - #+begin_src c - static void _recv_fd(auth_session_t *as, int fd) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct msghdr msg; - struct cmsghdr *cmp; - union { - struct cmsghdr hdr; - char buf[CMSG_SPACE(sizeof(int))]; - } cmsgbuf; - - memset(&msg, 0, sizeof(msg)); - msg.msg_control = &cmsgbuf.buf; - msg.msg_controllen = sizeof(cmsgbuf.buf); - if (recvmsg(fd, &msg, 0) == -1) - syslog(LOG_ERR, "recvmsg: %m"); - else if (msg.msg_flags & MSG_TRUNC) - syslog(LOG_ERR, "message truncated"); - else if (msg.msg_flags & MSG_CTRUNC) - syslog(LOG_ERR, "control message truncated"); - else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL) - syslog(LOG_ERR, "missing control message"); - else { - if (cmp->cmsg_level != SOL_SOCKET) - syslog(LOG_ERR, "unexpected cmsg_level %d", - cmp->cmsg_level); - else if (cmp->cmsg_type != SCM_RIGHTS) - syslog(LOG_ERR, "unexpected cmsg_type %d", - cmp->cmsg_type); - else if (cmp->cmsg_len != CMSG_LEN(sizeof(int))) - syslog(LOG_ERR, "bad cmsg_len %d", - cmp->cmsg_len); - else { - if (as->fd != -1) - close(as->fd); - as->fd = *(int *)CMSG_DATA(cmp); - } - } - } - #+end_src - @@html: </details> @@ - - =_recv_fd= reads control messages, also called ancillary data, from - =fd= and tries to receive a file descriptor. It does this using the - [[https://man.openbsd.org/CMSG_DATA.3][control message API]]. - - If it receives one and =as->fd= is equal to =-1=, it sets it to the - received file descriptor. Otherwise it closes the received file - descriptor. - -** _add_rmlist - :PROPERTIES: - :CUSTOM_ID: _add_rmlist - :END: - @@html: <details> <summary> @@ - #+begin_src c - static void _add_rmlist(auth_session_t *as, char *file) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct rmfiles *rm; - size_t i = strlen(file) + 1; - - // XXX should rangecheck i since we are about to add? - - if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) { - syslog(LOG_ERR, "Failed to allocate rmfiles: %m"); - return; - } - rm->file = (char *)(rm + 1); - rm->next = as->rmlist; - strlcpy(rm->file, file, i); - as->rmlist = rm; - } - #+end_src - @@html: </details> @@ - - =_add_rmlist= is used to add to the list of files to be removed - after authentication is complete - - A =rmfiles= struct is allocated and appended to the end of the - =as->rmlist= linked list. - -* auth_close - :PROPERTIES: - :CUSTOM_ID: auth_close - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int auth_close(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct rmfiles *rm; - struct authopts *opt; - struct authdata *data; - int s; - - /* - ,* Save our return value - ,*/ - s = as->state & AUTH_ALLOW; - - if (s == 0) - as->index = 0; - - auth_setenv(as); - - - /* - ,* Clean out the rmlist and remove specified files if the - ,* authentication failed - ,*/ - while ((rm = as->rmlist) != NULL) { - as->rmlist = rm->next; - if (s == 0) - unlink(rm->file); - free(rm); - } - - /* - ,* Clean out the opt list - ,*/ - while ((opt = as->optlist) != NULL) { - as->optlist = opt->next; - free(opt); - } - - /* - ,* Clean out data - ,*/ - while ((data = as->data) != NULL) { - if (as->data->len) - explicit_bzero(as->data->ptr, as->data->len); - as->data = data->next; - free(data); - } - - if (as->pwd != NULL) { - explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); - free(as->pwd); - as->pwd = NULL; - } - - /* - ,* Clean up random variables - ,*/ - if (as->service && as->service != defservice) - free(as->service); - free(as->challenge); - free(as->class); - free(as->style); - free(as->name); - - free(as); - return (s); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_close][=auth_close=]] is responsible for setting the environment variables, - removing any files requested by the authentication module, and - freeing =as=. - - First it saves the allow state of =as->state= in a variable =s=. - - #+begin_src c - s = as->state & AUTH_ALLOW; - #+end_src - - If =s= is equal to =0= (failure), =as->index= is set to =0=, truncating - =as->spool= so that no further functions will be able to read from - it. - - It then modifies the environment using [[#auth_setenv][=auth_setenv=]] - - #+begin_src c - auth_setenv(as); - #+end_src - - All =as->rmlist= structs are checked. If =s= is equal to =0=, the - files are deleted. All =rmlist= structs are then freed. - - All =as->optlist= structs are freed. - - All =as->data= structs are [[https://man.openbsd.org/man3/bzero.3#explicit_bzero][=explicit_bzero(3)=]]'d and then freed. - - =as->pwd= is =explicit_bzero='d and freed. - - All remaining structs referenced by =as= are freed. - - =as= is freed. - - =s= is returned. - -* auth_userchallenge - :PROPERTIES: - :CUSTOM_ID: auth_userchallenge - :END: - - @@html: <details> <summary> @@ - #+begin_src c - auth_session_t *auth_userchallenge(char *name, char *style, char *type, char **challengep) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1]; - auth_session_t *as; - login_cap_t *lc; - struct passwd pwstore, *pwd = NULL; - char *slash, pwbuf[_PW_BUF_LEN]; - - if (!_auth_validuser(name)) - return (NULL); - if (strlen(name) >= sizeof(namebuf)) - return (NULL); - strlcpy(namebuf, name, sizeof namebuf); - name = namebuf; - - /* - ,* Split up user:style names if we were not given a style - ,*/ - if (style == NULL && (style = strchr(name, ':')) != NULL) - ,*style++ = '\0'; - - /* - ,* Cope with user/instance. We are only using this to get - ,* the class so it is okay if we strip a /root instance - ,* The actual login script will pay attention to the instance. - ,*/ - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - if (pwd == NULL) { - if ((slash = strchr(name, '/')) != NULL) { - ,*slash = '\0'; - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - ,*slash = '/'; - } - } - if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL) - return (NULL); - - if ((style = login_getstyle(lc, style, type)) == NULL || - (as = auth_open()) == NULL) { - login_close(lc); - return (NULL); - } - if (auth_setitem(as, AUTHV_STYLE, style) < 0 || - auth_setitem(as, AUTHV_NAME, name) < 0 || - auth_setitem(as, AUTHV_CLASS, lc->lc_class) < 0) { - auth_close(as); - login_close(lc); - return (NULL); - } - login_close(lc); - ,*challengep = auth_challenge(as); - return (as); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_userchallenge][=auth_userchallenge=]] is used when the authentication style requires - that the user be presented with a challenge, but the user cannot be - directly interacted with over the terminal. As an example, this - might be used in cases where the user is using S/KEY authentication - over SSH. - - A fair portion of this function is very similar to - [[#auth_usercheck][=auth_usercheck=]]. Instead of having a password argument however, it - has a pointer to string, which is used to return the challenge to - the calling function. - - It first checks that =name= is a valid username using [[#_auth_validuser][=_auth_validuser=]]. - - If =style= is =NULL=, it checks if =name= is in the =user:style= - format, and splits it accordingly. - - It then gets the user's password database entry through - [[https://man.openbsd.org/man3/getpwnam.3#getpwnam_r][=getpwman_r(3)=]], which operates on the [[https://man.openbsd.org/passwd.5][=passwd(5)=]] database. It then - uses that to retrieve the user's login class using - [[https://man.openbsd.org/login_getclass#login_getclass][=login_getclass(3)=]], which returns a =login_cap_t=. Login classes - are stored in the [[https://man.openbsd.org/man5/login.conf.5][=login.conf(5)=]] database. - - That struct is then passed into [[https://man.openbsd.org/login_getclass#login_getstyle][=login_getstyle(3)=]], which also - received the =style= and =type=. If =type= is =NULL=, it returns - the first available login style for that class. If =style= is - specified, it is returned if available, otherwise =NULL= is - returned, which causes =auth_userchallenge= to return =NULL= as - well. - - This is where =auth_userchallenge= and [[#auth_usercheck][=auth_usercheck=]] begin to diverge. - - It creates a new auth session using [[#auth_open][=auth_open=]] as variable =as=. - - The =style=, =name= and =class= properties of the session are then - set using [[#auth_setitem][=auth_setitem=]]. - - It then calls [[#auth_challenge][=auth_challenge=]] with =as= as the argument. The return - value from that call is used to set =challengep=, and =as= is - returned. - - #+begin_src c - *challengep = auth_challenge(as); - return (as); - #+end_src - -* auth_challenge - :PROPERTIES: - :CUSTOM_ID: auth_challenge - :END: - - @@html: <details> <summary> @@ - #+begin_src c - char *auth_challenge(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - char path[PATH_MAX]; - int len; - - if (as == NULL || as->style == NULL || as->name == NULL || - !_auth_validuser(as->name)) - return (NULL); - - len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); - if (len < 0 || len >= sizeof(path)) - return (NULL); - - as->state = 0; - - free(as->challenge); - as->challenge = NULL; - - auth_call(as, path, as->style, "-s", "challenge", "--", as->name, - as->class, (char *)NULL); - if (as->state & AUTH_CHALLENGE) - as->challenge = auth_getvalue(as, "challenge"); - as->state = 0; - as->index = 0; /* toss our data */ - return (as->challenge); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_challenge][=auth_challenge=]], much like [[#auth_verify][=auth_verify=]] is a function that acts as - a front-end for [[#auth_call][=auth_call=]], except used specifically for - challenges. - - First the session =as= is checked. If it's =NULL=, or =as->style= is - =NULL=, =as->name= is =NULL=, or if the username begins with a - hyphen, or has a length of zero, the function returns =NULL=. - - Then the path to the auth module is created. - - #+begin_src c - snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); - #+end_src - - =as->state= and =as->challenge= are then reset, in case they were - already set. - - Then [[#auth_call][=auth_call=]] is called, with the challenge style set. - - #+begin_src c - auth_call(as, path, as->style, "-s", "challenge", "--", as->name, as->class, (char *)NULL); - #+end_src - - =as->state= is checked for the =AUTH_CHALLENGE= bit, indicating the - auth module has returned a challenge. If it's present, the challenge - is extracted from the back channel output, and used to set - =as->challenge=. - - #+begin_src c - if (as->state & AUTH_CHALLENGE) - as->challenge = auth_getvalue(as, "challenge"); - #+end_src - - =as->state= and =as->index= are then set to zero, discarding the - data. - - =as->challenge= is then returned. - -* auth_userresponse - :PROPERTIES: - :CUSTOM_ID: auth_userresponse - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int auth_userresponse(auth_session_t *as, char *response, int more) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - char path[PATH_MAX]; - char *style, *name, *challenge, *class; - int len; - - if (as == NULL) - return (0); - - auth_setstate(as, 0); - - if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL || - (name = auth_getitem(as, AUTHV_NAME)) == NULL || - !_auth_validuser(name)) { - if (more == 0) - return (auth_close(as)); - return(0); - } - - len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style); - if (len < 0 || len >= sizeof(path)) { - if (more == 0) - return (auth_close(as)); - return (0); - } - - challenge = auth_getitem(as, AUTHV_CHALLENGE); - class = auth_getitem(as, AUTHV_CLASS); - - if (challenge) - auth_setdata(as, challenge, strlen(challenge) + 1); - else - auth_setdata(as, "", 1); - if (response) { - auth_setdata(as, response, strlen(response) + 1); - explicit_bzero(response, strlen(response)); - } else - auth_setdata(as, "", 1); - - auth_call(as, path, style, "-s", "response", "--", name, - class, (char *)NULL); - - /* - * If they authenticated then make sure they did not expire - */ - if (auth_getstate(as) & AUTH_ALLOW) - auth_check_expire(as); - if (more == 0) - return (auth_close(as)); - return (auth_getstate(as) & AUTH_ALLOW); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_userresponse][=auth_userresponse=]] is used to pass the user's response from - [[#auth_userchallenge][=auth_userchallenge=]] back to the authentication module. Similar to - =auth_userchallenge=, it is also a front-end for [[#auth_call][=auth_call=]]. - - If =as= is =NULL=, =0= is returned. - - The state of =as= is then set to =0=. - #+begin_src c - auth_setstate(as, 0); - #+end_src - - =as= is then checked to ensure all the required items are set. It - checks if =as->style= or =as->name= are =NULL=, or if the username - is invalid using [[#_auth_validuser][=_auth_validuser=]]. If any of those checks fail, and - =more= is equal to =0=, then the session is closed using - [[#auth_close][=auth_close=]], and the return value of that returned. Otherwise =0= - is returned. - - Then the path to the [[#modules][auth module]] is created similarly to how it is - created in [[#auth_verify][=auth_verify=]]. - - The challenge and class of the session are extracted and stored in - variables =challenge= and =class= respectively. - - If =challenge= contains data, its contents are added to the - =as->data= spool, otherwise an empty string is added to the spool. - - If =response= contains data, it is added to the data spool as well, - and then =respose= is =explicit_bzero='d. Otherwise an empty string - is added to the data spool. - - Next [[#auth_call][=auth_call=]] is used to call the auth module with service type - =response=. - - #+begin_src c - auth_call(as, path, style, "-s", "response", "--", name, - class, (char *)NULL); - #+end_src - - If the request is allowed, it's checked to make sure it's not - expired using [[#auth_check_expire][=auth_check_expire=]]. - - If =more= is equal to =0=, the session is closed using [[#auth_close][=auth_close=]] - and the return value from it is returned. - - The allow state of the session is then returned. - - #+begin_src c - return (auth_getstate(as) & AUTH_ALLOW); - #+end_src - -* auth_approval - :PROPERTIES: - :CUSTOM_ID: auth_approval - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - int close_on_exit, close_lc_on_exit, len; - struct passwd pwstore, *pwd; - char *approve, *s, path[PATH_MAX], pwbuf[_PW_BUF_LEN]; - - pwd = NULL; - close_on_exit = as == NULL; - close_lc_on_exit = lc == NULL; - - if (as != NULL && name == NULL) - name = auth_getitem(as, AUTHV_NAME); - - if (as != NULL) - pwd = auth_getpwd(as); - - if (pwd == NULL) { - if (name != NULL) { - if (!_auth_validuser(name)) { - warnx("cannot approve who we don't recognize"); - return (0); - } - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - } else { - getpwuid_r(getuid(), &pwstore, pwbuf, sizeof(pwbuf), - &pwd); - if (pwd == NULL) { - syslog(LOG_ERR, "no such user id %u", getuid()); - warnx("cannot approve who we don't recognize"); - return (0); - } - name = pwd->pw_name; - } - } - - if (name == NULL) - name = pwd->pw_name; - - if (lc == NULL) { - if (strlen(name) >= PATH_MAX) { - syslog(LOG_ERR, "username to login %.*s...", - PATH_MAX, name); - warnx("username too long"); - return (0); - } - if (pwd == NULL && (approve = strchr(name, '.')) != NULL) { - strlcpy(path, name, sizeof path); - path[approve - name] = '\0'; - getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); - } - lc = login_getclass(pwd ? pwd->pw_class : NULL); - if (lc == NULL) { - warnx("unable to classify user"); - return (0); - } - } - - if (!type) - type = LOGIN_DEFSERVICE; - else { - if (strncmp(type, "approve-", 8) == 0) - type += 8; - - len = snprintf(path, sizeof(path), "approve-%s", type); - if (len < 0 || len >= sizeof(path)) { - if (close_lc_on_exit) - login_close(lc); - syslog(LOG_ERR, "approval path too long %.*s...", - PATH_MAX, type); - warnx("approval script path too long"); - return (0); - } - } - - if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL) - approve = login_getcapstr(lc, s = "approve", NULL, NULL); - - if (approve && approve[0] != '/') { - if (close_lc_on_exit) - login_close(lc); - syslog(LOG_ERR, "Invalid %s script: %s", s, approve); - warnx("invalid path to approval script"); - free(approve); - return (0); - } - - if (as == NULL && (as = auth_open()) == NULL) { - if (close_lc_on_exit) - login_close(lc); - syslog(LOG_ERR, "%m"); - warn(NULL); - free(approve); - return (0); - } - - auth_setstate(as, AUTH_OKAY); - if (auth_setitem(as, AUTHV_NAME, name) < 0) { - syslog(LOG_ERR, "%m"); - warn(NULL); - goto out; - } - if (auth_check_expire(as) < 0) /* is this account expired */ - goto out; - if (_auth_checknologin(lc, - auth_getitem(as, AUTHV_INTERACTIVE) != NULL)) { - auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); - goto out; - } - if (login_getcapbool(lc, "requirehome", 0) && pwd && pwd->pw_dir && - pwd->pw_dir[0]) { - struct stat sb; - - if (stat(pwd->pw_dir, &sb) == -1 || !S_ISDIR(sb.st_mode) || - (pwd->pw_uid && sb.st_uid == pwd->pw_uid && - (sb.st_mode & S_IXUSR) == 0)) { - auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW)); - goto out; - } - } - if (approve) - auth_call(as, approve, strrchr(approve, '/') + 1, "--", name, - lc->lc_class, type, (char *)NULL); - - out: - free(approve); - if (close_lc_on_exit) - login_close(lc); - - if (close_on_exit) - return (auth_close(as)); - return (auth_getstate(as) & AUTH_ALLOW); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_approval][=auth_approval=]] is used to check a user against the [[#approval][approval script]] - for service =type=. It is a front end for [[#auth_call][=auth_call=]]. Approval - script types all begin with =approval-=. - - Before running the scripts, first the validity of the account is - checked. This is done first using [[#auth_check_expired][=auth_check_expired=]], then - [[#_auth_checknologin][=_auth_checknologin=]], and finally [[https://man.openbsd.org/login_getcapbool#login_getcapbool][=login_getcapbool=]] to ensure the - user has a home directory if one is required by their login class. - - If =type= doesn't begin with =approval-= it will be prepended - internally. - - if =as= is =NULL=, an auth session will be created and destroyed - inside the function. - - If =lc= is =NULL=, it will be retrieved internally by looking up - =name=. - - If =type= is =NULL=, the default of =LOGIN_DEFSERVICE= is used. This - is defined in =login_cap.h= as =login=. This should call the default - =approval= script, according to the [[https://man.openbsd.org/login.conf#CAPABILITIES][=CAPABILITIES=]] section of the - =login.conf= man page. - - It returns either =0= for disapproval, or non-zero for approval. - -* auth_check_expire - :PROPERTIES: - :CUSTOM_ID: auth_check_expire - :END: - - @@html: <details> <summary> @@ - #+begin_src c - quad_t auth_check_expire(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { - as->state &= ~AUTH_ALLOW; - as->state |= AUTH_EXPIRED; /* XXX */ - return (-1); - } - - if (as->pwd == NULL) - return (0); - - if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { - if (as->now.tv_sec == 0) - WRAP(gettimeofday)(&as->now, NULL); - if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { - as->state &= ~AUTH_ALLOW; - as->state |= AUTH_EXPIRED; - } - if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) - return (-1); - return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); - } - return (0); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_check_expire][=auth_check_expire=]] is used to check if the account used for a - session is expired. If an account is valid, it returns =0=. - Otherwise it returns a negative number representing the number of - seconds elapsed since the account expired. If there's no account - associated with the session, it will return =-1=. - - It first checks if =as->pwd= is set, and if it isn't it tries to set - it using [[#auth_setpwd][=auth_setpwd=]]. If both of those fail, then it returns =-1= - and removes the =AUTH_ALLOW= bitmask from =as->state=, and adds the - bitmask for =AUTH_EXPIRED=. - -* auth_check_change - :PROPERTIES: - :CUSTOM_ID: auth_check_change - :END: - - @@html: <details> <summary> @@ - #+begin_src c - quad_t auth_check_change(auth_session_t *as) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) { - as->state &= ~AUTH_ALLOW; - as->state |= AUTH_PWEXPIRED; /* XXX */ - return (-1); - } - - if (as->pwd == NULL) - return (0); - - if (as->pwd && (quad_t)as->pwd->pw_change) { - if (as->now.tv_sec == 0) - WRAP(gettimeofday)(&as->now, NULL); - if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { - as->state &= ~AUTH_ALLOW; - as->state |= AUTH_PWEXPIRED; - } - if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) - return (-1); - return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); - } - return (0); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/auth_subr.3#auth_check_change][=auth_check_change=]] is used to check if the password associated with - an account is expired. If the password isn't expired, it returns - =0=. Otherwise it returns a negative number representing the number - of seconds elapsed since the password expired. If there's no account - associated with the session, it will return =-1=. - - It operates very similarly to [[#auth_check_expire][=auth_check_expire=]]. - -* auth_checknologin - :PROPERTIES: - :CUSTOM_ID: auth_checknologin - :END: - @@html: <details> <summary> @@ - #+begin_src c - void auth_checknologin(login_cap_t *lc) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - if (_auth_checknologin(lc, 1)) - exit(1); - } - - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/authenticate.3#auth_checknologin][=auth_checknologin=]] is a simple wrapper around the internal - [[#_auth_checknologin][=_auth_checknologin=]]. If the user is now allowed to login, it prints - the reason and calls =exit(1)=. - -* auth_cat - :PROPERTIES: - :CUSTOM_ID: auth_cat - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int auth_cat(char *file) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - int fd, nchars; - char tbuf[8192]; - - if ((fd = open(file, O_RDONLY, 0)) == -1) - return (0); - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) - (void)write(fileno(stdout), tbuf, nchars); - (void)close(fd); - return (1); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/man3/authenticate.3#auth_cat][=auth_cat=]] is a helper function that will write the contents of a - =file= to =stdout=. It returns =0= on failure or =1= on success. - -* auth_mkvalue - :PROPERTIES: - :CUSTOM_ID: auth_mkvalue - :END: - - @@html: <details> <summary> @@ - #+begin_src c - char *auth_mkvalue(char *value) - #+end_src - - @@html: </summary> @@ - #+begin_src c - { - char *big, *p; - - big = malloc(strlen(value) * 4 + 1); - if (big == NULL) - return (NULL); - /* - ,* XXX - There should be a more standardized - ,* routine for doing this sort of thing. - ,*/ - for (p = big; *value; ++value) { - switch (*value) { - case '\r': - ,*p++ = '\\'; - ,*p++ = 'r'; - break; - case '\n': - ,*p++ = '\\'; - ,*p++ = 'n'; - break; - case '\\': - ,*p++ = '\\'; - ,*p++ = *value; - break; - case '\t': - case ' ': - if (p == big) - ,*p++ = '\\'; - ,*p++ = *value; - break; - default: - if (!isprint((unsigned char)*value)) { - ,*p++ = '\\'; - ,*p++ = ((*value >> 6) & 0x3) + '0'; - ,*p++ = ((*value >> 3) & 0x7) + '0'; - ,*p++ = ((*value ) & 0x7) + '0'; - } else - ,*p++ = *value; - break; - } - } - ,*p = '\0'; - return (big); - } - #+end_src - @@html: </details> @@ - - [[https://man.openbsd.org/authenticate.3#auth_mkvalue][=auth_mkvalue=]] creates an escaped string which can be decoded by [[#auth_getvalue][=auth_getvalue=]]. - -* _auth_validuser - :PROPERTIES: - :CUSTOM_ID: _auth_validuser - :END: - - @@html: <details> <summary> @@ - #+begin_src c - int _auth_validuser(const char *name) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - /* User name must be specified and may not start with a '-'. */ - if (*name == '\0' || *name == '-') { - syslog(LOG_ERR, "invalid user name %s", name); - return 0; - } - return 1; - } - #+end_src - @@html: </details> @@ - - =_auth_validuser= is a small helper function used to check if a - username passes some very basic validity criteria. Those being that - it must not be an empty sting, and that it doesn't start with a - hyphen. - - If a username is invalid, it is logged in the syslog. - - It returns =1= if the username is valid, otherwise it returns =0=. - -* _auth_checknologin - :PROPERTIES: - :CUSTOM_ID: _auth_checknologin - :END: - - @@html: <details> <summary> @@ - #+begin_src c - static int _auth_checknologin(login_cap_t *lc, int print) - #+end_src - @@html: </summary> @@ - #+begin_src c - { - struct stat sb; - char *nologin; - int mustfree; - - if (login_getcapbool(lc, "ignorenologin", 0)) - return (0); - - /* - ,* If we fail to get the nologin file due to a database error, - ,* assume there should have been one... - ,*/ - nologin = login_getcapstr(lc, "nologin", "", NULL); - mustfree = nologin && *nologin != '\0'; - if (nologin == NULL) - goto print_nologin; - - /* First try the nologin file specified in login.conf. */ - if (*nologin != '\0' && stat(nologin, &sb) == 0) - goto print_nologin; - if (mustfree) { - free(nologin); - mustfree = 0; - } - - /* If that doesn't exist try _PATH_NOLOGIN. */ - if (stat(_PATH_NOLOGIN, &sb) == 0) { - nologin = _PATH_NOLOGIN; - goto print_nologin; - } - - /* Couldn't stat any nologin files, must be OK to login. */ - return (0); - - print_nologin: - if (print) { - if (!nologin || *nologin == '\0' || auth_cat(nologin) == 0) { - puts("Logins are not allowed at this time."); - fflush(stdout); - } - } - if (mustfree) - free(nologin); - return (-1); - } - #+end_src - @@html: </details> @@ - - =_auth_checknologin= is a helper function in =authenticate.c=. It is - used to check the =nologin= status of the account. If =print= is - non-zero, it will print the reason for the failure, and print the - contents of the nologin file using [[#auth_cat][=auth_cat=]]. - - It returns =0= if the user is allowed to login, and =-1= otherwise. - -* Call Graph - :PROPERTIES: - :CUSTOM_ID: graph - :END: - - #+ATTR_HTML: :title Authentication call graph - #+ATTR_HTML: :title Authentication call graph - [[file:graph.svg]] - - @@html: <details> <summary> @@ - *Click here* to see the code that generates the call graph. - @@html: </summary> @@ -#+INCLUDE: "gen_dot.rb" src ruby - @@html: </details> @@ - -* Copyright - :PROPERTIES: - :CUSTOM_ID: copyright - :END: - - @@html: <details> <summary> @@ - *Click here to expand copyright notices* - @@html: </summary> @@ - #+CAPTION: From [[https://github.com/openbsd/src/blob/master/lib/libc/gen/authenticate.c][=authenticate.c=]] - #+begin_src text - /* $OpenBSD: authenticate.c,v 1.28 2019/12/04 06:25:45 deraadt Exp $ */ - - /*- - ,* Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. - ,* - ,* Redistribution and use in source and binary forms, with or without - ,* modification, are permitted provided that the following conditions - ,* are met: - ,* 1. Redistributions of source code must retain the above copyright - ,* notice, this list of conditions and the following disclaimer. - ,* 2. Redistributions in binary form must reproduce the above copyright - ,* notice, this list of conditions and the following disclaimer in the - ,* documentation and/or other materials provided with the distribution. - ,* 3. All advertising materials mentioning features or use of this software - ,* must display the following acknowledgement: - ,* This product includes software developed by Berkeley Software Design, - ,* Inc. - ,* 4. The name of Berkeley Software Design, Inc. may not be used to endorse - ,* or promote products derived from this software without specific prior - ,* written permission. - ,* - ,* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND - ,* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - ,* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ,* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE - ,* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - ,* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - ,* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ,* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - ,* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - ,* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - ,* SUCH DAMAGE. - ,* - ,* BSDI $From: authenticate.c,v 2.21 1999/09/08 22:33:26 prb Exp $ - ,*/ - #+end_src - - #+CAPTION: From [[https://github.com/openbsd/src/blob/master/lib/libc/gen/auth_subr.c][=auth_subr.c=]] - #+begin_src text - /* $OpenBSD: auth_subr.c,v 1.56 2020/10/13 04:42:28 guenther Exp $ */ - - /* - ,* Copyright (c) 2000-2002,2004 Todd C. Miller <millert@openbsd.org> - ,* - ,* Permission to use, copy, modify, and distribute this software for any - ,* purpose with or without fee is hereby granted, provided that the above - ,* copyright notice and this permission notice appear in all copies. - ,* - ,* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - ,* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - ,* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ,* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - ,* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ,* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - ,* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ,*/ - /*- - ,* Copyright (c) 1995,1996,1997 Berkeley Software Design, Inc. - ,* All rights reserved. - ,* - ,* Redistribution and use in source and binary forms, with or without - ,* modification, are permitted provided that the following conditions - ,* are met: - ,* 1. Redistributions of source code must retain the above copyright - ,* notice, this list of conditions and the following disclaimer. - ,* 2. Redistributions in binary form must reproduce the above copyright - ,* notice, this list of conditions and the following disclaimer in the - ,* documentation and/or other materials provided with the distribution. - ,* 3. All advertising materials mentioning features or use of this software - ,* must display the following acknowledgement: - ,* This product includes software developed by Berkeley Software Design, - ,* Inc. - ,* 4. The name of Berkeley Software Design, Inc. may not be used to endorse - ,* or promote products derived from this software without specific prior - ,* written permission. - ,* - ,* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND - ,* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - ,* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ,* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE - ,* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - ,* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - ,* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ,* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - ,* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - ,* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - ,* SUCH DAMAGE. - ,* - ,* BSDI $From: auth_subr.c,v 2.4 1999/09/08 04:10:40 prb Exp $ - ,*/ - #+end_src - @@html: </details> @@ - -#+begin_export html -<style> - details > summary { - list-style: none; - } - details > summary::-webkit-details-marker { - display: none; - } -</style> -#+end_export |