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