diff options
| -rw-r--r-- | content/posts/how-bsd-authentication-works/graph.dot | 67 | ||||
| -rw-r--r-- | content/posts/how-bsd-authentication-works/index.org | 230 | 
2 files changed, 297 insertions, 0 deletions
| diff --git a/content/posts/how-bsd-authentication-works/graph.dot b/content/posts/how-bsd-authentication-works/graph.dot new file mode 100644 index 0000000..a07e3ec --- /dev/null +++ b/content/posts/how-bsd-authentication-works/graph.dot @@ -0,0 +1,67 @@ +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; +    // 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 new file mode 100644 index 0000000..20b825f --- /dev/null +++ b/content/posts/how-bsd-authentication-works/index.org @@ -0,0 +1,230 @@ +#+TITLE: How BSD Authentication Works +#+DATE: 2020-06-26T18:31:36-04:00 +#+DRAFT: true +#+DESCRIPTION: +#+TAGS[]: +#+KEYWORDS[]: +#+SLUG: +#+SUMMARY: + +[[https://web.archive.org/web/20170327150148/http://www.penzin.net/bsdauth/]] + +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. + +All of the high level authentication functions are described in +=authenticate(3)=. + +~#include <bsd_auth.h>~ + +The highest level function, and easiest to use is =auth_userokay= +which 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 + +The return codes are defined inside of =login_cap.h= as + +#+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 + +- =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 =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 and returns the value returned from =auth_close=. + +=auth_usercheck= + +From there it calls a couple other functions, constructing and +filling out an =auth_session_t= struct using the =auth_set*= +functions from =auth_subr(3)=. It contains things like the user +name, login class, along with other details required to +authenticate the user. + +# FILL THIS PART OUT MORE! + +#+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 + + +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, and the user name. + +#+BEGIN_SRC c +auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s", +    auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); +#+END_SRC + +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 between 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 + +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. + +This is the integer returned by +=auth_userokay=. + +# Setting env on auth_close(as) + +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. | 
