Annotation of src/usr.bin/ssh/auth2-pubkey.c, Revision 1.32
1.32 ! djm 1: /* $OpenBSD: auth2-pubkey.c,v 1.31 2012/10/30 21:29:54 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.1 markus 41:
1.15 deraadt 42: #include "xmalloc.h"
1.8 dtucker 43: #include "ssh.h"
1.1 markus 44: #include "ssh2.h"
45: #include "packet.h"
46: #include "buffer.h"
47: #include "log.h"
48: #include "servconf.h"
49: #include "compat.h"
1.15 deraadt 50: #include "key.h"
51: #include "hostfile.h"
1.1 markus 52: #include "auth.h"
53: #include "pathnames.h"
54: #include "uidswap.h"
55: #include "auth-options.h"
56: #include "canohost.h"
1.15 deraadt 57: #ifdef GSSAPI
58: #include "ssh-gss.h"
59: #endif
1.1 markus 60: #include "monitor_wrap.h"
1.9 dtucker 61: #include "misc.h"
1.21 djm 62: #include "authfile.h"
1.24 djm 63: #include "match.h"
1.1 markus 64:
65: /* import */
66: extern ServerOptions options;
67: extern u_char *session_id2;
1.4 markus 68: extern u_int session_id2_len;
1.1 markus 69:
1.2 markus 70: static int
1.1 markus 71: userauth_pubkey(Authctxt *authctxt)
72: {
73: Buffer b;
74: Key *key = NULL;
75: char *pkalg;
76: u_char *pkblob, *sig;
77: u_int alen, blen, slen;
78: int have_sig, pktype;
79: int authenticated = 0;
80:
81: if (!authctxt->valid) {
82: debug2("userauth_pubkey: disabled because of invalid user");
83: return 0;
84: }
85: have_sig = packet_get_char();
86: if (datafellows & SSH_BUG_PKAUTH) {
87: debug2("userauth_pubkey: SSH_BUG_PKAUTH");
88: /* no explicit pkalg given */
89: pkblob = packet_get_string(&blen);
90: buffer_init(&b);
91: buffer_append(&b, pkblob, blen);
92: /* so we have to extract the pkalg from the pkblob */
93: pkalg = buffer_get_string(&b, &alen);
94: buffer_free(&b);
95: } else {
96: pkalg = packet_get_string(&alen);
97: pkblob = packet_get_string(&blen);
98: }
99: pktype = key_type_from_name(pkalg);
100: if (pktype == KEY_UNSPEC) {
101: /* this is perfectly legal */
1.3 itojun 102: logit("userauth_pubkey: unsupported public key algorithm: %s",
1.1 markus 103: pkalg);
104: goto done;
105: }
106: key = key_from_blob(pkblob, blen);
107: if (key == NULL) {
108: error("userauth_pubkey: cannot decode key: %s", pkalg);
109: goto done;
110: }
111: if (key->type != pktype) {
112: error("userauth_pubkey: type mismatch for decoded key "
113: "(received %d, expected %d)", key->type, pktype);
114: goto done;
115: }
116: if (have_sig) {
117: sig = packet_get_string(&slen);
118: packet_check_eom();
119: buffer_init(&b);
120: if (datafellows & SSH_OLD_SESSIONID) {
121: buffer_append(&b, session_id2, session_id2_len);
122: } else {
123: buffer_put_string(&b, session_id2, session_id2_len);
124: }
125: /* reconstruct packet */
126: buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
127: buffer_put_cstring(&b, authctxt->user);
128: buffer_put_cstring(&b,
129: datafellows & SSH_BUG_PKSERVICE ?
130: "ssh-userauth" :
131: authctxt->service);
132: if (datafellows & SSH_BUG_PKAUTH) {
133: buffer_put_char(&b, have_sig);
134: } else {
135: buffer_put_cstring(&b, "publickey");
136: buffer_put_char(&b, have_sig);
137: buffer_put_cstring(&b, pkalg);
138: }
139: buffer_put_string(&b, pkblob, blen);
140: #ifdef DEBUG_PK
141: buffer_dump(&b);
142: #endif
143: /* test for correct signature */
144: authenticated = 0;
145: if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
146: PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
1.6 markus 147: buffer_len(&b))) == 1)
1.1 markus 148: authenticated = 1;
1.6 markus 149: buffer_free(&b);
1.1 markus 150: xfree(sig);
151: } else {
152: debug("test whether pkalg/pkblob are acceptable");
153: packet_check_eom();
154:
155: /* XXX fake reply and always send PK_OK ? */
156: /*
157: * XXX this allows testing whether a user is allowed
158: * to login: if you happen to have a valid pubkey this
159: * message is sent. the message is NEVER sent at all
160: * if a user is not allowed to login. is this an
161: * issue? -markus
162: */
163: if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
164: packet_start(SSH2_MSG_USERAUTH_PK_OK);
165: packet_put_string(pkalg, alen);
166: packet_put_string(pkblob, blen);
167: packet_send();
168: packet_write_wait();
169: authctxt->postponed = 1;
170: }
171: }
172: if (authenticated != 1)
173: auth_clear_options();
174: done:
175: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
176: if (key != NULL)
177: key_free(key);
178: xfree(pkalg);
179: xfree(pkblob);
180: return authenticated;
181: }
182:
1.24 djm 183: static int
184: match_principals_option(const char *principal_list, struct KeyCert *cert)
185: {
186: char *result;
187: u_int i;
188:
189: /* XXX percent_expand() sequences for authorized_principals? */
190:
191: for (i = 0; i < cert->nprincipals; i++) {
192: if ((result = match_list(cert->principals[i],
193: principal_list, NULL)) != NULL) {
194: debug3("matched principal from key options \"%.100s\"",
195: result);
196: xfree(result);
197: return 1;
198: }
199: }
200: return 0;
201: }
202:
203: static int
1.26 djm 204: match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
1.24 djm 205: {
206: FILE *f;
1.26 djm 207: char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
1.24 djm 208: u_long linenum = 0;
209: u_int i;
210:
211: temporarily_use_uid(pw);
212: debug("trying authorized principals file %s", file);
213: if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
214: restore_uid();
215: return 0;
216: }
217: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.26 djm 218: /* Skip leading whitespace. */
1.24 djm 219: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
220: ;
1.26 djm 221: /* Skip blank and comment lines. */
222: if ((ep = strchr(cp, '#')) != NULL)
223: *ep = '\0';
224: if (!*cp || *cp == '\n')
1.24 djm 225: continue;
1.26 djm 226: /* Trim trailing whitespace. */
227: ep = cp + strlen(cp) - 1;
228: while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
229: *ep-- = '\0';
230: /*
231: * If the line has internal whitespace then assume it has
232: * key options.
233: */
234: line_opts = NULL;
235: if ((ep = strrchr(cp, ' ')) != NULL ||
236: (ep = strrchr(cp, '\t')) != NULL) {
237: for (; *ep == ' ' || *ep == '\t'; ep++)
1.27 deraadt 238: ;
1.26 djm 239: line_opts = cp;
240: cp = ep;
241: }
1.24 djm 242: for (i = 0; i < cert->nprincipals; i++) {
243: if (strcmp(cp, cert->principals[i]) == 0) {
1.30 djm 244: debug3("matched principal \"%.100s\" "
245: "from file \"%s\" on line %lu",
1.31 djm 246: cert->principals[i], file, linenum);
1.26 djm 247: if (auth_parse_options(pw, line_opts,
248: file, linenum) != 1)
249: continue;
1.24 djm 250: fclose(f);
251: restore_uid();
252: return 1;
253: }
254: }
255: }
256: fclose(f);
257: restore_uid();
258: return 0;
1.31 djm 259: }
1.24 djm 260:
1.31 djm 261: /*
262: * Checks whether key is allowed in authorized_keys-format file,
263: * returns 1 if the key is allowed or 0 otherwise.
264: */
1.1 markus 265: static int
1.31 djm 266: check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
1.1 markus 267: {
1.8 dtucker 268: char line[SSH_MAX_PUBKEY_BYTES];
1.20 djm 269: const char *reason;
1.18 dtucker 270: int found_key = 0;
1.1 markus 271: u_long linenum = 0;
272: Key *found;
273: char *fp;
274:
275: found_key = 0;
1.20 djm 276: found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
1.1 markus 277:
1.8 dtucker 278: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.7 avsm 279: char *cp, *key_options = NULL;
1.8 dtucker 280:
1.20 djm 281: auth_clear_options();
282:
1.1 markus 283: /* Skip leading whitespace, empty and comment lines. */
284: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
285: ;
286: if (!*cp || *cp == '\n' || *cp == '#')
287: continue;
288:
289: if (key_read(found, &cp) != 1) {
290: /* no key? check if there are options for this key */
291: int quoted = 0;
292: debug2("user_key_allowed: check options: '%s'", cp);
1.7 avsm 293: key_options = cp;
1.1 markus 294: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
295: if (*cp == '\\' && cp[1] == '"')
296: cp++; /* Skip both */
297: else if (*cp == '"')
298: quoted = !quoted;
299: }
300: /* Skip remaining whitespace. */
301: for (; *cp == ' ' || *cp == '\t'; cp++)
302: ;
303: if (key_read(found, &cp) != 1) {
304: debug2("user_key_allowed: advance: '%s'", cp);
305: /* still no key? advance to next line*/
306: continue;
307: }
308: }
1.23 djm 309: if (key_is_cert(key)) {
1.25 djm 310: if (!key_equal(found, key->cert->signature_key))
311: continue;
312: if (auth_parse_options(pw, key_options, file,
313: linenum) != 1)
314: continue;
1.20 djm 315: if (!key_is_cert_authority)
316: continue;
317: fp = key_fingerprint(found, SSH_FP_MD5,
318: SSH_FP_HEX);
1.22 djm 319: debug("matching CA found: file %s, line %lu, %s %s",
320: file, linenum, key_type(found), fp);
1.24 djm 321: /*
322: * If the user has specified a list of principals as
323: * a key option, then prefer that list to matching
324: * their username in the certificate principals list.
325: */
326: if (authorized_principals != NULL &&
327: !match_principals_option(authorized_principals,
328: key->cert)) {
329: reason = "Certificate does not contain an "
330: "authorized principal";
331: fail_reason:
1.22 djm 332: xfree(fp);
1.20 djm 333: error("%s", reason);
334: auth_debug_add("%s", reason);
335: continue;
336: }
1.24 djm 337: if (key_cert_check_authority(key, 0, 0,
338: authorized_principals == NULL ? pw->pw_name : NULL,
339: &reason) != 0)
340: goto fail_reason;
1.23 djm 341: if (auth_cert_options(key, pw) != 0) {
1.22 djm 342: xfree(fp);
1.20 djm 343: continue;
1.22 djm 344: }
345: verbose("Accepted certificate ID \"%s\" "
346: "signed by %s CA %s via %s", key->cert->key_id,
347: key_type(found), fp, file);
348: xfree(fp);
1.20 djm 349: found_key = 1;
350: break;
1.25 djm 351: } else if (key_equal(found, key)) {
352: if (auth_parse_options(pw, key_options, file,
353: linenum) != 1)
354: continue;
355: if (key_is_cert_authority)
356: continue;
1.1 markus 357: found_key = 1;
358: debug("matching key found: file %s, line %lu",
359: file, linenum);
360: fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
361: verbose("Found matching %s key: %s",
362: key_type(found), fp);
363: xfree(fp);
364: break;
365: }
366: }
367: key_free(found);
368: if (!found_key)
369: debug2("key not found");
370: return found_key;
371: }
372:
1.21 djm 373: /* Authenticate a certificate key against TrustedUserCAKeys */
374: static int
375: user_cert_trusted_ca(struct passwd *pw, Key *key)
376: {
1.24 djm 377: char *ca_fp, *principals_file = NULL;
1.21 djm 378: const char *reason;
379: int ret = 0;
380:
381: if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
382: return 0;
383:
1.22 djm 384: ca_fp = key_fingerprint(key->cert->signature_key,
385: SSH_FP_MD5, SSH_FP_HEX);
1.21 djm 386:
387: if (key_in_file(key->cert->signature_key,
388: options.trusted_user_ca_keys, 1) != 1) {
389: debug2("%s: CA %s %s is not listed in %s", __func__,
390: key_type(key->cert->signature_key), ca_fp,
391: options.trusted_user_ca_keys);
392: goto out;
393: }
1.24 djm 394: /*
395: * If AuthorizedPrincipals is in use, then compare the certificate
396: * principals against the names in that file rather than matching
397: * against the username.
398: */
399: if ((principals_file = authorized_principals_file(pw)) != NULL) {
400: if (!match_principals_file(principals_file, pw, key->cert)) {
401: reason = "Certificate does not contain an "
402: "authorized principal";
403: fail_reason:
404: error("%s", reason);
405: auth_debug_add("%s", reason);
406: goto out;
407: }
1.21 djm 408: }
1.24 djm 409: if (key_cert_check_authority(key, 0, 1,
410: principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
411: goto fail_reason;
1.23 djm 412: if (auth_cert_options(key, pw) != 0)
1.21 djm 413: goto out;
414:
1.22 djm 415: verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
416: key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
417: options.trusted_user_ca_keys);
1.21 djm 418: ret = 1;
419:
420: out:
1.24 djm 421: if (principals_file != NULL)
422: xfree(principals_file);
1.21 djm 423: if (ca_fp != NULL)
424: xfree(ca_fp);
425: return ret;
426: }
427:
1.31 djm 428: /*
429: * Checks whether key is allowed in file.
430: * returns 1 if the key is allowed or 0 otherwise.
431: */
432: static int
433: user_key_allowed2(struct passwd *pw, Key *key, char *file)
434: {
435: FILE *f;
436: int found_key = 0;
437:
438: /* Temporarily use the user's uid. */
439: temporarily_use_uid(pw);
440:
441: debug("trying public key file %s", file);
442: if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
443: found_key = check_authkeys_file(f, file, key, pw);
444: fclose(f);
445: }
446:
447: restore_uid();
448: return found_key;
449: }
450:
451: /*
452: * Checks whether key is allowed in output of command.
453: * returns 1 if the key is allowed or 0 otherwise.
454: */
455: static int
456: user_key_command_allowed2(struct passwd *user_pw, Key *key)
457: {
458: FILE *f;
459: int ok, found_key = 0;
460: struct passwd *pw;
461: struct stat st;
462: int status, devnull, p[2], i;
463: pid_t pid;
1.32 ! djm 464: char *username, errmsg[512];
1.31 djm 465:
466: if (options.authorized_keys_command == NULL ||
467: options.authorized_keys_command[0] != '/')
468: return 0;
469:
1.32 ! djm 470: if (options.authorized_keys_command_user == NULL) {
! 471: error("No user for AuthorizedKeysCommand specified, skipping");
! 472: return 0;
! 473: }
! 474:
! 475: username = percent_expand(options.authorized_keys_command_user,
! 476: "u", user_pw->pw_name, (char *)NULL);
! 477: pw = getpwnam(username);
! 478: if (pw == NULL) {
! 479: error("AuthorizedKeyCommandUser \"%s\" not found: %s",
! 480: options.authorized_keys_command, strerror(errno));
! 481: free(username);
! 482: return 0;
1.31 djm 483: }
1.32 ! djm 484: free(username);
1.31 djm 485:
486: temporarily_use_uid(pw);
487:
488: if (stat(options.authorized_keys_command, &st) < 0) {
489: error("Could not stat AuthorizedKeysCommand \"%s\": %s",
490: options.authorized_keys_command, strerror(errno));
491: goto out;
492: }
493: if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
494: errmsg, sizeof(errmsg)) != 0) {
495: error("Unsafe AuthorizedKeysCommand: %s", errmsg);
496: goto out;
497: }
498:
499: if (pipe(p) != 0) {
500: error("%s: pipe: %s", __func__, strerror(errno));
501: goto out;
502: }
503:
504: debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"",
505: options.authorized_keys_command, pw->pw_name);
506:
507: /*
508: * Don't want to call this in the child, where it can fatal() and
509: * run cleanup_exit() code.
510: */
511: restore_uid();
512:
513: switch ((pid = fork())) {
514: case -1: /* error */
515: error("%s: fork: %s", __func__, strerror(errno));
516: close(p[0]);
517: close(p[1]);
518: return 0;
519: case 0: /* child */
520: for (i = 0; i < NSIG; i++)
521: signal(i, SIG_DFL);
522:
1.32 ! djm 523: closefrom(STDERR_FILENO + 1);
1.31 djm 524: /* Don't use permanently_set_uid() here to avoid fatal() */
525: if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
526: error("setresgid %u: %s", (u_int)pw->pw_gid,
527: strerror(errno));
528: _exit(1);
529: }
530: if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
531: error("setresuid %u: %s", (u_int)pw->pw_uid,
532: strerror(errno));
533: _exit(1);
534: }
535:
536: close(p[0]);
537: if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
538: error("%s: open %s: %s", __func__, _PATH_DEVNULL,
539: strerror(errno));
540: _exit(1);
541: }
542: if (dup2(devnull, STDIN_FILENO) == -1 ||
543: dup2(p[1], STDOUT_FILENO) == -1 ||
544: dup2(devnull, STDERR_FILENO) == -1) {
545: error("%s: dup2: %s", __func__, strerror(errno));
546: _exit(1);
547: }
548:
549: execl(options.authorized_keys_command,
550: options.authorized_keys_command, pw->pw_name, NULL);
551:
552: error("AuthorizedKeysCommand %s exec failed: %s",
553: options.authorized_keys_command, strerror(errno));
554: _exit(127);
555: default: /* parent */
556: break;
557: }
558:
559: temporarily_use_uid(pw);
560:
561: close(p[1]);
562: if ((f = fdopen(p[0], "r")) == NULL) {
563: error("%s: fdopen: %s", __func__, strerror(errno));
564: close(p[0]);
565: /* Don't leave zombie child */
566: kill(pid, SIGTERM);
567: while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
568: ;
569: goto out;
570: }
571: ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
572: fclose(f);
573:
574: while (waitpid(pid, &status, 0) == -1) {
575: if (errno != EINTR) {
576: error("%s: waitpid: %s", __func__, strerror(errno));
577: goto out;
578: }
579: }
580: if (WIFSIGNALED(status)) {
581: error("AuthorizedKeysCommand %s exited on signal %d",
582: options.authorized_keys_command, WTERMSIG(status));
583: goto out;
584: } else if (WEXITSTATUS(status) != 0) {
585: error("AuthorizedKeysCommand %s returned status %d",
586: options.authorized_keys_command, WEXITSTATUS(status));
587: goto out;
588: }
589: found_key = ok;
590: out:
591: restore_uid();
592: return found_key;
593: }
594:
595: /*
596: * Check whether key authenticates and authorises the user.
597: */
1.1 markus 598: int
599: user_key_allowed(struct passwd *pw, Key *key)
600: {
1.29 djm 601: u_int success, i;
1.1 markus 602: char *file;
1.21 djm 603:
604: if (auth_key_is_revoked(key))
605: return 0;
606: if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
607: return 0;
608:
609: success = user_cert_trusted_ca(pw, key);
610: if (success)
611: return success;
1.1 markus 612:
1.31 djm 613: success = user_key_command_allowed2(pw, key);
614: if (success > 0)
615: return success;
616:
1.29 djm 617: for (i = 0; !success && i < options.num_authkeys_files; i++) {
1.31 djm 618:
619: if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
620: continue;
1.29 djm 621: file = expand_authorized_keys(
622: options.authorized_keys_files[i], pw);
1.31 djm 623:
1.29 djm 624: success = user_key_allowed2(pw, key, file);
625: xfree(file);
626: }
1.1 markus 627:
628: return success;
629: }
1.2 markus 630:
631: Authmethod method_pubkey = {
632: "publickey",
633: userauth_pubkey,
634: &options.pubkey_authentication
635: };