Annotation of src/usr.bin/ssh/auth2-pubkey.c, Revision 1.68
1.68 ! djm 1: /* $OpenBSD: auth2-pubkey.c,v 1.67 2017/05/31 10:54:00 markus Exp $ */
1.1 markus 2: /*
3: * Copyright (c) 2000 Markus Friedl. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24: */
25:
1.10 stevesk 26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
1.31 djm 29: #include <sys/wait.h>
1.13 stevesk 30:
1.31 djm 31: #include <errno.h>
1.16 djm 32: #include <fcntl.h>
1.31 djm 33: #include <paths.h>
1.13 stevesk 34: #include <pwd.h>
1.31 djm 35: #include <signal.h>
1.14 stevesk 36: #include <stdio.h>
1.15 deraadt 37: #include <stdarg.h>
1.20 djm 38: #include <string.h>
39: #include <time.h>
1.17 dtucker 40: #include <unistd.h>
1.44 djm 41: #include <limits.h>
1.1 markus 42:
1.15 deraadt 43: #include "xmalloc.h"
1.8 dtucker 44: #include "ssh.h"
1.1 markus 45: #include "ssh2.h"
46: #include "packet.h"
47: #include "buffer.h"
48: #include "log.h"
1.41 millert 49: #include "misc.h"
1.1 markus 50: #include "servconf.h"
51: #include "compat.h"
1.64 markus 52: #include "sshkey.h"
1.15 deraadt 53: #include "hostfile.h"
1.1 markus 54: #include "auth.h"
55: #include "pathnames.h"
56: #include "uidswap.h"
57: #include "auth-options.h"
58: #include "canohost.h"
1.15 deraadt 59: #ifdef GSSAPI
60: #include "ssh-gss.h"
61: #endif
1.1 markus 62: #include "monitor_wrap.h"
1.21 djm 63: #include "authfile.h"
1.24 djm 64: #include "match.h"
1.50 djm 65: #include "ssherr.h"
66: #include "channels.h" /* XXX for session.h */
67: #include "session.h" /* XXX for child_set_env(); refactor? */
1.1 markus 68:
69: /* import */
70: extern ServerOptions options;
71: extern u_char *session_id2;
1.4 markus 72: extern u_int session_id2_len;
1.1 markus 73:
1.2 markus 74: static int
1.65 markus 75: userauth_pubkey(struct ssh *ssh)
1.1 markus 76: {
1.65 markus 77: Authctxt *authctxt = ssh->authctxt;
1.64 markus 78: struct sshbuf *b;
1.63 markus 79: struct sshkey *key = NULL;
1.64 markus 80: char *pkalg, *userstyle = NULL, *fp = NULL;
81: u_char *pkblob, *sig, have_sig;
82: size_t blen, slen;
83: int r, pktype;
1.1 markus 84: int authenticated = 0;
85:
86: if (!authctxt->valid) {
1.55 djm 87: debug2("%s: disabled because of invalid user", __func__);
1.1 markus 88: return 0;
89: }
1.64 markus 90: if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0)
91: fatal("%s: sshpkt_get_u8 failed: %s", __func__, ssh_err(r));
92: if (ssh->compat & SSH_BUG_PKAUTH) {
1.55 djm 93: debug2("%s: SSH_BUG_PKAUTH", __func__);
1.64 markus 94: if ((b = sshbuf_new()) == NULL)
95: fatal("%s: sshbuf_new failed", __func__);
1.1 markus 96: /* no explicit pkalg given */
97: /* so we have to extract the pkalg from the pkblob */
1.64 markus 98: /* XXX use sshbuf_from() */
99: if ((r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
100: (r = sshbuf_put(b, pkblob, blen)) != 0 ||
101: (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0)
102: fatal("%s: failed: %s", __func__, ssh_err(r));
103: sshbuf_free(b);
1.1 markus 104: } else {
1.64 markus 105: if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
106: (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
107: fatal("%s: sshpkt_get_cstring failed: %s",
108: __func__, ssh_err(r));
1.1 markus 109: }
1.64 markus 110: pktype = sshkey_type_from_name(pkalg);
1.1 markus 111: if (pktype == KEY_UNSPEC) {
112: /* this is perfectly legal */
1.55 djm 113: logit("%s: unsupported public key algorithm: %s",
114: __func__, pkalg);
1.1 markus 115: goto done;
116: }
1.64 markus 117: if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
118: error("%s: could not parse key: %s", __func__, ssh_err(r));
119: goto done;
120: }
1.1 markus 121: if (key == NULL) {
1.55 djm 122: error("%s: cannot decode key: %s", __func__, pkalg);
1.1 markus 123: goto done;
124: }
125: if (key->type != pktype) {
1.55 djm 126: error("%s: type mismatch for decoded key "
127: "(received %d, expected %d)", __func__, key->type, pktype);
1.39 djm 128: goto done;
129: }
1.64 markus 130: if (sshkey_type_plain(key->type) == KEY_RSA &&
131: (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1.39 djm 132: logit("Refusing RSA key because client uses unsafe "
133: "signature scheme");
1.1 markus 134: goto done;
135: }
1.55 djm 136: fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
1.68 ! djm 137: if (auth2_key_already_used(authctxt, key)) {
1.64 markus 138: logit("refusing previously-used %s key", sshkey_type(key));
1.44 djm 139: goto done;
140: }
1.49 djm 141: if (match_pattern_list(sshkey_ssh_name(key),
142: options.pubkey_key_types, 0) != 1) {
1.45 djm 143: logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
144: __func__, sshkey_ssh_name(key));
145: goto done;
146: }
147:
1.1 markus 148: if (have_sig) {
1.55 djm 149: debug3("%s: have signature for %s %s",
150: __func__, sshkey_type(key), fp);
1.64 markus 151: if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
152: (r = sshpkt_get_end(ssh)) != 0)
153: fatal("%s: %s", __func__, ssh_err(r));
154: if ((b = sshbuf_new()) == NULL)
155: fatal("%s: sshbuf_new failed", __func__);
156: if (ssh->compat & SSH_OLD_SESSIONID) {
157: if ((r = sshbuf_put(b, session_id2,
158: session_id2_len)) != 0)
159: fatal("%s: sshbuf_put session id: %s",
160: __func__, ssh_err(r));
1.1 markus 161: } else {
1.64 markus 162: if ((r = sshbuf_put_string(b, session_id2,
163: session_id2_len)) != 0)
164: fatal("%s: sshbuf_put_string session id: %s",
165: __func__, ssh_err(r));
1.1 markus 166: }
167: /* reconstruct packet */
1.35 djm 168: xasprintf(&userstyle, "%s%s%s", authctxt->user,
169: authctxt->style ? ":" : "",
170: authctxt->style ? authctxt->style : "");
1.64 markus 171: if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
172: (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
173: (r = sshbuf_put_cstring(b, ssh->compat & SSH_BUG_PKSERVICE ?
174: "ssh-userauth" : authctxt->service)) != 0)
175: fatal("%s: build packet failed: %s",
176: __func__, ssh_err(r));
177: if (ssh->compat & SSH_BUG_PKAUTH) {
178: if ((r = sshbuf_put_u8(b, have_sig)) != 0)
179: fatal("%s: build packet failed: %s",
180: __func__, ssh_err(r));
1.1 markus 181: } else {
1.64 markus 182: if ((r = sshbuf_put_cstring(b, "publickey")) != 0 ||
183: (r = sshbuf_put_u8(b, have_sig)) != 0 ||
184: (r = sshbuf_put_cstring(b, pkalg) != 0))
185: fatal("%s: build packet failed: %s",
186: __func__, ssh_err(r));
1.1 markus 187: }
1.64 markus 188: if ((r = sshbuf_put_string(b, pkblob, blen)) != 0)
189: fatal("%s: build packet failed: %s",
190: __func__, ssh_err(r));
1.1 markus 191: #ifdef DEBUG_PK
1.64 markus 192: sshbuf_dump(b, stderr);
1.1 markus 193: #endif
1.37 djm 194:
1.1 markus 195: /* test for correct signature */
196: authenticated = 0;
1.48 djm 197: if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
1.64 markus 198: PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
199: sshbuf_len(b), ssh->compat)) == 0) {
1.1 markus 200: authenticated = 1;
1.44 djm 201: }
1.64 markus 202: sshbuf_free(b);
1.36 djm 203: free(sig);
1.68 ! djm 204: auth2_record_key(authctxt, authenticated, key);
1.1 markus 205: } else {
1.55 djm 206: debug("%s: test whether pkalg/pkblob are acceptable for %s %s",
207: __func__, sshkey_type(key), fp);
1.64 markus 208: if ((r = sshpkt_get_end(ssh)) != 0)
209: fatal("%s: %s", __func__, ssh_err(r));
1.1 markus 210:
211: /* XXX fake reply and always send PK_OK ? */
212: /*
213: * XXX this allows testing whether a user is allowed
214: * to login: if you happen to have a valid pubkey this
215: * message is sent. the message is NEVER sent at all
216: * if a user is not allowed to login. is this an
217: * issue? -markus
218: */
1.48 djm 219: if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) {
1.64 markus 220: if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
221: != 0 ||
222: (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
223: (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
224: (r = sshpkt_send(ssh)) != 0)
225: fatal("%s: %s", __func__, ssh_err(r));
226: ssh_packet_write_wait(ssh);
1.1 markus 227: authctxt->postponed = 1;
228: }
229: }
230: if (authenticated != 1)
231: auth_clear_options();
232: done:
1.55 djm 233: debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
1.68 ! djm 234: sshkey_free(key);
1.64 markus 235: free(userstyle);
1.36 djm 236: free(pkalg);
237: free(pkblob);
1.55 djm 238: free(fp);
1.1 markus 239: return authenticated;
240: }
241:
1.50 djm 242: /*
243: * Splits 's' into an argument vector. Handles quoted string and basic
244: * escape characters (\\, \", \'). Caller must free the argument vector
245: * and its members.
246: */
247: static int
248: split_argv(const char *s, int *argcp, char ***argvp)
249: {
250: int r = SSH_ERR_INTERNAL_ERROR;
251: int argc = 0, quote, i, j;
252: char *arg, **argv = xcalloc(1, sizeof(*argv));
253:
254: *argvp = NULL;
255: *argcp = 0;
256:
257: for (i = 0; s[i] != '\0'; i++) {
258: /* Skip leading whitespace */
259: if (s[i] == ' ' || s[i] == '\t')
260: continue;
261:
262: /* Start of a token */
263: quote = 0;
264: if (s[i] == '\\' &&
265: (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\'))
266: i++;
267: else if (s[i] == '\'' || s[i] == '"')
268: quote = s[i++];
269:
270: argv = xreallocarray(argv, (argc + 2), sizeof(*argv));
271: arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
272: argv[argc] = NULL;
273:
274: /* Copy the token in, removing escapes */
275: for (j = 0; s[i] != '\0'; i++) {
276: if (s[i] == '\\') {
277: if (s[i + 1] == '\'' ||
278: s[i + 1] == '\"' ||
279: s[i + 1] == '\\') {
280: i++; /* Skip '\' */
281: arg[j++] = s[i];
282: } else {
283: /* Unrecognised escape */
284: arg[j++] = s[i];
285: }
286: } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
287: break; /* done */
288: else if (quote != 0 && s[i] == quote)
289: break; /* done */
290: else
291: arg[j++] = s[i];
292: }
293: if (s[i] == '\0') {
294: if (quote != 0) {
295: /* Ran out of string looking for close quote */
296: r = SSH_ERR_INVALID_FORMAT;
297: goto out;
298: }
299: break;
300: }
301: }
302: /* Success */
303: *argcp = argc;
304: *argvp = argv;
305: argc = 0;
306: argv = NULL;
307: r = 0;
308: out:
309: if (argc != 0 && argv != NULL) {
310: for (i = 0; i < argc; i++)
311: free(argv[i]);
312: free(argv);
313: }
314: return r;
315: }
316:
317: /*
318: * Reassemble an argument vector into a string, quoting and escaping as
319: * necessary. Caller must free returned string.
320: */
321: static char *
322: assemble_argv(int argc, char **argv)
323: {
324: int i, j, ws, r;
325: char c, *ret;
326: struct sshbuf *buf, *arg;
327:
328: if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL)
329: fatal("%s: sshbuf_new failed", __func__);
330:
331: for (i = 0; i < argc; i++) {
332: ws = 0;
333: sshbuf_reset(arg);
334: for (j = 0; argv[i][j] != '\0'; j++) {
335: r = 0;
336: c = argv[i][j];
337: switch (c) {
338: case ' ':
339: case '\t':
340: ws = 1;
341: r = sshbuf_put_u8(arg, c);
342: break;
343: case '\\':
344: case '\'':
345: case '"':
346: if ((r = sshbuf_put_u8(arg, '\\')) != 0)
347: break;
348: /* FALLTHROUGH */
349: default:
350: r = sshbuf_put_u8(arg, c);
351: break;
352: }
353: if (r != 0)
354: fatal("%s: sshbuf_put_u8: %s",
355: __func__, ssh_err(r));
356: }
357: if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) ||
358: (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) ||
359: (r = sshbuf_putb(buf, arg)) != 0 ||
360: (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0))
361: fatal("%s: buffer error: %s", __func__, ssh_err(r));
362: }
363: if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL)
364: fatal("%s: malloc failed", __func__);
365: memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf));
366: ret[sshbuf_len(buf)] = '\0';
367: sshbuf_free(buf);
368: sshbuf_free(arg);
369: return ret;
370: }
371:
372: /*
373: * Runs command in a subprocess. Returns pid on success and a FILE* to the
374: * subprocess' stdout or 0 on failure.
375: * NB. "command" is only used for logging.
376: */
377: static pid_t
378: subprocess(const char *tag, struct passwd *pw, const char *command,
379: int ac, char **av, FILE **child)
380: {
381: FILE *f;
382: struct stat st;
383: int devnull, p[2], i;
384: pid_t pid;
385: char *cp, errmsg[512];
386: u_int envsize;
387: char **child_env;
388:
389: *child = NULL;
390:
391: debug3("%s: %s command \"%s\" running as %s", __func__,
392: tag, command, pw->pw_name);
393:
394: /* Verify the path exists and is safe-ish to execute */
395: if (*av[0] != '/') {
396: error("%s path is not absolute", tag);
397: return 0;
398: }
399: temporarily_use_uid(pw);
400: if (stat(av[0], &st) < 0) {
401: error("Could not stat %s \"%s\": %s", tag,
402: av[0], strerror(errno));
403: restore_uid();
404: return 0;
405: }
406: if (auth_secure_path(av[0], &st, NULL, 0,
407: errmsg, sizeof(errmsg)) != 0) {
408: error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
409: restore_uid();
410: return 0;
411: }
412:
413: /*
414: * Run the command; stderr is left in place, stdout is the
415: * authorized_keys output.
416: */
417: if (pipe(p) != 0) {
418: error("%s: pipe: %s", tag, strerror(errno));
419: restore_uid();
420: return 0;
421: }
422:
423: /*
424: * Don't want to call this in the child, where it can fatal() and
425: * run cleanup_exit() code.
426: */
427: restore_uid();
428:
429: switch ((pid = fork())) {
430: case -1: /* error */
431: error("%s: fork: %s", tag, strerror(errno));
432: close(p[0]);
433: close(p[1]);
434: return 0;
435: case 0: /* child */
436: /* Prepare a minimal environment for the child. */
437: envsize = 5;
438: child_env = xcalloc(sizeof(*child_env), envsize);
439: child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
440: child_set_env(&child_env, &envsize, "USER", pw->pw_name);
441: child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
442: child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
443: if ((cp = getenv("LANG")) != NULL)
444: child_set_env(&child_env, &envsize, "LANG", cp);
445:
446: for (i = 0; i < NSIG; i++)
447: signal(i, SIG_DFL);
448:
449: if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
450: error("%s: open %s: %s", tag, _PATH_DEVNULL,
451: strerror(errno));
452: _exit(1);
453: }
454: /* Keep stderr around a while longer to catch errors */
455: if (dup2(devnull, STDIN_FILENO) == -1 ||
456: dup2(p[1], STDOUT_FILENO) == -1) {
457: error("%s: dup2: %s", tag, strerror(errno));
458: _exit(1);
459: }
460: closefrom(STDERR_FILENO + 1);
461:
462: /* Don't use permanently_set_uid() here to avoid fatal() */
463: if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
464: error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
465: strerror(errno));
466: _exit(1);
467: }
468: if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
469: error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
470: strerror(errno));
471: _exit(1);
472: }
473: /* stdin is pointed to /dev/null at this point */
474: if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
475: error("%s: dup2: %s", tag, strerror(errno));
476: _exit(1);
477: }
478:
479: execve(av[0], av, child_env);
480: error("%s exec \"%s\": %s", tag, command, strerror(errno));
481: _exit(127);
482: default: /* parent */
483: break;
484: }
485:
486: close(p[1]);
487: if ((f = fdopen(p[0], "r")) == NULL) {
488: error("%s: fdopen: %s", tag, strerror(errno));
489: close(p[0]);
490: /* Don't leave zombie child */
491: kill(pid, SIGTERM);
492: while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
493: ;
494: return 0;
495: }
496: /* Success */
497: debug3("%s: %s pid %ld", __func__, tag, (long)pid);
498: *child = f;
499: return pid;
500: }
501:
502: /* Returns 0 if pid exited cleanly, non-zero otherwise */
503: static int
504: exited_cleanly(pid_t pid, const char *tag, const char *cmd)
505: {
506: int status;
507:
508: while (waitpid(pid, &status, 0) == -1) {
509: if (errno != EINTR) {
510: error("%s: waitpid: %s", tag, strerror(errno));
511: return -1;
512: }
513: }
514: if (WIFSIGNALED(status)) {
515: error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status));
516: return -1;
517: } else if (WEXITSTATUS(status) != 0) {
518: error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status));
519: return -1;
520: }
521: return 0;
522: }
523:
1.24 djm 524: static int
1.40 djm 525: match_principals_option(const char *principal_list, struct sshkey_cert *cert)
1.24 djm 526: {
527: char *result;
528: u_int i;
529:
530: /* XXX percent_expand() sequences for authorized_principals? */
531:
532: for (i = 0; i < cert->nprincipals; i++) {
533: if ((result = match_list(cert->principals[i],
534: principal_list, NULL)) != NULL) {
535: debug3("matched principal from key options \"%.100s\"",
536: result);
1.36 djm 537: free(result);
1.24 djm 538: return 1;
539: }
540: }
541: return 0;
542: }
543:
544: static int
1.67 markus 545: process_principals(FILE *f, const char *file, struct passwd *pw,
1.56 djm 546: const struct sshkey_cert *cert)
1.24 djm 547: {
1.26 djm 548: char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
1.24 djm 549: u_long linenum = 0;
1.62 djm 550: u_int i, found_principal = 0;
1.24 djm 551:
552: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.62 djm 553: /* Always consume entire input */
554: if (found_principal)
555: continue;
1.26 djm 556: /* Skip leading whitespace. */
1.24 djm 557: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
558: ;
1.26 djm 559: /* Skip blank and comment lines. */
560: if ((ep = strchr(cp, '#')) != NULL)
561: *ep = '\0';
562: if (!*cp || *cp == '\n')
1.24 djm 563: continue;
1.26 djm 564: /* Trim trailing whitespace. */
565: ep = cp + strlen(cp) - 1;
566: while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
567: *ep-- = '\0';
568: /*
569: * If the line has internal whitespace then assume it has
570: * key options.
571: */
572: line_opts = NULL;
573: if ((ep = strrchr(cp, ' ')) != NULL ||
574: (ep = strrchr(cp, '\t')) != NULL) {
575: for (; *ep == ' ' || *ep == '\t'; ep++)
1.27 deraadt 576: ;
1.26 djm 577: line_opts = cp;
578: cp = ep;
579: }
1.24 djm 580: for (i = 0; i < cert->nprincipals; i++) {
581: if (strcmp(cp, cert->principals[i]) == 0) {
1.51 djm 582: debug3("%s:%lu: matched principal \"%.100s\"",
1.67 markus 583: file, linenum, cert->principals[i]);
1.26 djm 584: if (auth_parse_options(pw, line_opts,
585: file, linenum) != 1)
586: continue;
1.62 djm 587: found_principal = 1;
588: continue;
1.24 djm 589: }
590: }
591: }
1.62 djm 592: return found_principal;
1.51 djm 593: }
594:
595: static int
596: match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
597: {
598: FILE *f;
599: int success;
600:
601: temporarily_use_uid(pw);
602: debug("trying authorized principals file %s", file);
603: if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
604: restore_uid();
605: return 0;
606: }
607: success = process_principals(f, file, pw, cert);
1.24 djm 608: fclose(f);
609: restore_uid();
1.51 djm 610: return success;
1.31 djm 611: }
1.24 djm 612:
1.31 djm 613: /*
1.51 djm 614: * Checks whether principal is allowed in output of command.
615: * returns 1 if the principal is allowed or 0 otherwise.
616: */
617: static int
1.56 djm 618: match_principals_command(struct passwd *user_pw, const struct sshkey *key)
1.51 djm 619: {
1.56 djm 620: const struct sshkey_cert *cert = key->cert;
1.51 djm 621: FILE *f = NULL;
1.56 djm 622: int r, ok, found_principal = 0;
1.51 djm 623: struct passwd *pw;
624: int i, ac = 0, uid_swapped = 0;
625: pid_t pid;
626: char *tmp, *username = NULL, *command = NULL, **av = NULL;
1.56 djm 627: char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
1.58 djm 628: char serial_s[16];
1.51 djm 629: void (*osigchld)(int);
630:
631: if (options.authorized_principals_command == NULL)
632: return 0;
633: if (options.authorized_principals_command_user == NULL) {
634: error("No user for AuthorizedPrincipalsCommand specified, "
635: "skipping");
636: return 0;
637: }
638:
639: /*
640: * NB. all returns later this function should go via "out" to
641: * ensure the original SIGCHLD handler is restored properly.
642: */
643: osigchld = signal(SIGCHLD, SIG_DFL);
644:
645: /* Prepare and verify the user for the command */
646: username = percent_expand(options.authorized_principals_command_user,
647: "u", user_pw->pw_name, (char *)NULL);
648: pw = getpwnam(username);
649: if (pw == NULL) {
650: error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
651: username, strerror(errno));
652: goto out;
653: }
654:
655: /* Turn the command into an argument vector */
656: if (split_argv(options.authorized_principals_command, &ac, &av) != 0) {
657: error("AuthorizedPrincipalsCommand \"%s\" contains "
658: "invalid quotes", command);
659: goto out;
660: }
661: if (ac == 0) {
662: error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
663: command);
664: goto out;
665: }
1.56 djm 666: if ((ca_fp = sshkey_fingerprint(cert->signature_key,
667: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
668: error("%s: sshkey_fingerprint failed", __func__);
669: goto out;
670: }
1.57 djm 671: if ((key_fp = sshkey_fingerprint(key,
1.56 djm 672: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
673: error("%s: sshkey_fingerprint failed", __func__);
674: goto out;
675: }
676: if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
677: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
678: goto out;
679: }
680: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
681: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
682: goto out;
683: }
1.59 djm 684: snprintf(serial_s, sizeof(serial_s), "%llu",
685: (unsigned long long)cert->serial);
1.51 djm 686: for (i = 1; i < ac; i++) {
687: tmp = percent_expand(av[i],
688: "u", user_pw->pw_name,
689: "h", user_pw->pw_dir,
1.56 djm 690: "t", sshkey_ssh_name(key),
691: "T", sshkey_ssh_name(cert->signature_key),
692: "f", key_fp,
693: "F", ca_fp,
694: "k", keytext,
695: "K", catext,
1.58 djm 696: "i", cert->key_id,
697: "s", serial_s,
1.51 djm 698: (char *)NULL);
699: if (tmp == NULL)
700: fatal("%s: percent_expand failed", __func__);
701: free(av[i]);
702: av[i] = tmp;
703: }
704: /* Prepare a printable command for logs, etc. */
705: command = assemble_argv(ac, av);
706:
707: if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
708: ac, av, &f)) == 0)
709: goto out;
710:
711: uid_swapped = 1;
712: temporarily_use_uid(pw);
713:
1.67 markus 714: ok = process_principals(f, "(command)", pw, cert);
1.51 djm 715:
1.61 djm 716: fclose(f);
717: f = NULL;
718:
1.51 djm 719: if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0)
720: goto out;
721:
722: /* Read completed successfully */
723: found_principal = ok;
724: out:
725: if (f != NULL)
726: fclose(f);
727: signal(SIGCHLD, osigchld);
728: for (i = 0; i < ac; i++)
729: free(av[i]);
730: free(av);
731: if (uid_swapped)
732: restore_uid();
733: free(command);
734: free(username);
1.56 djm 735: free(ca_fp);
736: free(key_fp);
737: free(catext);
738: free(keytext);
1.51 djm 739: return found_principal;
740: }
741: /*
1.31 djm 742: * Checks whether key is allowed in authorized_keys-format file,
743: * returns 1 if the key is allowed or 0 otherwise.
744: */
1.1 markus 745: static int
1.64 markus 746: check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw)
1.1 markus 747: {
1.8 dtucker 748: char line[SSH_MAX_PUBKEY_BYTES];
1.18 dtucker 749: int found_key = 0;
1.1 markus 750: u_long linenum = 0;
1.64 markus 751: struct sshkey *found = NULL;
1.1 markus 752:
1.8 dtucker 753: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.60 djm 754: char *cp, *key_options = NULL, *fp = NULL;
755: const char *reason = NULL;
756:
1.62 djm 757: /* Always consume entrire file */
758: if (found_key)
759: continue;
1.37 djm 760: if (found != NULL)
1.64 markus 761: sshkey_free(found);
762: found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type);
763: if (found == NULL)
764: goto done;
1.20 djm 765: auth_clear_options();
766:
1.1 markus 767: /* Skip leading whitespace, empty and comment lines. */
768: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
769: ;
770: if (!*cp || *cp == '\n' || *cp == '#')
771: continue;
772:
1.64 markus 773: if (sshkey_read(found, &cp) != 0) {
1.1 markus 774: /* no key? check if there are options for this key */
775: int quoted = 0;
776: debug2("user_key_allowed: check options: '%s'", cp);
1.7 avsm 777: key_options = cp;
1.1 markus 778: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
779: if (*cp == '\\' && cp[1] == '"')
780: cp++; /* Skip both */
781: else if (*cp == '"')
782: quoted = !quoted;
783: }
784: /* Skip remaining whitespace. */
785: for (; *cp == ' ' || *cp == '\t'; cp++)
786: ;
1.64 markus 787: if (sshkey_read(found, &cp) != 0) {
1.1 markus 788: debug2("user_key_allowed: advance: '%s'", cp);
789: /* still no key? advance to next line*/
790: continue;
791: }
792: }
1.64 markus 793: if (sshkey_is_cert(key)) {
794: if (!sshkey_equal(found, key->cert->signature_key))
1.25 djm 795: continue;
796: if (auth_parse_options(pw, key_options, file,
797: linenum) != 1)
798: continue;
1.20 djm 799: if (!key_is_cert_authority)
800: continue;
1.46 djm 801: if ((fp = sshkey_fingerprint(found,
802: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
803: continue;
1.22 djm 804: debug("matching CA found: file %s, line %lu, %s %s",
1.64 markus 805: file, linenum, sshkey_type(found), fp);
1.24 djm 806: /*
807: * If the user has specified a list of principals as
808: * a key option, then prefer that list to matching
809: * their username in the certificate principals list.
810: */
811: if (authorized_principals != NULL &&
812: !match_principals_option(authorized_principals,
813: key->cert)) {
814: reason = "Certificate does not contain an "
815: "authorized principal";
816: fail_reason:
1.36 djm 817: free(fp);
1.20 djm 818: error("%s", reason);
819: auth_debug_add("%s", reason);
820: continue;
821: }
1.64 markus 822: if (sshkey_cert_check_authority(key, 0, 0,
1.24 djm 823: authorized_principals == NULL ? pw->pw_name : NULL,
824: &reason) != 0)
825: goto fail_reason;
1.60 djm 826: if (auth_cert_options(key, pw, &reason) != 0)
827: goto fail_reason;
1.54 djm 828: verbose("Accepted certificate ID \"%s\" (serial %llu) "
1.22 djm 829: "signed by %s CA %s via %s", key->cert->key_id,
1.54 djm 830: (unsigned long long)key->cert->serial,
1.64 markus 831: sshkey_type(found), fp, file);
1.36 djm 832: free(fp);
1.20 djm 833: found_key = 1;
834: break;
1.64 markus 835: } else if (sshkey_equal(found, key)) {
1.25 djm 836: if (auth_parse_options(pw, key_options, file,
837: linenum) != 1)
838: continue;
839: if (key_is_cert_authority)
840: continue;
1.46 djm 841: if ((fp = sshkey_fingerprint(found,
842: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
843: continue;
1.37 djm 844: debug("matching key found: file %s, line %lu %s %s",
1.64 markus 845: file, linenum, sshkey_type(found), fp);
1.36 djm 846: free(fp);
1.46 djm 847: found_key = 1;
1.62 djm 848: continue;
1.1 markus 849: }
850: }
1.64 markus 851: done:
1.37 djm 852: if (found != NULL)
1.64 markus 853: sshkey_free(found);
1.1 markus 854: if (!found_key)
855: debug2("key not found");
856: return found_key;
857: }
858:
1.21 djm 859: /* Authenticate a certificate key against TrustedUserCAKeys */
860: static int
1.63 markus 861: user_cert_trusted_ca(struct passwd *pw, struct sshkey *key)
1.21 djm 862: {
1.24 djm 863: char *ca_fp, *principals_file = NULL;
1.21 djm 864: const char *reason;
1.64 markus 865: int r, ret = 0, found_principal = 0, use_authorized_principals;
1.21 djm 866:
1.64 markus 867: if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
1.21 djm 868: return 0;
869:
1.46 djm 870: if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
871: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
872: return 0;
1.21 djm 873:
1.64 markus 874: if ((r = sshkey_in_file(key->cert->signature_key,
875: options.trusted_user_ca_keys, 1, 0)) != 0) {
876: debug2("%s: CA %s %s is not listed in %s: %s", __func__,
877: sshkey_type(key->cert->signature_key), ca_fp,
878: options.trusted_user_ca_keys, ssh_err(r));
1.21 djm 879: goto out;
880: }
1.24 djm 881: /*
882: * If AuthorizedPrincipals is in use, then compare the certificate
883: * principals against the names in that file rather than matching
884: * against the username.
885: */
886: if ((principals_file = authorized_principals_file(pw)) != NULL) {
1.51 djm 887: if (match_principals_file(principals_file, pw, key->cert))
888: found_principal = 1;
889: }
890: /* Try querying command if specified */
1.56 djm 891: if (!found_principal && match_principals_command(pw, key))
1.51 djm 892: found_principal = 1;
1.53 jsing 893: /* If principals file or command is specified, then require a match */
894: use_authorized_principals = principals_file != NULL ||
895: options.authorized_principals_command != NULL;
896: if (!found_principal && use_authorized_principals) {
1.51 djm 897: reason = "Certificate does not contain an authorized principal";
1.24 djm 898: fail_reason:
1.51 djm 899: error("%s", reason);
900: auth_debug_add("%s", reason);
901: goto out;
1.21 djm 902: }
1.64 markus 903: if (sshkey_cert_check_authority(key, 0, 1,
1.53 jsing 904: use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
1.24 djm 905: goto fail_reason;
1.60 djm 906: if (auth_cert_options(key, pw, &reason) != 0)
907: goto fail_reason;
1.21 djm 908:
1.54 djm 909: verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
910: "%s CA %s via %s", key->cert->key_id,
911: (unsigned long long)key->cert->serial,
1.64 markus 912: sshkey_type(key->cert->signature_key), ca_fp,
1.22 djm 913: options.trusted_user_ca_keys);
1.21 djm 914: ret = 1;
915:
916: out:
1.36 djm 917: free(principals_file);
918: free(ca_fp);
1.21 djm 919: return ret;
920: }
921:
1.31 djm 922: /*
923: * Checks whether key is allowed in file.
924: * returns 1 if the key is allowed or 0 otherwise.
925: */
926: static int
1.63 markus 927: user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file)
1.31 djm 928: {
929: FILE *f;
930: int found_key = 0;
931:
932: /* Temporarily use the user's uid. */
933: temporarily_use_uid(pw);
934:
935: debug("trying public key file %s", file);
936: if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
937: found_key = check_authkeys_file(f, file, key, pw);
938: fclose(f);
939: }
940:
941: restore_uid();
942: return found_key;
943: }
944:
945: /*
946: * Checks whether key is allowed in output of command.
947: * returns 1 if the key is allowed or 0 otherwise.
948: */
949: static int
1.63 markus 950: user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key)
1.31 djm 951: {
1.50 djm 952: FILE *f = NULL;
953: int r, ok, found_key = 0;
1.31 djm 954: struct passwd *pw;
1.50 djm 955: int i, uid_swapped = 0, ac = 0;
1.31 djm 956: pid_t pid;
1.50 djm 957: char *username = NULL, *key_fp = NULL, *keytext = NULL;
958: char *tmp, *command = NULL, **av = NULL;
959: void (*osigchld)(int);
1.31 djm 960:
1.50 djm 961: if (options.authorized_keys_command == NULL)
1.31 djm 962: return 0;
1.32 djm 963: if (options.authorized_keys_command_user == NULL) {
964: error("No user for AuthorizedKeysCommand specified, skipping");
965: return 0;
966: }
967:
1.50 djm 968: /*
969: * NB. all returns later this function should go via "out" to
970: * ensure the original SIGCHLD handler is restored properly.
971: */
972: osigchld = signal(SIGCHLD, SIG_DFL);
973:
974: /* Prepare and verify the user for the command */
1.32 djm 975: username = percent_expand(options.authorized_keys_command_user,
976: "u", user_pw->pw_name, (char *)NULL);
977: pw = getpwnam(username);
978: if (pw == NULL) {
1.34 djm 979: error("AuthorizedKeysCommandUser \"%s\" not found: %s",
980: username, strerror(errno));
1.50 djm 981: goto out;
1.31 djm 982: }
983:
1.50 djm 984: /* Prepare AuthorizedKeysCommand */
985: if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
986: SSH_FP_DEFAULT)) == NULL) {
987: error("%s: sshkey_fingerprint failed", __func__);
1.31 djm 988: goto out;
989: }
1.50 djm 990: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
991: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
1.31 djm 992: goto out;
993: }
994:
1.50 djm 995: /* Turn the command into an argument vector */
996: if (split_argv(options.authorized_keys_command, &ac, &av) != 0) {
997: error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
998: command);
999: goto out;
1000: }
1001: if (ac == 0) {
1002: error("AuthorizedKeysCommand \"%s\" yielded no arguments",
1003: command);
1.31 djm 1004: goto out;
1005: }
1.50 djm 1006: for (i = 1; i < ac; i++) {
1007: tmp = percent_expand(av[i],
1008: "u", user_pw->pw_name,
1009: "h", user_pw->pw_dir,
1010: "t", sshkey_ssh_name(key),
1011: "f", key_fp,
1012: "k", keytext,
1013: (char *)NULL);
1014: if (tmp == NULL)
1015: fatal("%s: percent_expand failed", __func__);
1016: free(av[i]);
1017: av[i] = tmp;
1018: }
1019: /* Prepare a printable command for logs, etc. */
1020: command = assemble_argv(ac, av);
1.31 djm 1021:
1022: /*
1.50 djm 1023: * If AuthorizedKeysCommand was run without arguments
1024: * then fall back to the old behaviour of passing the
1025: * target username as a single argument.
1.31 djm 1026: */
1.50 djm 1027: if (ac == 1) {
1028: av = xreallocarray(av, ac + 2, sizeof(*av));
1029: av[1] = xstrdup(user_pw->pw_name);
1030: av[2] = NULL;
1031: /* Fix up command too, since it is used in log messages */
1032: free(command);
1033: xasprintf(&command, "%s %s", av[0], av[1]);
1034: }
1.31 djm 1035:
1.50 djm 1036: if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
1037: ac, av, &f)) == 0)
1038: goto out;
1.31 djm 1039:
1.50 djm 1040: uid_swapped = 1;
1.31 djm 1041: temporarily_use_uid(pw);
1042:
1043: ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
1.61 djm 1044:
1045: fclose(f);
1046: f = NULL;
1.31 djm 1047:
1.50 djm 1048: if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0)
1.31 djm 1049: goto out;
1.50 djm 1050:
1051: /* Read completed successfully */
1.31 djm 1052: found_key = ok;
1053: out:
1.50 djm 1054: if (f != NULL)
1055: fclose(f);
1056: signal(SIGCHLD, osigchld);
1057: for (i = 0; i < ac; i++)
1058: free(av[i]);
1059: free(av);
1060: if (uid_swapped)
1061: restore_uid();
1062: free(command);
1063: free(username);
1064: free(key_fp);
1065: free(keytext);
1.31 djm 1066: return found_key;
1067: }
1068:
1069: /*
1070: * Check whether key authenticates and authorises the user.
1071: */
1.1 markus 1072: int
1.63 markus 1073: user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt)
1.1 markus 1074: {
1.29 djm 1075: u_int success, i;
1.1 markus 1076: char *file;
1.21 djm 1077:
1078: if (auth_key_is_revoked(key))
1079: return 0;
1.64 markus 1080: if (sshkey_is_cert(key) &&
1081: auth_key_is_revoked(key->cert->signature_key))
1.21 djm 1082: return 0;
1083:
1084: success = user_cert_trusted_ca(pw, key);
1085: if (success)
1086: return success;
1.1 markus 1087:
1.31 djm 1088: success = user_key_command_allowed2(pw, key);
1089: if (success > 0)
1090: return success;
1091:
1.29 djm 1092: for (i = 0; !success && i < options.num_authkeys_files; i++) {
1.31 djm 1093:
1094: if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1095: continue;
1.29 djm 1096: file = expand_authorized_keys(
1097: options.authorized_keys_files[i], pw);
1.31 djm 1098:
1.29 djm 1099: success = user_key_allowed2(pw, key, file);
1.36 djm 1100: free(file);
1.29 djm 1101: }
1.1 markus 1102:
1103: return success;
1104: }
1.2 markus 1105:
1106: Authmethod method_pubkey = {
1107: "publickey",
1108: userauth_pubkey,
1109: &options.pubkey_authentication
1110: };