summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDante Catalfamo2021-05-28 15:46:38 -0400
committerDante Catalfamo2021-05-28 15:46:38 -0400
commitaae4ca4e9a36280c3dd9b9d753f181eddbd12ce7 (patch)
tree06f40bfcd728e3c39a8afc5503c2a05d63700c35
parent55433eb5096f01b86e01e0bb76e353e8c7dfc18f (diff)
downloadblog-aae4ca4e9a36280c3dd9b9d753f181eddbd12ce7.tar.gz
blog-aae4ca4e9a36280c3dd9b9d753f181eddbd12ce7.tar.bz2
blog-aae4ca4e9a36280c3dd9b9d753f181eddbd12ce7.zip
bsd-auth: add remaining function definitions from those mentioned
-rw-r--r--content/posts/WIP-how-bsd-authentication-works/index.org624
1 files changed, 618 insertions, 6 deletions
diff --git a/content/posts/WIP-how-bsd-authentication-works/index.org b/content/posts/WIP-how-bsd-authentication-works/index.org
index 4090a95..5d4843d 100644
--- a/content/posts/WIP-how-bsd-authentication-works/index.org
+++ b/content/posts/WIP-how-bsd-authentication-works/index.org
@@ -601,10 +601,32 @@
: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> @@
=auth_clrenv= removes all lines containing =BI_SETENV= and
=BI_UNSETENV= from =as->spool=. This is explained under the
@@ -614,10 +636,62 @@
: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> @@
=auth_setenv= scans through =as->spool=, modifying the environment
according to =BI_SETENV= and =BI_UNSETENV= instructions.
@@ -626,10 +700,87 @@
: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> @@
=auth_getvalue= scans =as->spool= looking for lines beginning with
=BI_VALUE=. It then checks if the next word is equal to =what=.
@@ -646,10 +797,24 @@
: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> @@
=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
@@ -667,9 +832,70 @@
: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> @@
=auth_usercheck= first checks that =*name= is a valid username. This
means that it doesn't begin with a hyphen, had a non-zero length.
@@ -720,10 +946,44 @@
: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> @@
=auth_verify= is used as a frontend for [[#auth_call][=auth_call=]].
@@ -768,10 +1028,204 @@
: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> @@
=auth_call= is responsible for setting up the environment,
calling the modules, and communicating with them.
@@ -1077,9 +1531,79 @@
: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> @@
=auth_close= is responsible for setting the environment variables,
removing any files requested by the authentication module, and
@@ -1121,9 +1645,66 @@
: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> @@
=auth_userchallenge= is used when the authentication style requires
that the user be presented with a challenge, but the user cannot be
@@ -1176,9 +1757,39 @@
: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> @@
=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
@@ -1280,6 +1891,7 @@
}
#+end_src
@@html: </details> @@
+
=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=]].