Annotation of src/usr.bin/ssh/auth2-pubkey.c, Revision 1.51
1.51 ! djm 1: /* $OpenBSD: auth2-pubkey.c,v 1.50 2015/05/21 06:38:35 djm 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
! 624: match_principals_command(struct passwd *user_pw, struct sshkey *key)
! 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:
! 689: ok = process_principals(f, NULL, pw, key->cert);
! 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: }
796: verbose("Accepted certificate ID \"%s\" "
797: "signed by %s CA %s via %s", key->cert->key_id,
798: key_type(found), fp, file);
1.36 djm 799: free(fp);
1.20 djm 800: found_key = 1;
801: break;
1.25 djm 802: } else if (key_equal(found, key)) {
803: if (auth_parse_options(pw, key_options, file,
804: linenum) != 1)
805: continue;
806: if (key_is_cert_authority)
807: continue;
1.46 djm 808: if ((fp = sshkey_fingerprint(found,
809: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
810: continue;
1.37 djm 811: debug("matching key found: file %s, line %lu %s %s",
812: file, linenum, key_type(found), fp);
1.36 djm 813: free(fp);
1.46 djm 814: found_key = 1;
1.1 markus 815: break;
816: }
817: }
1.37 djm 818: if (found != NULL)
819: key_free(found);
1.1 markus 820: if (!found_key)
821: debug2("key not found");
822: return found_key;
823: }
824:
1.21 djm 825: /* Authenticate a certificate key against TrustedUserCAKeys */
826: static int
827: user_cert_trusted_ca(struct passwd *pw, Key *key)
828: {
1.24 djm 829: char *ca_fp, *principals_file = NULL;
1.21 djm 830: const char *reason;
1.51 ! djm 831: int ret = 0, found_principal = 0;
1.21 djm 832:
833: if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
834: return 0;
835:
1.46 djm 836: if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
837: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
838: return 0;
1.21 djm 839:
1.42 djm 840: if (sshkey_in_file(key->cert->signature_key,
841: options.trusted_user_ca_keys, 1, 0) != 0) {
1.21 djm 842: debug2("%s: CA %s %s is not listed in %s", __func__,
843: key_type(key->cert->signature_key), ca_fp,
844: options.trusted_user_ca_keys);
845: goto out;
846: }
1.24 djm 847: /*
848: * If AuthorizedPrincipals is in use, then compare the certificate
849: * principals against the names in that file rather than matching
850: * against the username.
851: */
852: if ((principals_file = authorized_principals_file(pw)) != NULL) {
1.51 ! djm 853: if (match_principals_file(principals_file, pw, key->cert))
! 854: found_principal = 1;
! 855: }
! 856: /* Try querying command if specified */
! 857: if (!found_principal && match_principals_command(pw, key))
! 858: found_principal = 1;
! 859: /* If principals file or command specify, then require a match here */
! 860: if (!found_principal && (principals_file != NULL ||
! 861: options.authorized_principals_command != NULL)) {
! 862: reason = "Certificate does not contain an authorized principal";
1.24 djm 863: fail_reason:
1.51 ! djm 864: error("%s", reason);
! 865: auth_debug_add("%s", reason);
! 866: goto out;
1.21 djm 867: }
1.24 djm 868: if (key_cert_check_authority(key, 0, 1,
869: principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
870: goto fail_reason;
1.23 djm 871: if (auth_cert_options(key, pw) != 0)
1.21 djm 872: goto out;
873:
1.22 djm 874: verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
875: key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
876: options.trusted_user_ca_keys);
1.21 djm 877: ret = 1;
878:
879: out:
1.36 djm 880: free(principals_file);
881: free(ca_fp);
1.21 djm 882: return ret;
883: }
884:
1.31 djm 885: /*
886: * Checks whether key is allowed in file.
887: * returns 1 if the key is allowed or 0 otherwise.
888: */
889: static int
890: user_key_allowed2(struct passwd *pw, Key *key, char *file)
891: {
892: FILE *f;
893: int found_key = 0;
894:
895: /* Temporarily use the user's uid. */
896: temporarily_use_uid(pw);
897:
898: debug("trying public key file %s", file);
899: if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
900: found_key = check_authkeys_file(f, file, key, pw);
901: fclose(f);
902: }
903:
904: restore_uid();
905: return found_key;
906: }
907:
908: /*
909: * Checks whether key is allowed in output of command.
910: * returns 1 if the key is allowed or 0 otherwise.
911: */
912: static int
913: user_key_command_allowed2(struct passwd *user_pw, Key *key)
914: {
1.50 djm 915: FILE *f = NULL;
916: int r, ok, found_key = 0;
1.31 djm 917: struct passwd *pw;
1.50 djm 918: int i, uid_swapped = 0, ac = 0;
1.31 djm 919: pid_t pid;
1.50 djm 920: char *username = NULL, *key_fp = NULL, *keytext = NULL;
921: char *tmp, *command = NULL, **av = NULL;
922: void (*osigchld)(int);
1.31 djm 923:
1.50 djm 924: if (options.authorized_keys_command == NULL)
1.31 djm 925: return 0;
1.32 djm 926: if (options.authorized_keys_command_user == NULL) {
927: error("No user for AuthorizedKeysCommand specified, skipping");
928: return 0;
929: }
930:
1.50 djm 931: /*
932: * NB. all returns later this function should go via "out" to
933: * ensure the original SIGCHLD handler is restored properly.
934: */
935: osigchld = signal(SIGCHLD, SIG_DFL);
936:
937: /* Prepare and verify the user for the command */
1.32 djm 938: username = percent_expand(options.authorized_keys_command_user,
939: "u", user_pw->pw_name, (char *)NULL);
940: pw = getpwnam(username);
941: if (pw == NULL) {
1.34 djm 942: error("AuthorizedKeysCommandUser \"%s\" not found: %s",
943: username, strerror(errno));
1.50 djm 944: goto out;
1.31 djm 945: }
946:
1.50 djm 947: /* Prepare AuthorizedKeysCommand */
948: if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
949: SSH_FP_DEFAULT)) == NULL) {
950: error("%s: sshkey_fingerprint failed", __func__);
1.31 djm 951: goto out;
952: }
1.50 djm 953: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
954: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
1.31 djm 955: goto out;
956: }
957:
1.50 djm 958: /* Turn the command into an argument vector */
959: if (split_argv(options.authorized_keys_command, &ac, &av) != 0) {
960: error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
961: command);
962: goto out;
963: }
964: if (ac == 0) {
965: error("AuthorizedKeysCommand \"%s\" yielded no arguments",
966: command);
1.31 djm 967: goto out;
968: }
1.50 djm 969: for (i = 1; i < ac; i++) {
970: tmp = percent_expand(av[i],
971: "u", user_pw->pw_name,
972: "h", user_pw->pw_dir,
973: "t", sshkey_ssh_name(key),
974: "f", key_fp,
975: "k", keytext,
976: (char *)NULL);
977: if (tmp == NULL)
978: fatal("%s: percent_expand failed", __func__);
979: free(av[i]);
980: av[i] = tmp;
981: }
982: /* Prepare a printable command for logs, etc. */
983: command = assemble_argv(ac, av);
1.31 djm 984:
985: /*
1.50 djm 986: * If AuthorizedKeysCommand was run without arguments
987: * then fall back to the old behaviour of passing the
988: * target username as a single argument.
1.31 djm 989: */
1.50 djm 990: if (ac == 1) {
991: av = xreallocarray(av, ac + 2, sizeof(*av));
992: av[1] = xstrdup(user_pw->pw_name);
993: av[2] = NULL;
994: /* Fix up command too, since it is used in log messages */
995: free(command);
996: xasprintf(&command, "%s %s", av[0], av[1]);
997: }
1.31 djm 998:
1.50 djm 999: if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
1000: ac, av, &f)) == 0)
1001: goto out;
1.31 djm 1002:
1.50 djm 1003: uid_swapped = 1;
1.31 djm 1004: temporarily_use_uid(pw);
1005:
1006: ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
1007:
1.50 djm 1008: if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0)
1.31 djm 1009: goto out;
1.50 djm 1010:
1011: /* Read completed successfully */
1.31 djm 1012: found_key = ok;
1013: out:
1.50 djm 1014: if (f != NULL)
1015: fclose(f);
1016: signal(SIGCHLD, osigchld);
1017: for (i = 0; i < ac; i++)
1018: free(av[i]);
1019: free(av);
1020: if (uid_swapped)
1021: restore_uid();
1022: free(command);
1023: free(username);
1024: free(key_fp);
1025: free(keytext);
1.31 djm 1026: return found_key;
1027: }
1028:
1029: /*
1030: * Check whether key authenticates and authorises the user.
1031: */
1.1 markus 1032: int
1.48 djm 1033: user_key_allowed(struct passwd *pw, Key *key, int auth_attempt)
1.1 markus 1034: {
1.29 djm 1035: u_int success, i;
1.1 markus 1036: char *file;
1.21 djm 1037:
1038: if (auth_key_is_revoked(key))
1039: return 0;
1040: if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
1041: return 0;
1042:
1043: success = user_cert_trusted_ca(pw, key);
1044: if (success)
1045: return success;
1.1 markus 1046:
1.31 djm 1047: success = user_key_command_allowed2(pw, key);
1048: if (success > 0)
1049: return success;
1050:
1.29 djm 1051: for (i = 0; !success && i < options.num_authkeys_files; i++) {
1.31 djm 1052:
1053: if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1054: continue;
1.29 djm 1055: file = expand_authorized_keys(
1056: options.authorized_keys_files[i], pw);
1.31 djm 1057:
1.29 djm 1058: success = user_key_allowed2(pw, key, file);
1.36 djm 1059: free(file);
1.29 djm 1060: }
1.1 markus 1061:
1062: return success;
1.44 djm 1063: }
1064:
1065: /* Records a public key in the list of previously-successful keys */
1066: void
1067: auth2_record_userkey(Authctxt *authctxt, struct sshkey *key)
1068: {
1069: struct sshkey **tmp;
1070:
1071: if (authctxt->nprev_userkeys >= INT_MAX ||
1072: (tmp = reallocarray(authctxt->prev_userkeys,
1073: authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL)
1074: fatal("%s: reallocarray failed", __func__);
1075: authctxt->prev_userkeys = tmp;
1076: authctxt->prev_userkeys[authctxt->nprev_userkeys] = key;
1077: authctxt->nprev_userkeys++;
1078: }
1079:
1080: /* Checks whether a key has already been used successfully for authentication */
1081: int
1082: auth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key)
1083: {
1084: u_int i;
1085:
1086: for (i = 0; i < authctxt->nprev_userkeys; i++) {
1087: if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) {
1088: return 1;
1089: }
1090: }
1091: return 0;
1.1 markus 1092: }
1.2 markus 1093:
1094: Authmethod method_pubkey = {
1095: "publickey",
1096: userauth_pubkey,
1097: &options.pubkey_authentication
1098: };