Annotation of src/usr.bin/ssh/auth2-pubkey.c, Revision 1.72
1.72 ! djm 1: /* $OpenBSD: auth2-pubkey.c,v 1.71 2017/09/07 23:48:09 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.13 stevesk 29:
1.31 djm 30: #include <errno.h>
1.16 djm 31: #include <fcntl.h>
1.31 djm 32: #include <paths.h>
1.13 stevesk 33: #include <pwd.h>
1.31 djm 34: #include <signal.h>
1.14 stevesk 35: #include <stdio.h>
1.15 deraadt 36: #include <stdarg.h>
1.20 djm 37: #include <string.h>
38: #include <time.h>
1.17 dtucker 39: #include <unistd.h>
1.44 djm 40: #include <limits.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"
1.41 millert 48: #include "misc.h"
1.1 markus 49: #include "servconf.h"
50: #include "compat.h"
1.64 markus 51: #include "sshkey.h"
1.15 deraadt 52: #include "hostfile.h"
1.1 markus 53: #include "auth.h"
54: #include "pathnames.h"
55: #include "uidswap.h"
56: #include "auth-options.h"
57: #include "canohost.h"
1.15 deraadt 58: #ifdef GSSAPI
59: #include "ssh-gss.h"
60: #endif
1.1 markus 61: #include "monitor_wrap.h"
1.21 djm 62: #include "authfile.h"
1.24 djm 63: #include "match.h"
1.50 djm 64: #include "ssherr.h"
65: #include "channels.h" /* XXX for session.h */
66: #include "session.h" /* XXX for child_set_env(); refactor? */
1.1 markus 67:
68: /* import */
69: extern ServerOptions options;
70: extern u_char *session_id2;
1.4 markus 71: extern u_int session_id2_len;
1.1 markus 72:
1.2 markus 73: static int
1.65 markus 74: userauth_pubkey(struct ssh *ssh)
1.1 markus 75: {
1.65 markus 76: Authctxt *authctxt = ssh->authctxt;
1.64 markus 77: struct sshbuf *b;
1.63 markus 78: struct sshkey *key = NULL;
1.64 markus 79: char *pkalg, *userstyle = NULL, *fp = NULL;
80: u_char *pkblob, *sig, have_sig;
81: size_t blen, slen;
82: int r, pktype;
1.1 markus 83: int authenticated = 0;
84:
85: if (!authctxt->valid) {
1.55 djm 86: debug2("%s: disabled because of invalid user", __func__);
1.1 markus 87: return 0;
88: }
1.64 markus 89: if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0)
90: fatal("%s: sshpkt_get_u8 failed: %s", __func__, ssh_err(r));
91: if (ssh->compat & SSH_BUG_PKAUTH) {
1.55 djm 92: debug2("%s: SSH_BUG_PKAUTH", __func__);
1.64 markus 93: if ((b = sshbuf_new()) == NULL)
94: fatal("%s: sshbuf_new failed", __func__);
1.1 markus 95: /* no explicit pkalg given */
96: /* so we have to extract the pkalg from the pkblob */
1.64 markus 97: /* XXX use sshbuf_from() */
98: if ((r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
99: (r = sshbuf_put(b, pkblob, blen)) != 0 ||
100: (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0)
101: fatal("%s: failed: %s", __func__, ssh_err(r));
102: sshbuf_free(b);
1.1 markus 103: } else {
1.64 markus 104: if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
105: (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
106: fatal("%s: sshpkt_get_cstring failed: %s",
107: __func__, ssh_err(r));
1.1 markus 108: }
1.64 markus 109: pktype = sshkey_type_from_name(pkalg);
1.1 markus 110: if (pktype == KEY_UNSPEC) {
111: /* this is perfectly legal */
1.55 djm 112: logit("%s: unsupported public key algorithm: %s",
113: __func__, pkalg);
1.1 markus 114: goto done;
115: }
1.64 markus 116: if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
117: error("%s: could not parse key: %s", __func__, ssh_err(r));
118: goto done;
119: }
1.1 markus 120: if (key == NULL) {
1.55 djm 121: error("%s: cannot decode key: %s", __func__, pkalg);
1.1 markus 122: goto done;
123: }
124: if (key->type != pktype) {
1.55 djm 125: error("%s: type mismatch for decoded key "
126: "(received %d, expected %d)", __func__, key->type, pktype);
1.39 djm 127: goto done;
128: }
1.64 markus 129: if (sshkey_type_plain(key->type) == KEY_RSA &&
130: (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1.39 djm 131: logit("Refusing RSA key because client uses unsafe "
132: "signature scheme");
1.1 markus 133: goto done;
134: }
1.55 djm 135: fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
1.68 djm 136: if (auth2_key_already_used(authctxt, key)) {
1.64 markus 137: logit("refusing previously-used %s key", sshkey_type(key));
1.44 djm 138: goto done;
139: }
1.49 djm 140: if (match_pattern_list(sshkey_ssh_name(key),
141: options.pubkey_key_types, 0) != 1) {
1.45 djm 142: logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
143: __func__, sshkey_ssh_name(key));
144: goto done;
145: }
146:
1.1 markus 147: if (have_sig) {
1.55 djm 148: debug3("%s: have signature for %s %s",
149: __func__, sshkey_type(key), fp);
1.64 markus 150: if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
151: (r = sshpkt_get_end(ssh)) != 0)
152: fatal("%s: %s", __func__, ssh_err(r));
153: if ((b = sshbuf_new()) == NULL)
154: fatal("%s: sshbuf_new failed", __func__);
155: if (ssh->compat & SSH_OLD_SESSIONID) {
156: if ((r = sshbuf_put(b, session_id2,
157: session_id2_len)) != 0)
158: fatal("%s: sshbuf_put session id: %s",
159: __func__, ssh_err(r));
1.1 markus 160: } else {
1.64 markus 161: if ((r = sshbuf_put_string(b, session_id2,
162: session_id2_len)) != 0)
163: fatal("%s: sshbuf_put_string session id: %s",
164: __func__, ssh_err(r));
1.1 markus 165: }
166: /* reconstruct packet */
1.35 djm 167: xasprintf(&userstyle, "%s%s%s", authctxt->user,
168: authctxt->style ? ":" : "",
169: authctxt->style ? authctxt->style : "");
1.64 markus 170: if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
171: (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
172: (r = sshbuf_put_cstring(b, ssh->compat & SSH_BUG_PKSERVICE ?
173: "ssh-userauth" : authctxt->service)) != 0)
174: fatal("%s: build packet failed: %s",
175: __func__, ssh_err(r));
176: if (ssh->compat & SSH_BUG_PKAUTH) {
177: if ((r = sshbuf_put_u8(b, have_sig)) != 0)
178: fatal("%s: build packet failed: %s",
179: __func__, ssh_err(r));
1.1 markus 180: } else {
1.64 markus 181: if ((r = sshbuf_put_cstring(b, "publickey")) != 0 ||
182: (r = sshbuf_put_u8(b, have_sig)) != 0 ||
183: (r = sshbuf_put_cstring(b, pkalg) != 0))
184: fatal("%s: build packet failed: %s",
185: __func__, ssh_err(r));
1.1 markus 186: }
1.64 markus 187: if ((r = sshbuf_put_string(b, pkblob, blen)) != 0)
188: fatal("%s: build packet failed: %s",
189: __func__, ssh_err(r));
1.1 markus 190: #ifdef DEBUG_PK
1.64 markus 191: sshbuf_dump(b, stderr);
1.1 markus 192: #endif
1.37 djm 193:
1.1 markus 194: /* test for correct signature */
195: authenticated = 0;
1.48 djm 196: if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
1.64 markus 197: PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
1.72 ! djm 198: sshbuf_len(b), pkalg, ssh->compat)) == 0) {
1.1 markus 199: authenticated = 1;
1.44 djm 200: }
1.64 markus 201: sshbuf_free(b);
1.36 djm 202: free(sig);
1.68 djm 203: auth2_record_key(authctxt, authenticated, key);
1.1 markus 204: } else {
1.55 djm 205: debug("%s: test whether pkalg/pkblob are acceptable for %s %s",
206: __func__, sshkey_type(key), fp);
1.64 markus 207: if ((r = sshpkt_get_end(ssh)) != 0)
208: fatal("%s: %s", __func__, ssh_err(r));
1.1 markus 209:
210: /* XXX fake reply and always send PK_OK ? */
211: /*
212: * XXX this allows testing whether a user is allowed
213: * to login: if you happen to have a valid pubkey this
214: * message is sent. the message is NEVER sent at all
215: * if a user is not allowed to login. is this an
216: * issue? -markus
217: */
1.48 djm 218: if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) {
1.64 markus 219: if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
220: != 0 ||
221: (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
222: (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
223: (r = sshpkt_send(ssh)) != 0)
224: fatal("%s: %s", __func__, ssh_err(r));
225: ssh_packet_write_wait(ssh);
1.1 markus 226: authctxt->postponed = 1;
227: }
228: }
229: if (authenticated != 1)
230: auth_clear_options();
231: done:
1.55 djm 232: debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
1.68 djm 233: sshkey_free(key);
1.64 markus 234: free(userstyle);
1.36 djm 235: free(pkalg);
236: free(pkblob);
1.55 djm 237: free(fp);
1.1 markus 238: return authenticated;
239: }
240:
1.24 djm 241: static int
1.40 djm 242: match_principals_option(const char *principal_list, struct sshkey_cert *cert)
1.24 djm 243: {
244: char *result;
245: u_int i;
246:
247: /* XXX percent_expand() sequences for authorized_principals? */
248:
249: for (i = 0; i < cert->nprincipals; i++) {
250: if ((result = match_list(cert->principals[i],
251: principal_list, NULL)) != NULL) {
252: debug3("matched principal from key options \"%.100s\"",
253: result);
1.36 djm 254: free(result);
1.24 djm 255: return 1;
256: }
257: }
258: return 0;
259: }
260:
261: static int
1.67 markus 262: process_principals(FILE *f, const char *file, struct passwd *pw,
1.56 djm 263: const struct sshkey_cert *cert)
1.24 djm 264: {
1.26 djm 265: char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
1.24 djm 266: u_long linenum = 0;
1.62 djm 267: u_int i, found_principal = 0;
1.24 djm 268:
269: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.62 djm 270: /* Always consume entire input */
271: if (found_principal)
272: continue;
1.26 djm 273: /* Skip leading whitespace. */
1.24 djm 274: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
275: ;
1.26 djm 276: /* Skip blank and comment lines. */
277: if ((ep = strchr(cp, '#')) != NULL)
278: *ep = '\0';
279: if (!*cp || *cp == '\n')
1.24 djm 280: continue;
1.26 djm 281: /* Trim trailing whitespace. */
282: ep = cp + strlen(cp) - 1;
283: while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
284: *ep-- = '\0';
285: /*
286: * If the line has internal whitespace then assume it has
287: * key options.
288: */
289: line_opts = NULL;
290: if ((ep = strrchr(cp, ' ')) != NULL ||
291: (ep = strrchr(cp, '\t')) != NULL) {
292: for (; *ep == ' ' || *ep == '\t'; ep++)
1.27 deraadt 293: ;
1.26 djm 294: line_opts = cp;
295: cp = ep;
296: }
1.24 djm 297: for (i = 0; i < cert->nprincipals; i++) {
298: if (strcmp(cp, cert->principals[i]) == 0) {
1.51 djm 299: debug3("%s:%lu: matched principal \"%.100s\"",
1.67 markus 300: file, linenum, cert->principals[i]);
1.26 djm 301: if (auth_parse_options(pw, line_opts,
302: file, linenum) != 1)
303: continue;
1.62 djm 304: found_principal = 1;
305: continue;
1.24 djm 306: }
307: }
308: }
1.62 djm 309: return found_principal;
1.51 djm 310: }
311:
312: static int
313: match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
314: {
315: FILE *f;
316: int success;
317:
318: temporarily_use_uid(pw);
319: debug("trying authorized principals file %s", file);
320: if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
321: restore_uid();
322: return 0;
323: }
324: success = process_principals(f, file, pw, cert);
1.24 djm 325: fclose(f);
326: restore_uid();
1.51 djm 327: return success;
1.31 djm 328: }
1.24 djm 329:
1.31 djm 330: /*
1.51 djm 331: * Checks whether principal is allowed in output of command.
332: * returns 1 if the principal is allowed or 0 otherwise.
333: */
334: static int
1.56 djm 335: match_principals_command(struct passwd *user_pw, const struct sshkey *key)
1.51 djm 336: {
1.56 djm 337: const struct sshkey_cert *cert = key->cert;
1.51 djm 338: FILE *f = NULL;
1.56 djm 339: int r, ok, found_principal = 0;
1.51 djm 340: struct passwd *pw;
341: int i, ac = 0, uid_swapped = 0;
342: pid_t pid;
343: char *tmp, *username = NULL, *command = NULL, **av = NULL;
1.56 djm 344: char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
1.58 djm 345: char serial_s[16];
1.51 djm 346: void (*osigchld)(int);
347:
348: if (options.authorized_principals_command == NULL)
349: return 0;
350: if (options.authorized_principals_command_user == NULL) {
351: error("No user for AuthorizedPrincipalsCommand specified, "
352: "skipping");
353: return 0;
354: }
355:
356: /*
357: * NB. all returns later this function should go via "out" to
358: * ensure the original SIGCHLD handler is restored properly.
359: */
360: osigchld = signal(SIGCHLD, SIG_DFL);
361:
362: /* Prepare and verify the user for the command */
363: username = percent_expand(options.authorized_principals_command_user,
364: "u", user_pw->pw_name, (char *)NULL);
365: pw = getpwnam(username);
366: if (pw == NULL) {
367: error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
368: username, strerror(errno));
369: goto out;
370: }
371:
372: /* Turn the command into an argument vector */
1.69 djm 373: if (argv_split(options.authorized_principals_command, &ac, &av) != 0) {
1.51 djm 374: error("AuthorizedPrincipalsCommand \"%s\" contains "
375: "invalid quotes", command);
376: goto out;
377: }
378: if (ac == 0) {
379: error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
380: command);
381: goto out;
382: }
1.56 djm 383: if ((ca_fp = sshkey_fingerprint(cert->signature_key,
384: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
385: error("%s: sshkey_fingerprint failed", __func__);
386: goto out;
387: }
1.57 djm 388: if ((key_fp = sshkey_fingerprint(key,
1.56 djm 389: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
390: error("%s: sshkey_fingerprint failed", __func__);
391: goto out;
392: }
393: if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
394: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
395: goto out;
396: }
397: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
398: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
399: goto out;
400: }
1.59 djm 401: snprintf(serial_s, sizeof(serial_s), "%llu",
402: (unsigned long long)cert->serial);
1.51 djm 403: for (i = 1; i < ac; i++) {
404: tmp = percent_expand(av[i],
405: "u", user_pw->pw_name,
406: "h", user_pw->pw_dir,
1.56 djm 407: "t", sshkey_ssh_name(key),
408: "T", sshkey_ssh_name(cert->signature_key),
409: "f", key_fp,
410: "F", ca_fp,
411: "k", keytext,
412: "K", catext,
1.58 djm 413: "i", cert->key_id,
414: "s", serial_s,
1.51 djm 415: (char *)NULL);
416: if (tmp == NULL)
417: fatal("%s: percent_expand failed", __func__);
418: free(av[i]);
419: av[i] = tmp;
420: }
421: /* Prepare a printable command for logs, etc. */
1.69 djm 422: command = argv_assemble(ac, av);
1.51 djm 423:
424: if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
1.69 djm 425: ac, av, &f,
426: SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
1.51 djm 427: goto out;
428:
429: uid_swapped = 1;
430: temporarily_use_uid(pw);
431:
1.67 markus 432: ok = process_principals(f, "(command)", pw, cert);
1.51 djm 433:
1.61 djm 434: fclose(f);
435: f = NULL;
436:
1.70 djm 437: if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
1.51 djm 438: goto out;
439:
440: /* Read completed successfully */
441: found_principal = ok;
442: out:
443: if (f != NULL)
444: fclose(f);
445: signal(SIGCHLD, osigchld);
446: for (i = 0; i < ac; i++)
447: free(av[i]);
448: free(av);
449: if (uid_swapped)
450: restore_uid();
451: free(command);
452: free(username);
1.56 djm 453: free(ca_fp);
454: free(key_fp);
455: free(catext);
456: free(keytext);
1.51 djm 457: return found_principal;
458: }
459: /*
1.31 djm 460: * Checks whether key is allowed in authorized_keys-format file,
461: * returns 1 if the key is allowed or 0 otherwise.
462: */
1.1 markus 463: static int
1.64 markus 464: check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw)
1.1 markus 465: {
1.8 dtucker 466: char line[SSH_MAX_PUBKEY_BYTES];
1.18 dtucker 467: int found_key = 0;
1.1 markus 468: u_long linenum = 0;
1.64 markus 469: struct sshkey *found = NULL;
1.1 markus 470:
1.8 dtucker 471: while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
1.60 djm 472: char *cp, *key_options = NULL, *fp = NULL;
473: const char *reason = NULL;
474:
1.71 djm 475: /* Always consume entire file */
1.62 djm 476: if (found_key)
477: continue;
1.37 djm 478: if (found != NULL)
1.64 markus 479: sshkey_free(found);
480: found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type);
481: if (found == NULL)
482: goto done;
1.20 djm 483: auth_clear_options();
484:
1.1 markus 485: /* Skip leading whitespace, empty and comment lines. */
486: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
487: ;
488: if (!*cp || *cp == '\n' || *cp == '#')
489: continue;
490:
1.64 markus 491: if (sshkey_read(found, &cp) != 0) {
1.1 markus 492: /* no key? check if there are options for this key */
493: int quoted = 0;
494: debug2("user_key_allowed: check options: '%s'", cp);
1.7 avsm 495: key_options = cp;
1.1 markus 496: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
497: if (*cp == '\\' && cp[1] == '"')
498: cp++; /* Skip both */
499: else if (*cp == '"')
500: quoted = !quoted;
501: }
502: /* Skip remaining whitespace. */
503: for (; *cp == ' ' || *cp == '\t'; cp++)
504: ;
1.64 markus 505: if (sshkey_read(found, &cp) != 0) {
1.1 markus 506: debug2("user_key_allowed: advance: '%s'", cp);
507: /* still no key? advance to next line*/
508: continue;
509: }
510: }
1.64 markus 511: if (sshkey_is_cert(key)) {
512: if (!sshkey_equal(found, key->cert->signature_key))
1.25 djm 513: continue;
514: if (auth_parse_options(pw, key_options, file,
515: linenum) != 1)
516: continue;
1.20 djm 517: if (!key_is_cert_authority)
518: continue;
1.46 djm 519: if ((fp = sshkey_fingerprint(found,
520: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
521: continue;
1.22 djm 522: debug("matching CA found: file %s, line %lu, %s %s",
1.64 markus 523: file, linenum, sshkey_type(found), fp);
1.24 djm 524: /*
525: * If the user has specified a list of principals as
526: * a key option, then prefer that list to matching
527: * their username in the certificate principals list.
528: */
529: if (authorized_principals != NULL &&
530: !match_principals_option(authorized_principals,
531: key->cert)) {
532: reason = "Certificate does not contain an "
533: "authorized principal";
534: fail_reason:
1.36 djm 535: free(fp);
1.20 djm 536: error("%s", reason);
537: auth_debug_add("%s", reason);
538: continue;
539: }
1.64 markus 540: if (sshkey_cert_check_authority(key, 0, 0,
1.24 djm 541: authorized_principals == NULL ? pw->pw_name : NULL,
542: &reason) != 0)
543: goto fail_reason;
1.60 djm 544: if (auth_cert_options(key, pw, &reason) != 0)
545: goto fail_reason;
1.54 djm 546: verbose("Accepted certificate ID \"%s\" (serial %llu) "
1.22 djm 547: "signed by %s CA %s via %s", key->cert->key_id,
1.54 djm 548: (unsigned long long)key->cert->serial,
1.64 markus 549: sshkey_type(found), fp, file);
1.36 djm 550: free(fp);
1.20 djm 551: found_key = 1;
552: break;
1.64 markus 553: } else if (sshkey_equal(found, key)) {
1.25 djm 554: if (auth_parse_options(pw, key_options, file,
555: linenum) != 1)
556: continue;
557: if (key_is_cert_authority)
558: continue;
1.46 djm 559: if ((fp = sshkey_fingerprint(found,
560: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
561: continue;
1.37 djm 562: debug("matching key found: file %s, line %lu %s %s",
1.64 markus 563: file, linenum, sshkey_type(found), fp);
1.36 djm 564: free(fp);
1.46 djm 565: found_key = 1;
1.62 djm 566: continue;
1.1 markus 567: }
568: }
1.64 markus 569: done:
1.37 djm 570: if (found != NULL)
1.64 markus 571: sshkey_free(found);
1.1 markus 572: if (!found_key)
573: debug2("key not found");
574: return found_key;
575: }
576:
1.21 djm 577: /* Authenticate a certificate key against TrustedUserCAKeys */
578: static int
1.63 markus 579: user_cert_trusted_ca(struct passwd *pw, struct sshkey *key)
1.21 djm 580: {
1.24 djm 581: char *ca_fp, *principals_file = NULL;
1.21 djm 582: const char *reason;
1.64 markus 583: int r, ret = 0, found_principal = 0, use_authorized_principals;
1.21 djm 584:
1.64 markus 585: if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
1.21 djm 586: return 0;
587:
1.46 djm 588: if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
589: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
590: return 0;
1.21 djm 591:
1.64 markus 592: if ((r = sshkey_in_file(key->cert->signature_key,
593: options.trusted_user_ca_keys, 1, 0)) != 0) {
594: debug2("%s: CA %s %s is not listed in %s: %s", __func__,
595: sshkey_type(key->cert->signature_key), ca_fp,
596: options.trusted_user_ca_keys, ssh_err(r));
1.21 djm 597: goto out;
598: }
1.24 djm 599: /*
600: * If AuthorizedPrincipals is in use, then compare the certificate
601: * principals against the names in that file rather than matching
602: * against the username.
603: */
604: if ((principals_file = authorized_principals_file(pw)) != NULL) {
1.51 djm 605: if (match_principals_file(principals_file, pw, key->cert))
606: found_principal = 1;
607: }
608: /* Try querying command if specified */
1.56 djm 609: if (!found_principal && match_principals_command(pw, key))
1.51 djm 610: found_principal = 1;
1.53 jsing 611: /* If principals file or command is specified, then require a match */
612: use_authorized_principals = principals_file != NULL ||
613: options.authorized_principals_command != NULL;
614: if (!found_principal && use_authorized_principals) {
1.51 djm 615: reason = "Certificate does not contain an authorized principal";
1.24 djm 616: fail_reason:
1.51 djm 617: error("%s", reason);
618: auth_debug_add("%s", reason);
619: goto out;
1.21 djm 620: }
1.64 markus 621: if (sshkey_cert_check_authority(key, 0, 1,
1.53 jsing 622: use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
1.24 djm 623: goto fail_reason;
1.60 djm 624: if (auth_cert_options(key, pw, &reason) != 0)
625: goto fail_reason;
1.21 djm 626:
1.54 djm 627: verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
628: "%s CA %s via %s", key->cert->key_id,
629: (unsigned long long)key->cert->serial,
1.64 markus 630: sshkey_type(key->cert->signature_key), ca_fp,
1.22 djm 631: options.trusted_user_ca_keys);
1.21 djm 632: ret = 1;
633:
634: out:
1.36 djm 635: free(principals_file);
636: free(ca_fp);
1.21 djm 637: return ret;
638: }
639:
1.31 djm 640: /*
641: * Checks whether key is allowed in file.
642: * returns 1 if the key is allowed or 0 otherwise.
643: */
644: static int
1.63 markus 645: user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file)
1.31 djm 646: {
647: FILE *f;
648: int found_key = 0;
649:
650: /* Temporarily use the user's uid. */
651: temporarily_use_uid(pw);
652:
653: debug("trying public key file %s", file);
654: if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
655: found_key = check_authkeys_file(f, file, key, pw);
656: fclose(f);
657: }
658:
659: restore_uid();
660: return found_key;
661: }
662:
663: /*
664: * Checks whether key is allowed in output of command.
665: * returns 1 if the key is allowed or 0 otherwise.
666: */
667: static int
1.63 markus 668: user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key)
1.31 djm 669: {
1.50 djm 670: FILE *f = NULL;
671: int r, ok, found_key = 0;
1.31 djm 672: struct passwd *pw;
1.50 djm 673: int i, uid_swapped = 0, ac = 0;
1.31 djm 674: pid_t pid;
1.50 djm 675: char *username = NULL, *key_fp = NULL, *keytext = NULL;
676: char *tmp, *command = NULL, **av = NULL;
677: void (*osigchld)(int);
1.31 djm 678:
1.50 djm 679: if (options.authorized_keys_command == NULL)
1.31 djm 680: return 0;
1.32 djm 681: if (options.authorized_keys_command_user == NULL) {
682: error("No user for AuthorizedKeysCommand specified, skipping");
683: return 0;
684: }
685:
1.50 djm 686: /*
687: * NB. all returns later this function should go via "out" to
688: * ensure the original SIGCHLD handler is restored properly.
689: */
690: osigchld = signal(SIGCHLD, SIG_DFL);
691:
692: /* Prepare and verify the user for the command */
1.32 djm 693: username = percent_expand(options.authorized_keys_command_user,
694: "u", user_pw->pw_name, (char *)NULL);
695: pw = getpwnam(username);
696: if (pw == NULL) {
1.34 djm 697: error("AuthorizedKeysCommandUser \"%s\" not found: %s",
698: username, strerror(errno));
1.50 djm 699: goto out;
1.31 djm 700: }
701:
1.50 djm 702: /* Prepare AuthorizedKeysCommand */
703: if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
704: SSH_FP_DEFAULT)) == NULL) {
705: error("%s: sshkey_fingerprint failed", __func__);
1.31 djm 706: goto out;
707: }
1.50 djm 708: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
709: error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
1.31 djm 710: goto out;
711: }
712:
1.50 djm 713: /* Turn the command into an argument vector */
1.69 djm 714: if (argv_split(options.authorized_keys_command, &ac, &av) != 0) {
1.50 djm 715: error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
716: command);
717: goto out;
718: }
719: if (ac == 0) {
720: error("AuthorizedKeysCommand \"%s\" yielded no arguments",
721: command);
1.31 djm 722: goto out;
723: }
1.50 djm 724: for (i = 1; i < ac; i++) {
725: tmp = percent_expand(av[i],
726: "u", user_pw->pw_name,
727: "h", user_pw->pw_dir,
728: "t", sshkey_ssh_name(key),
729: "f", key_fp,
730: "k", keytext,
731: (char *)NULL);
732: if (tmp == NULL)
733: fatal("%s: percent_expand failed", __func__);
734: free(av[i]);
735: av[i] = tmp;
736: }
737: /* Prepare a printable command for logs, etc. */
1.69 djm 738: command = argv_assemble(ac, av);
1.31 djm 739:
740: /*
1.50 djm 741: * If AuthorizedKeysCommand was run without arguments
742: * then fall back to the old behaviour of passing the
743: * target username as a single argument.
1.31 djm 744: */
1.50 djm 745: if (ac == 1) {
746: av = xreallocarray(av, ac + 2, sizeof(*av));
747: av[1] = xstrdup(user_pw->pw_name);
748: av[2] = NULL;
749: /* Fix up command too, since it is used in log messages */
750: free(command);
751: xasprintf(&command, "%s %s", av[0], av[1]);
752: }
1.31 djm 753:
1.50 djm 754: if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
1.69 djm 755: ac, av, &f,
756: SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
1.50 djm 757: goto out;
1.31 djm 758:
1.50 djm 759: uid_swapped = 1;
1.31 djm 760: temporarily_use_uid(pw);
761:
762: ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
1.61 djm 763:
764: fclose(f);
765: f = NULL;
1.31 djm 766:
1.70 djm 767: if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0)
1.31 djm 768: goto out;
1.50 djm 769:
770: /* Read completed successfully */
1.31 djm 771: found_key = ok;
772: out:
1.50 djm 773: if (f != NULL)
774: fclose(f);
775: signal(SIGCHLD, osigchld);
776: for (i = 0; i < ac; i++)
777: free(av[i]);
778: free(av);
779: if (uid_swapped)
780: restore_uid();
781: free(command);
782: free(username);
783: free(key_fp);
784: free(keytext);
1.31 djm 785: return found_key;
786: }
787:
788: /*
789: * Check whether key authenticates and authorises the user.
790: */
1.1 markus 791: int
1.63 markus 792: user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt)
1.1 markus 793: {
1.29 djm 794: u_int success, i;
1.1 markus 795: char *file;
1.21 djm 796:
797: if (auth_key_is_revoked(key))
798: return 0;
1.64 markus 799: if (sshkey_is_cert(key) &&
800: auth_key_is_revoked(key->cert->signature_key))
1.21 djm 801: return 0;
802:
803: success = user_cert_trusted_ca(pw, key);
804: if (success)
805: return success;
1.1 markus 806:
1.31 djm 807: success = user_key_command_allowed2(pw, key);
808: if (success > 0)
809: return success;
810:
1.29 djm 811: for (i = 0; !success && i < options.num_authkeys_files; i++) {
1.31 djm 812:
813: if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
814: continue;
1.29 djm 815: file = expand_authorized_keys(
816: options.authorized_keys_files[i], pw);
1.31 djm 817:
1.29 djm 818: success = user_key_allowed2(pw, key, file);
1.36 djm 819: free(file);
1.29 djm 820: }
1.1 markus 821:
822: return success;
823: }
1.2 markus 824:
825: Authmethod method_pubkey = {
826: "publickey",
827: userauth_pubkey,
828: &options.pubkey_authentication
829: };