untrusted comment: verify with openbsd-65-base.pub RWSZaRmt1LEQTx1EA3Isgvt8ZkZgBpCtyvKjj4NUrxm2/w/5Ov22iA4VbHkm83mgJogiSYLXjkh1HpE1i0s7RsEFMeeZJ3qCYwU= OpenBSD 6.5 errata 021, December 3, 2019: libc's authentication layer performed insufficient username validation. Apply by doing: signify -Vep /etc/signify/openbsd-65-base.pub -x 021_libcauth.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install libc, su and login: cd /usr/src/lib/libc make obj make make install cd /usr/src/usr.bin/su make obj make make install cd /usr/src/usr.bin/login make obj make make install Index: lib/libc/gen/auth_subr.c =================================================================== RCS file: /cvs/src/lib/libc/gen/auth_subr.c,v diff -u -p -u -r1.52 auth_subr.c --- lib/libc/gen/auth_subr.c 23 Mar 2019 17:03:00 -0000 1.52 +++ lib/libc/gen/auth_subr.c 3 Dec 2019 18:04:09 -0000 @@ -304,7 +304,7 @@ auth_challenge(auth_session_t *as) char path[PATH_MAX]; int len; - if (as == NULL || as->style == NULL || as->name == NULL) + if (as == NULL || as->style == NULL || !_auth_validuser(as->name)) return (NULL); len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style); @@ -316,7 +316,7 @@ auth_challenge(auth_session_t *as) free(as->challenge); as->challenge = NULL; - auth_call(as, path, as->style, "-s", "challenge", as->name, + 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"); @@ -476,6 +476,10 @@ auth_setitem(auth_session_t *as, auth_it 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); @@ -821,6 +825,7 @@ auth_call(auth_session_t *as, char *path 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"; Index: lib/libc/gen/authenticate.c =================================================================== RCS file: /cvs/src/lib/libc/gen/authenticate.c,v diff -u -p -u -r1.26 authenticate.c --- lib/libc/gen/authenticate.c 26 May 2016 15:51:37 -0000 1.26 +++ lib/libc/gen/authenticate.c 3 Dec 2019 01:33:38 -0000 @@ -174,6 +174,17 @@ auth_cat(char *file) DEF_WEAK(auth_cat); int +_auth_validuser(const char *name) +{ + /* User name must be specified and may not start with a '-'. */ + if (name == NULL || *name == '\0' || *name == '-') { + syslog(LOG_ERR, "invalid user name %s", name ? name : "(NULL)"); + return 0; + } + return 1; +} + +int auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type) { int close_on_exit, close_lc_on_exit, len; @@ -192,6 +203,10 @@ auth_approval(auth_session_t *as, login_ 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), @@ -217,7 +232,7 @@ auth_approval(auth_session_t *as, login_ } if (pwd == NULL && (approve = strchr(name, '.')) != NULL) { strlcpy(path, name, sizeof path); - path[approve-name] = '\0'; + path[approve - name] = '\0'; getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); } lc = login_getclass(pwd ? pwd->pw_class : NULL); @@ -290,7 +305,7 @@ auth_approval(auth_session_t *as, login_ } } if (approve) - auth_call(as, approve, strrchr(approve, '/') + 1, name, + auth_call(as, approve, strrchr(approve, '/') + 1, "--", name, lc->lc_class, type, (char *)NULL); out: @@ -314,6 +329,8 @@ auth_usercheck(char *name, char *style, 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; @@ -382,6 +399,8 @@ auth_userchallenge(char *name, char *sty 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); @@ -440,7 +459,8 @@ auth_userresponse(auth_session_t *as, ch auth_setstate(as, 0); if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL || - (name = auth_getitem(as, AUTHV_NAME)) == NULL) { + (name = auth_getitem(as, AUTHV_NAME)) == NULL || + !_auth_validuser(name)) { if (more == 0) return (auth_close(as)); return(0); @@ -466,7 +486,8 @@ auth_userresponse(auth_session_t *as, ch } else auth_setdata(as, "", 1); - auth_call(as, path, style, "-s", "response", name, class, (char *)NULL); + auth_call(as, path, style, "-s", "response", "--", name, + class, (char *)NULL); /* * If they authenticated then make sure they did not expire @@ -495,7 +516,7 @@ auth_verify(auth_session_t *as, char *st char path[PATH_MAX]; if ((name == NULL || style == NULL) && as == NULL) - return (as); + return (NULL); if (as == NULL && (as = auth_open()) == NULL) return (NULL); @@ -509,12 +530,14 @@ auth_verify(auth_session_t *as, char *st 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); + auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL); va_end(ap); return (as); } Index: lib/libc/hidden/bsd_auth.h =================================================================== RCS file: /cvs/src/lib/libc/hidden/bsd_auth.h,v diff -u -p -u -r1.1 bsd_auth.h --- lib/libc/hidden/bsd_auth.h 12 Sep 2015 15:20:14 -0000 1.1 +++ lib/libc/hidden/bsd_auth.h 3 Dec 2019 01:33:38 -0000 @@ -20,6 +20,10 @@ #include_next +__BEGIN_HIDDEN_DECLS +int _auth_validuser(const char *name); +__END_HIDDEN_DECLS + PROTO_NORMAL(auth_approval); PROTO_NORMAL(auth_call); PROTO_NORMAL(auth_cat); Index: usr.bin/su/su.c =================================================================== RCS file: /cvs/src/usr.bin/su/su.c,v diff -u -p -u -r1.73 su.c --- usr.bin/su/su.c 28 Jan 2019 01:38:06 -0000 1.73 +++ usr.bin/su/su.c 3 Dec 2019 01:33:38 -0000 @@ -149,11 +149,11 @@ main(int argc, char **argv) if (pwd == NULL) auth_errx(as, 1, "who are you?"); if ((username = strdup(pwd->pw_name)) == NULL) - auth_errx(as, 1, "can't allocate memory"); + auth_err(as, 1, NULL); if (asme && !altshell) { if (pwd->pw_shell && *pwd->pw_shell) { if ((shell = strdup(pwd->pw_shell)) == NULL) - auth_errx(as, 1, "can't allocate memory"); + auth_err(as, 1, NULL); } else { shell = _PATH_BSHELL; iscsh = NO; @@ -194,7 +194,7 @@ main(int argc, char **argv) auth_clean(as); if (auth_setitem(as, AUTHV_INTERACTIVE, "True") != 0 || auth_setitem(as, AUTHV_NAME, user) != 0) - auth_errx(as, 1, "can't allocate memory"); + auth_err(as, 1, NULL); if ((user = auth_getitem(as, AUTHV_NAME)) == NULL) auth_errx(as, 1, "internal error"); if (auth_setpwd(as, NULL) || (pwd = auth_getpwd(as)) == NULL) { @@ -223,6 +223,8 @@ main(int argc, char **argv) } fprintf(stderr, "Login incorrect\n"); } + if (pwd == NULL) + auth_errx(as, 1, "internal error"); if (pledge("stdio unveil rpath getpw exec id", NULL) == -1) err(1, "pledge"); @@ -234,7 +236,7 @@ main(int argc, char **argv) auth_errx(as, 1, "permission denied (shell)."); } else if (pwd->pw_shell && *pwd->pw_shell) { if ((shell = strdup(pwd->pw_shell)) == NULL) - auth_errx(as, 1, "can't allocate memory"); + auth_err(as, 1, NULL); iscsh = UNSET; } else { shell = _PATH_BSHELL; Index: usr.bin/login/login.c =================================================================== RCS file: /cvs/src/usr.bin/login/login.c,v diff -u -p -u -r1.70 login.c --- usr.bin/login/login.c 15 Aug 2018 19:38:47 -0000 1.70 +++ usr.bin/login/login.c 3 Dec 2019 03:45:01 -0000 @@ -340,8 +340,13 @@ main(int argc, char *argv[]) } shell = strrchr(script, '/') + 1; auth_setstate(as, AUTH_OKAY); - auth_call(as, script, shell, - fflag ? "-f" : username, fflag ? username : 0, (char *)0); + if (fflag) { + auth_call(as, script, shell, "-f", "--", username, + (char *)NULL); + } else { + auth_call(as, script, shell, "--", username, + (char *)NULL); + } if (!(auth_getstate(as) & AUTH_ALLOW)) quickexit(1); auth_setenv(as); @@ -367,7 +372,7 @@ main(int argc, char *argv[]) } /* - * Request the things like the approval script print things + * Request that things like the approval script print things * to stdout (in particular, the nologins files) */ auth_setitem(as, AUTHV_INTERACTIVE, "True");