Annotation of src/usr.bin/ssh/auth2-pubkey.c, Revision 1.114
1.114 ! djm 1: /* $OpenBSD: auth2-pubkey.c,v 1.113 2022/02/27 01:33:59 naddy 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.94 djm 30: #include <stdlib.h>
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"
1.106 djm 47: #include "kex.h"
1.81 markus 48: #include "sshbuf.h"
1.1 markus 49: #include "log.h"
1.41 millert 50: #include "misc.h"
1.1 markus 51: #include "servconf.h"
52: #include "compat.h"
1.64 markus 53: #include "sshkey.h"
1.15 deraadt 54: #include "hostfile.h"
1.1 markus 55: #include "auth.h"
56: #include "pathnames.h"
57: #include "uidswap.h"
58: #include "auth-options.h"
59: #include "canohost.h"
1.15 deraadt 60: #ifdef GSSAPI
61: #include "ssh-gss.h"
62: #endif
1.1 markus 63: #include "monitor_wrap.h"
1.21 djm 64: #include "authfile.h"
1.24 djm 65: #include "match.h"
1.50 djm 66: #include "ssherr.h"
1.112 djm 67: #include "kex.h"
1.50 djm 68: #include "channels.h" /* XXX for session.h */
69: #include "session.h" /* XXX for child_set_env(); refactor? */
1.96 djm 70: #include "sk-api.h"
1.1 markus 71:
72: /* import */
73: extern ServerOptions options;
74:
1.73 djm 75: static char *
76: format_key(const struct sshkey *key)
77: {
78: char *ret, *fp = sshkey_fingerprint(key,
79: options.fingerprint_hash, SSH_FP_DEFAULT);
80:
81: xasprintf(&ret, "%s %s", sshkey_type(key), fp);
82: free(fp);
83: return ret;
84: }
85:
1.2 markus 86: static int
1.111 djm 87: userauth_pubkey(struct ssh *ssh, const char *method)
1.1 markus 88: {
1.65 markus 89: Authctxt *authctxt = ssh->authctxt;
1.77 djm 90: struct passwd *pw = authctxt->pw;
1.83 djm 91: struct sshbuf *b = NULL;
1.112 djm 92: struct sshkey *key = NULL, *hostkey = NULL;
1.83 djm 93: char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
94: u_char *pkblob = NULL, *sig = NULL, have_sig;
1.64 markus 95: size_t blen, slen;
1.112 djm 96: int hostbound, r, pktype;
1.100 djm 97: int req_presence = 0, req_verify = 0, authenticated = 0;
1.77 djm 98: struct sshauthopt *authopts = NULL;
1.95 djm 99: struct sshkey_sig_details *sig_details = NULL;
1.114 ! djm 100: const char *remote_ip = ssh_remote_ipaddr(ssh);
! 101: const char *remote_host = auth_get_canonical_hostname(ssh,
! 102: options.use_dns);
1.1 markus 103:
1.112 djm 104: hostbound = strcmp(method, "publickey-hostbound-v00@openssh.com") == 0;
105:
1.75 djm 106: if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
107: (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
108: (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
1.112 djm 109: fatal_fr(r, "parse %s packet", method);
110:
111: /* hostbound auth includes the hostkey offered at initial KEX */
112: if (hostbound) {
113: if ((r = sshpkt_getb_froms(ssh, &b)) != 0 ||
114: (r = sshkey_fromb(b, &hostkey)) != 0)
115: fatal_fr(r, "parse %s hostkey", method);
116: if (ssh->kex->initial_hostkey == NULL)
117: fatal_f("internal error: initial hostkey not recorded");
118: if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey))
119: fatal_f("%s packet contained wrong host key", method);
120: sshbuf_free(b);
121: b = NULL;
122: }
1.87 djm 123:
124: if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) {
125: char *keystring;
126: struct sshbuf *pkbuf;
127:
128: if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL)
1.101 djm 129: fatal_f("sshbuf_from failed");
1.91 djm 130: if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL)
1.101 djm 131: fatal_f("sshbuf_dtob64 failed");
132: debug2_f("%s user %s %s public key %s %s",
1.87 djm 133: authctxt->valid ? "valid" : "invalid", authctxt->user,
134: have_sig ? "attempting" : "querying", pkalg, keystring);
135: sshbuf_free(pkbuf);
136: free(keystring);
137: }
138:
1.64 markus 139: pktype = sshkey_type_from_name(pkalg);
1.1 markus 140: if (pktype == KEY_UNSPEC) {
141: /* this is perfectly legal */
1.101 djm 142: verbose_f("unsupported public key algorithm: %s", pkalg);
1.1 markus 143: goto done;
144: }
1.64 markus 145: if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
1.101 djm 146: error_fr(r, "parse key");
1.64 markus 147: goto done;
148: }
1.1 markus 149: if (key == NULL) {
1.101 djm 150: error_f("cannot decode key: %s", pkalg);
1.1 markus 151: goto done;
152: }
153: if (key->type != pktype) {
1.101 djm 154: error_f("type mismatch for decoded key "
155: "(received %d, expected %d)", key->type, pktype);
1.39 djm 156: goto done;
157: }
1.64 markus 158: if (sshkey_type_plain(key->type) == KEY_RSA &&
159: (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1.39 djm 160: logit("Refusing RSA key because client uses unsafe "
161: "signature scheme");
1.1 markus 162: goto done;
163: }
1.68 djm 164: if (auth2_key_already_used(authctxt, key)) {
1.64 markus 165: logit("refusing previously-used %s key", sshkey_type(key));
1.44 djm 166: goto done;
167: }
1.104 dtucker 168: if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) {
1.113 naddy 169: logit_f("signature algorithm %s not in "
170: "PubkeyAcceptedAlgorithms", pkalg);
1.45 djm 171: goto done;
172: }
1.86 djm 173: if ((r = sshkey_check_cert_sigtype(key,
174: options.ca_sign_algorithms)) != 0) {
1.101 djm 175: logit_fr(r, "certificate signature algorithm %s",
1.86 djm 176: (key->cert == NULL || key->cert->signature_type == NULL) ?
1.101 djm 177: "(null)" : key->cert->signature_type);
1.86 djm 178: goto done;
179: }
1.73 djm 180: key_s = format_key(key);
181: if (sshkey_is_cert(key))
182: ca_s = format_key(key->cert->signature_key);
183:
1.1 markus 184: if (have_sig) {
1.112 djm 185: debug3_f("%s have %s signature for %s%s%s",
186: method, pkalg, key_s,
1.101 djm 187: ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
1.64 markus 188: if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
189: (r = sshpkt_get_end(ssh)) != 0)
1.101 djm 190: fatal_fr(r, "parse signature packet");
1.64 markus 191: if ((b = sshbuf_new()) == NULL)
1.101 djm 192: fatal_f("sshbuf_new failed");
1.64 markus 193: if (ssh->compat & SSH_OLD_SESSIONID) {
1.106 djm 194: if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0)
1.101 djm 195: fatal_fr(r, "put old session id");
1.1 markus 196: } else {
1.106 djm 197: if ((r = sshbuf_put_stringb(b,
198: ssh->kex->session_id)) != 0)
1.101 djm 199: fatal_fr(r, "put session id");
1.1 markus 200: }
1.83 djm 201: if (!authctxt->valid || authctxt->user == NULL) {
1.101 djm 202: debug2_f("disabled because of invalid user");
1.83 djm 203: goto done;
204: }
1.1 markus 205: /* reconstruct packet */
1.35 djm 206: xasprintf(&userstyle, "%s%s%s", authctxt->user,
207: authctxt->style ? ":" : "",
208: authctxt->style ? authctxt->style : "");
1.64 markus 209: if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
210: (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
1.75 djm 211: (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
1.111 djm 212: (r = sshbuf_put_cstring(b, method)) != 0 ||
1.75 djm 213: (r = sshbuf_put_u8(b, have_sig)) != 0 ||
1.85 mestre 214: (r = sshbuf_put_cstring(b, pkalg)) != 0 ||
1.75 djm 215: (r = sshbuf_put_string(b, pkblob, blen)) != 0)
1.112 djm 216: fatal_fr(r, "reconstruct %s packet", method);
217: if (hostbound &&
218: (r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0)
219: fatal_fr(r, "reconstruct %s packet", method);
1.1 markus 220: #ifdef DEBUG_PK
1.64 markus 221: sshbuf_dump(b, stderr);
1.1 markus 222: #endif
223: /* test for correct signature */
224: authenticated = 0;
1.114 ! djm 225: if (PRIVSEP(user_key_allowed(pw, key, 1, remote_ip,
! 226: remote_host, &authopts)) &&
1.80 djm 227: PRIVSEP(sshkey_verify(key, sig, slen,
228: sshbuf_ptr(b), sshbuf_len(b),
229: (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
1.95 djm 230: ssh->compat, &sig_details)) == 0) {
1.1 markus 231: authenticated = 1;
1.44 djm 232: }
1.96 djm 233: if (authenticated == 1 && sig_details != NULL) {
234: auth2_record_info(authctxt, "signature count = %u",
235: sig_details->sk_counter);
1.101 djm 236: debug_f("sk_counter = %u, sk_flags = 0x%02x",
237: sig_details->sk_counter, sig_details->sk_flags);
1.96 djm 238: req_presence = (options.pubkey_auth_options &
1.97 djm 239: PUBKEYAUTH_TOUCH_REQUIRED) ||
240: !authopts->no_require_user_presence;
1.96 djm 241: if (req_presence && (sig_details->sk_flags &
242: SSH_SK_USER_PRESENCE_REQD) == 0) {
243: error("public key %s signature for %s%s from "
244: "%.128s port %d rejected: user presence "
1.99 naddy 245: "(authenticator touch) requirement "
246: "not met ", key_s,
1.100 djm 247: authctxt->valid ? "" : "invalid user ",
248: authctxt->user, ssh_remote_ipaddr(ssh),
249: ssh_remote_port(ssh));
250: authenticated = 0;
251: goto done;
252: }
253: req_verify = (options.pubkey_auth_options &
254: PUBKEYAUTH_VERIFY_REQUIRED) ||
255: authopts->require_verify;
256: if (req_verify && (sig_details->sk_flags &
257: SSH_SK_USER_VERIFICATION_REQD) == 0) {
258: error("public key %s signature for %s%s from "
259: "%.128s port %d rejected: user "
260: "verification requirement not met ", key_s,
1.96 djm 261: authctxt->valid ? "" : "invalid user ",
262: authctxt->user, ssh_remote_ipaddr(ssh),
263: ssh_remote_port(ssh));
264: authenticated = 0;
265: goto done;
266: }
1.95 djm 267: }
1.68 djm 268: auth2_record_key(authctxt, authenticated, key);
1.1 markus 269: } else {
1.112 djm 270: debug_f("%s test pkalg %s pkblob %s%s%s", method, pkalg, key_s,
1.101 djm 271: ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
1.73 djm 272:
1.64 markus 273: if ((r = sshpkt_get_end(ssh)) != 0)
1.101 djm 274: fatal_fr(r, "parse packet");
1.1 markus 275:
1.83 djm 276: if (!authctxt->valid || authctxt->user == NULL) {
1.101 djm 277: debug2_f("disabled because of invalid user");
1.83 djm 278: goto done;
279: }
1.1 markus 280: /* XXX fake reply and always send PK_OK ? */
281: /*
282: * XXX this allows testing whether a user is allowed
283: * to login: if you happen to have a valid pubkey this
284: * message is sent. the message is NEVER sent at all
285: * if a user is not allowed to login. is this an
286: * issue? -markus
287: */
1.114 ! djm 288: if (PRIVSEP(user_key_allowed(pw, key, 0, remote_ip,
! 289: remote_host, NULL))) {
1.64 markus 290: if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
291: != 0 ||
292: (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
293: (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
1.82 markus 294: (r = sshpkt_send(ssh)) != 0 ||
295: (r = ssh_packet_write_wait(ssh)) != 0)
1.101 djm 296: fatal_fr(r, "send packet");
1.1 markus 297: authctxt->postponed = 1;
298: }
299: }
300: done:
1.77 djm 301: if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) {
1.101 djm 302: debug_f("key options inconsistent with existing");
1.77 djm 303: authenticated = 0;
304: }
1.101 djm 305: debug2_f("authenticated %d pkalg %s", authenticated, pkalg);
1.77 djm 306:
1.84 djm 307: sshbuf_free(b);
1.77 djm 308: sshauthopt_free(authopts);
1.68 djm 309: sshkey_free(key);
1.112 djm 310: sshkey_free(hostkey);
1.64 markus 311: free(userstyle);
1.36 djm 312: free(pkalg);
313: free(pkblob);
1.73 djm 314: free(key_s);
315: free(ca_s);
1.83 djm 316: free(sig);
1.95 djm 317: sshkey_sig_details_free(sig_details);
1.1 markus 318: return authenticated;
319: }
320:
1.24 djm 321: static int
1.40 djm 322: match_principals_option(const char *principal_list, struct sshkey_cert *cert)
1.24 djm 323: {
324: char *result;
325: u_int i;
326:
327: /* XXX percent_expand() sequences for authorized_principals? */
328:
329: for (i = 0; i < cert->nprincipals; i++) {
330: if ((result = match_list(cert->principals[i],
331: principal_list, NULL)) != NULL) {
332: debug3("matched principal from key options \"%.100s\"",
333: result);
1.36 djm 334: free(result);
1.24 djm 335: return 1;
336: }
337: }
338: return 0;
339: }
340:
1.77 djm 341: /*
342: * Process a single authorized_principals format line. Returns 0 and sets
343: * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
344: * log preamble for file/line information.
345: */
1.24 djm 346: static int
1.114 ! djm 347: check_principals_line(char *cp, const struct sshkey_cert *cert,
1.77 djm 348: const char *loc, struct sshauthopt **authoptsp)
1.24 djm 349: {
1.77 djm 350: u_int i, found = 0;
351: char *ep, *line_opts;
352: const char *reason = NULL;
353: struct sshauthopt *opts = NULL;
354:
355: if (authoptsp != NULL)
356: *authoptsp = NULL;
357:
358: /* Trim trailing whitespace. */
359: ep = cp + strlen(cp) - 1;
360: while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
361: *ep-- = '\0';
362:
363: /*
364: * If the line has internal whitespace then assume it has
365: * key options.
366: */
367: line_opts = NULL;
368: if ((ep = strrchr(cp, ' ')) != NULL ||
369: (ep = strrchr(cp, '\t')) != NULL) {
370: for (; *ep == ' ' || *ep == '\t'; ep++)
371: ;
372: line_opts = cp;
373: cp = ep;
374: }
375: if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
376: debug("%s: bad principals options: %s", loc, reason);
377: auth_debug_add("%s: bad principals options: %s", loc, reason);
378: return -1;
379: }
380: /* Check principals in cert against those on line */
381: for (i = 0; i < cert->nprincipals; i++) {
382: if (strcmp(cp, cert->principals[i]) != 0)
383: continue;
384: debug3("%s: matched principal \"%.100s\"",
385: loc, cert->principals[i]);
386: found = 1;
387: }
388: if (found && authoptsp != NULL) {
389: *authoptsp = opts;
390: opts = NULL;
391: }
392: sshauthopt_free(opts);
393: return found ? 0 : -1;
394: }
395:
396: static int
1.114 ! djm 397: process_principals(FILE *f, const char *file,
1.77 djm 398: const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
399: {
1.79 markus 400: char loc[256], *line = NULL, *cp, *ep;
401: size_t linesize = 0;
1.110 djm 402: u_long linenum = 0, nonblank = 0;
1.77 djm 403: u_int found_principal = 0;
404:
405: if (authoptsp != NULL)
406: *authoptsp = NULL;
1.24 djm 407:
1.79 markus 408: while (getline(&line, &linesize, f) != -1) {
409: linenum++;
1.62 djm 410: /* Always consume entire input */
411: if (found_principal)
412: continue;
1.77 djm 413:
1.26 djm 414: /* Skip leading whitespace. */
1.24 djm 415: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
416: ;
1.26 djm 417: /* Skip blank and comment lines. */
418: if ((ep = strchr(cp, '#')) != NULL)
419: *ep = '\0';
420: if (!*cp || *cp == '\n')
1.24 djm 421: continue;
1.77 djm 422:
1.110 djm 423: nonblank++;
1.77 djm 424: snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
1.114 ! djm 425: if (check_principals_line(cp, cert, loc, authoptsp) == 0)
1.77 djm 426: found_principal = 1;
1.24 djm 427: }
1.110 djm 428: debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
1.79 markus 429: free(line);
1.62 djm 430: return found_principal;
1.51 djm 431: }
432:
1.77 djm 433: /* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
434:
1.51 djm 435: static int
1.114 ! djm 436: match_principals_file(struct passwd *pw, char *file,
1.77 djm 437: struct sshkey_cert *cert, struct sshauthopt **authoptsp)
1.51 djm 438: {
439: FILE *f;
440: int success;
441:
1.77 djm 442: if (authoptsp != NULL)
443: *authoptsp = NULL;
444:
1.51 djm 445: temporarily_use_uid(pw);
446: debug("trying authorized principals file %s", file);
447: if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
448: restore_uid();
449: return 0;
450: }
1.114 ! djm 451: success = process_principals(f, file, cert, authoptsp);
1.24 djm 452: fclose(f);
453: restore_uid();
1.51 djm 454: return success;
1.31 djm 455: }
1.24 djm 456:
1.31 djm 457: /*
1.51 djm 458: * Checks whether principal is allowed in output of command.
459: * returns 1 if the principal is allowed or 0 otherwise.
460: */
461: static int
1.114 ! djm 462: match_principals_command(struct passwd *user_pw,
1.77 djm 463: const struct sshkey *key, struct sshauthopt **authoptsp)
1.51 djm 464: {
1.77 djm 465: struct passwd *runas_pw = NULL;
1.56 djm 466: const struct sshkey_cert *cert = key->cert;
1.51 djm 467: FILE *f = NULL;
1.56 djm 468: int r, ok, found_principal = 0;
1.51 djm 469: int i, ac = 0, uid_swapped = 0;
470: pid_t pid;
471: char *tmp, *username = NULL, *command = NULL, **av = NULL;
1.56 djm 472: char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
1.88 djm 473: char serial_s[32], uidstr[32];
1.51 djm 474: void (*osigchld)(int);
475:
1.77 djm 476: if (authoptsp != NULL)
477: *authoptsp = NULL;
1.51 djm 478: if (options.authorized_principals_command == NULL)
479: return 0;
480: if (options.authorized_principals_command_user == NULL) {
481: error("No user for AuthorizedPrincipalsCommand specified, "
482: "skipping");
483: return 0;
484: }
485:
486: /*
487: * NB. all returns later this function should go via "out" to
488: * ensure the original SIGCHLD handler is restored properly.
489: */
1.98 dtucker 490: osigchld = ssh_signal(SIGCHLD, SIG_DFL);
1.51 djm 491:
492: /* Prepare and verify the user for the command */
493: username = percent_expand(options.authorized_principals_command_user,
494: "u", user_pw->pw_name, (char *)NULL);
1.77 djm 495: runas_pw = getpwnam(username);
496: if (runas_pw == NULL) {
1.51 djm 497: error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
498: username, strerror(errno));
499: goto out;
500: }
501:
502: /* Turn the command into an argument vector */
1.108 djm 503: if (argv_split(options.authorized_principals_command,
504: &ac, &av, 0) != 0) {
1.51 djm 505: error("AuthorizedPrincipalsCommand \"%s\" contains "
1.90 djm 506: "invalid quotes", options.authorized_principals_command);
1.51 djm 507: goto out;
508: }
509: if (ac == 0) {
510: error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
1.90 djm 511: options.authorized_principals_command);
1.51 djm 512: goto out;
513: }
1.56 djm 514: if ((ca_fp = sshkey_fingerprint(cert->signature_key,
515: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1.101 djm 516: error_f("sshkey_fingerprint failed");
1.56 djm 517: goto out;
518: }
1.57 djm 519: if ((key_fp = sshkey_fingerprint(key,
1.56 djm 520: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1.101 djm 521: error_f("sshkey_fingerprint failed");
1.56 djm 522: goto out;
523: }
524: if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
1.101 djm 525: error_fr(r, "sshkey_to_base64 failed");
1.56 djm 526: goto out;
527: }
528: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
1.101 djm 529: error_fr(r, "sshkey_to_base64 failed");
1.56 djm 530: goto out;
531: }
1.59 djm 532: snprintf(serial_s, sizeof(serial_s), "%llu",
533: (unsigned long long)cert->serial);
1.78 djm 534: snprintf(uidstr, sizeof(uidstr), "%llu",
535: (unsigned long long)user_pw->pw_uid);
1.51 djm 536: for (i = 1; i < ac; i++) {
537: tmp = percent_expand(av[i],
1.78 djm 538: "U", uidstr,
1.51 djm 539: "u", user_pw->pw_name,
540: "h", user_pw->pw_dir,
1.56 djm 541: "t", sshkey_ssh_name(key),
542: "T", sshkey_ssh_name(cert->signature_key),
543: "f", key_fp,
544: "F", ca_fp,
545: "k", keytext,
546: "K", catext,
1.58 djm 547: "i", cert->key_id,
548: "s", serial_s,
1.51 djm 549: (char *)NULL);
550: if (tmp == NULL)
1.101 djm 551: fatal_f("percent_expand failed");
1.51 djm 552: free(av[i]);
553: av[i] = tmp;
554: }
555: /* Prepare a printable command for logs, etc. */
1.69 djm 556: command = argv_assemble(ac, av);
1.51 djm 557:
1.103 djm 558: if ((pid = subprocess("AuthorizedPrincipalsCommand", command,
1.69 djm 559: ac, av, &f,
1.103 djm 560: SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
561: runas_pw, temporarily_use_uid, restore_uid)) == 0)
1.51 djm 562: goto out;
563:
564: uid_swapped = 1;
1.77 djm 565: temporarily_use_uid(runas_pw);
1.51 djm 566:
1.114 ! djm 567: ok = process_principals(f, "(command)", cert, authoptsp);
1.51 djm 568:
1.61 djm 569: fclose(f);
570: f = NULL;
571:
1.70 djm 572: if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
1.51 djm 573: goto out;
574:
575: /* Read completed successfully */
576: found_principal = ok;
577: out:
578: if (f != NULL)
579: fclose(f);
1.98 dtucker 580: ssh_signal(SIGCHLD, osigchld);
1.51 djm 581: for (i = 0; i < ac; i++)
582: free(av[i]);
583: free(av);
584: if (uid_swapped)
585: restore_uid();
586: free(command);
587: free(username);
1.56 djm 588: free(ca_fp);
589: free(key_fp);
590: free(catext);
591: free(keytext);
1.51 djm 592: return found_principal;
1.77 djm 593: }
594:
595: /*
596: * Check a single line of an authorized_keys-format file. Returns 0 if key
597: * matches, -1 otherwise. Will return key/cert options via *authoptsp
598: * on success. "loc" is used as file/line location in log messages.
599: */
600: static int
1.114 ! djm 601: check_authkey_line(struct passwd *pw, struct sshkey *key,
! 602: char *cp, const char *remote_ip, const char *remote_host, const char *loc,
! 603: struct sshauthopt **authoptsp)
1.77 djm 604: {
605: int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
606: struct sshkey *found = NULL;
607: struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
608: char *key_options = NULL, *fp = NULL;
609: const char *reason = NULL;
610: int ret = -1;
611:
612: if (authoptsp != NULL)
613: *authoptsp = NULL;
614:
615: if ((found = sshkey_new(want_keytype)) == NULL) {
1.101 djm 616: debug3_f("keytype %d failed", want_keytype);
1.77 djm 617: goto out;
618: }
619:
620: /* XXX djm: peek at key type in line and skip if unwanted */
621:
622: if (sshkey_read(found, &cp) != 0) {
623: /* no key? check for options */
624: debug2("%s: check options: '%s'", loc, cp);
625: key_options = cp;
1.93 djm 626: if (sshkey_advance_past_options(&cp) != 0) {
1.77 djm 627: reason = "invalid key option string";
628: goto fail_reason;
629: }
630: skip_space(&cp);
631: if (sshkey_read(found, &cp) != 0) {
632: /* still no key? advance to next line*/
633: debug2("%s: advance: '%s'", loc, cp);
634: goto out;
635: }
636: }
637: /* Parse key options now; we need to know if this is a CA key */
638: if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
639: debug("%s: bad key options: %s", loc, reason);
640: auth_debug_add("%s: bad key options: %s", loc, reason);
641: goto out;
642: }
643: /* Ignore keys that don't match or incorrectly marked as CAs */
644: if (sshkey_is_cert(key)) {
645: /* Certificate; check signature key against CA */
646: if (!sshkey_equal(found, key->cert->signature_key) ||
647: !keyopts->cert_authority)
648: goto out;
649: } else {
650: /* Plain key: check it against key found in file */
651: if (!sshkey_equal(found, key) || keyopts->cert_authority)
652: goto out;
653: }
654:
655: /* We have a candidate key, perform authorisation checks */
656: if ((fp = sshkey_fingerprint(found,
657: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
1.101 djm 658: fatal_f("fingerprint failed");
1.77 djm 659:
660: debug("%s: matching %s found: %s %s", loc,
661: sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
662:
1.114 ! djm 663: if (auth_authorise_keyopts(pw, keyopts,
! 664: sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {
1.77 djm 665: reason = "Refused by key options";
666: goto fail_reason;
667: }
668: /* That's all we need for plain keys. */
669: if (!sshkey_is_cert(key)) {
670: verbose("Accepted key %s %s found at %s",
671: sshkey_type(found), fp, loc);
672: finalopts = keyopts;
673: keyopts = NULL;
674: goto success;
675: }
676:
677: /*
678: * Additional authorisation for certificates.
679: */
680:
681: /* Parse and check options present in certificate */
682: if ((certopts = sshauthopt_from_cert(key)) == NULL) {
683: reason = "Invalid certificate options";
684: goto fail_reason;
685: }
1.114 ! djm 686: if (auth_authorise_keyopts(pw, certopts, 0,
! 687: remote_ip, remote_host, loc) != 0) {
1.77 djm 688: reason = "Refused by certificate options";
689: goto fail_reason;
690: }
691: if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
692: goto fail_reason;
693:
694: /*
695: * If the user has specified a list of principals as
696: * a key option, then prefer that list to matching
697: * their username in the certificate principals list.
698: */
699: if (keyopts->cert_principals != NULL &&
700: !match_principals_option(keyopts->cert_principals, key->cert)) {
701: reason = "Certificate does not contain an authorized principal";
702: goto fail_reason;
703: }
1.109 djm 704: if (sshkey_cert_check_authority_now(key, 0, 0, 0,
1.107 djm 705: keyopts->cert_principals == NULL ? pw->pw_name : NULL,
706: &reason) != 0)
1.77 djm 707: goto fail_reason;
708:
709: verbose("Accepted certificate ID \"%s\" (serial %llu) "
710: "signed by CA %s %s found at %s",
711: key->cert->key_id,
712: (unsigned long long)key->cert->serial,
713: sshkey_type(found), fp, loc);
714:
715: success:
716: if (finalopts == NULL)
1.101 djm 717: fatal_f("internal error: missing options");
1.77 djm 718: if (authoptsp != NULL) {
719: *authoptsp = finalopts;
720: finalopts = NULL;
721: }
722: /* success */
723: ret = 0;
724: goto out;
725:
726: fail_reason:
727: error("%s", reason);
728: auth_debug_add("%s", reason);
729: out:
730: free(fp);
731: sshauthopt_free(keyopts);
732: sshauthopt_free(certopts);
733: sshauthopt_free(finalopts);
734: sshkey_free(found);
735: return ret;
736: }
737:
1.51 djm 738: /*
1.31 djm 739: * Checks whether key is allowed in authorized_keys-format file,
740: * returns 1 if the key is allowed or 0 otherwise.
741: */
1.1 markus 742: static int
1.114 ! djm 743: check_authkeys_file(struct passwd *pw, FILE *f, char *file,
! 744: struct sshkey *key, const char *remote_ip,
! 745: const char *remote_host, struct sshauthopt **authoptsp)
1.1 markus 746: {
1.79 markus 747: char *cp, *line = NULL, loc[256];
748: size_t linesize = 0;
1.18 dtucker 749: int found_key = 0;
1.110 djm 750: u_long linenum = 0, nonblank = 0;
1.77 djm 751:
752: if (authoptsp != NULL)
753: *authoptsp = NULL;
1.1 markus 754:
1.79 markus 755: while (getline(&line, &linesize, f) != -1) {
756: linenum++;
1.71 djm 757: /* Always consume entire file */
1.62 djm 758: if (found_key)
759: continue;
1.20 djm 760:
1.1 markus 761: /* Skip leading whitespace, empty and comment lines. */
1.77 djm 762: cp = line;
763: skip_space(&cp);
1.1 markus 764: if (!*cp || *cp == '\n' || *cp == '#')
765: continue;
1.110 djm 766:
767: nonblank++;
1.77 djm 768: snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
1.114 ! djm 769: if (check_authkey_line(pw, key, cp,
! 770: remote_ip, remote_host, loc, authoptsp) == 0)
1.20 djm 771: found_key = 1;
1.1 markus 772: }
1.79 markus 773: free(line);
1.110 djm 774: debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
1.1 markus 775: return found_key;
776: }
777:
1.21 djm 778: /* Authenticate a certificate key against TrustedUserCAKeys */
779: static int
1.114 ! djm 780: user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,
! 781: const char *remote_ip, const char *remote_host,
1.77 djm 782: struct sshauthopt **authoptsp)
1.21 djm 783: {
1.24 djm 784: char *ca_fp, *principals_file = NULL;
1.21 djm 785: const char *reason;
1.77 djm 786: struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;
787: struct sshauthopt *final_opts = NULL;
1.64 markus 788: int r, ret = 0, found_principal = 0, use_authorized_principals;
1.21 djm 789:
1.77 djm 790: if (authoptsp != NULL)
791: *authoptsp = NULL;
792:
1.64 markus 793: if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
1.21 djm 794: return 0;
795:
1.46 djm 796: if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
797: options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
798: return 0;
1.21 djm 799:
1.64 markus 800: if ((r = sshkey_in_file(key->cert->signature_key,
801: options.trusted_user_ca_keys, 1, 0)) != 0) {
1.101 djm 802: debug2_fr(r, "CA %s %s is not listed in %s",
1.64 markus 803: sshkey_type(key->cert->signature_key), ca_fp,
1.101 djm 804: options.trusted_user_ca_keys);
1.21 djm 805: goto out;
806: }
1.24 djm 807: /*
808: * If AuthorizedPrincipals is in use, then compare the certificate
809: * principals against the names in that file rather than matching
810: * against the username.
811: */
812: if ((principals_file = authorized_principals_file(pw)) != NULL) {
1.114 ! djm 813: if (match_principals_file(pw, principals_file,
1.77 djm 814: key->cert, &principals_opts))
1.51 djm 815: found_principal = 1;
816: }
817: /* Try querying command if specified */
1.114 ! djm 818: if (!found_principal && match_principals_command(pw, key,
1.77 djm 819: &principals_opts))
1.51 djm 820: found_principal = 1;
1.53 jsing 821: /* If principals file or command is specified, then require a match */
822: use_authorized_principals = principals_file != NULL ||
1.107 djm 823: options.authorized_principals_command != NULL;
1.53 jsing 824: if (!found_principal && use_authorized_principals) {
1.51 djm 825: reason = "Certificate does not contain an authorized principal";
1.77 djm 826: goto fail_reason;
1.21 djm 827: }
1.77 djm 828: if (use_authorized_principals && principals_opts == NULL)
1.101 djm 829: fatal_f("internal error: missing principals_opts");
1.109 djm 830: if (sshkey_cert_check_authority_now(key, 0, 1, 0,
1.53 jsing 831: use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
1.24 djm 832: goto fail_reason;
1.77 djm 833:
834: /* Check authority from options in key and from principals file/cmd */
835: if ((cert_opts = sshauthopt_from_cert(key)) == NULL) {
836: reason = "Invalid certificate options";
837: goto fail_reason;
838: }
1.114 ! djm 839: if (auth_authorise_keyopts(pw, cert_opts, 0,
! 840: remote_ip, remote_host, "cert") != 0) {
1.77 djm 841: reason = "Refused by certificate options";
1.60 djm 842: goto fail_reason;
1.77 djm 843: }
844: if (principals_opts == NULL) {
845: final_opts = cert_opts;
846: cert_opts = NULL;
847: } else {
1.114 ! djm 848: if (auth_authorise_keyopts(pw, principals_opts, 0,
! 849: remote_ip, remote_host, "principals") != 0) {
1.77 djm 850: reason = "Refused by certificate principals options";
851: goto fail_reason;
852: }
853: if ((final_opts = sshauthopt_merge(principals_opts,
854: cert_opts, &reason)) == NULL) {
855: fail_reason:
856: error("%s", reason);
857: auth_debug_add("%s", reason);
858: goto out;
859: }
860: }
1.21 djm 861:
1.77 djm 862: /* Success */
1.54 djm 863: verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
864: "%s CA %s via %s", key->cert->key_id,
865: (unsigned long long)key->cert->serial,
1.64 markus 866: sshkey_type(key->cert->signature_key), ca_fp,
1.22 djm 867: options.trusted_user_ca_keys);
1.77 djm 868: if (authoptsp != NULL) {
869: *authoptsp = final_opts;
870: final_opts = NULL;
871: }
1.21 djm 872: ret = 1;
873: out:
1.77 djm 874: sshauthopt_free(principals_opts);
875: sshauthopt_free(cert_opts);
876: sshauthopt_free(final_opts);
1.36 djm 877: free(principals_file);
878: free(ca_fp);
1.21 djm 879: return ret;
880: }
881:
1.31 djm 882: /*
883: * Checks whether key is allowed in file.
884: * returns 1 if the key is allowed or 0 otherwise.
885: */
886: static int
1.114 ! djm 887: user_key_allowed2(struct passwd *pw, struct sshkey *key,
! 888: char *file, const char *remote_ip, const char *remote_host,
! 889: struct sshauthopt **authoptsp)
1.31 djm 890: {
891: FILE *f;
892: int found_key = 0;
893:
1.77 djm 894: if (authoptsp != NULL)
895: *authoptsp = NULL;
896:
1.31 djm 897: /* Temporarily use the user's uid. */
898: temporarily_use_uid(pw);
899:
900: debug("trying public key file %s", file);
901: if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
1.114 ! djm 902: found_key = check_authkeys_file(pw, f, file,
! 903: key, remote_ip, remote_host, authoptsp);
1.31 djm 904: fclose(f);
905: }
906:
907: restore_uid();
908: return found_key;
909: }
910:
911: /*
912: * Checks whether key is allowed in output of command.
913: * returns 1 if the key is allowed or 0 otherwise.
914: */
915: static int
1.114 ! djm 916: user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key,
! 917: const char *remote_ip, const char *remote_host,
! 918: struct sshauthopt **authoptsp)
1.31 djm 919: {
1.77 djm 920: struct passwd *runas_pw = NULL;
1.50 djm 921: FILE *f = NULL;
922: int r, ok, found_key = 0;
923: int i, uid_swapped = 0, ac = 0;
1.31 djm 924: pid_t pid;
1.50 djm 925: char *username = NULL, *key_fp = NULL, *keytext = NULL;
1.78 djm 926: char uidstr[32], *tmp, *command = NULL, **av = NULL;
1.50 djm 927: void (*osigchld)(int);
1.31 djm 928:
1.77 djm 929: if (authoptsp != NULL)
930: *authoptsp = NULL;
1.50 djm 931: if (options.authorized_keys_command == NULL)
1.31 djm 932: return 0;
1.32 djm 933: if (options.authorized_keys_command_user == NULL) {
934: error("No user for AuthorizedKeysCommand specified, skipping");
935: return 0;
936: }
937:
1.50 djm 938: /*
939: * NB. all returns later this function should go via "out" to
940: * ensure the original SIGCHLD handler is restored properly.
941: */
1.98 dtucker 942: osigchld = ssh_signal(SIGCHLD, SIG_DFL);
1.50 djm 943:
944: /* Prepare and verify the user for the command */
1.32 djm 945: username = percent_expand(options.authorized_keys_command_user,
946: "u", user_pw->pw_name, (char *)NULL);
1.77 djm 947: runas_pw = getpwnam(username);
948: if (runas_pw == NULL) {
1.34 djm 949: error("AuthorizedKeysCommandUser \"%s\" not found: %s",
950: username, strerror(errno));
1.50 djm 951: goto out;
1.31 djm 952: }
953:
1.50 djm 954: /* Prepare AuthorizedKeysCommand */
955: if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
956: SSH_FP_DEFAULT)) == NULL) {
1.101 djm 957: error_f("sshkey_fingerprint failed");
1.31 djm 958: goto out;
959: }
1.50 djm 960: if ((r = sshkey_to_base64(key, &keytext)) != 0) {
1.101 djm 961: error_fr(r, "sshkey_to_base64 failed");
1.31 djm 962: goto out;
963: }
964:
1.50 djm 965: /* Turn the command into an argument vector */
1.108 djm 966: if (argv_split(options.authorized_keys_command, &ac, &av, 0) != 0) {
1.50 djm 967: error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
1.102 djm 968: options.authorized_keys_command);
1.50 djm 969: goto out;
970: }
971: if (ac == 0) {
972: error("AuthorizedKeysCommand \"%s\" yielded no arguments",
1.102 djm 973: options.authorized_keys_command);
1.31 djm 974: goto out;
975: }
1.78 djm 976: snprintf(uidstr, sizeof(uidstr), "%llu",
977: (unsigned long long)user_pw->pw_uid);
1.50 djm 978: for (i = 1; i < ac; i++) {
979: tmp = percent_expand(av[i],
1.78 djm 980: "U", uidstr,
1.50 djm 981: "u", user_pw->pw_name,
982: "h", user_pw->pw_dir,
983: "t", sshkey_ssh_name(key),
984: "f", key_fp,
985: "k", keytext,
986: (char *)NULL);
987: if (tmp == NULL)
1.101 djm 988: fatal_f("percent_expand failed");
1.50 djm 989: free(av[i]);
990: av[i] = tmp;
991: }
992: /* Prepare a printable command for logs, etc. */
1.69 djm 993: command = argv_assemble(ac, av);
1.31 djm 994:
995: /*
1.50 djm 996: * If AuthorizedKeysCommand was run without arguments
997: * then fall back to the old behaviour of passing the
998: * target username as a single argument.
1.31 djm 999: */
1.50 djm 1000: if (ac == 1) {
1001: av = xreallocarray(av, ac + 2, sizeof(*av));
1002: av[1] = xstrdup(user_pw->pw_name);
1003: av[2] = NULL;
1004: /* Fix up command too, since it is used in log messages */
1005: free(command);
1006: xasprintf(&command, "%s %s", av[0], av[1]);
1007: }
1.31 djm 1008:
1.103 djm 1009: if ((pid = subprocess("AuthorizedKeysCommand", command,
1.69 djm 1010: ac, av, &f,
1.103 djm 1011: SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
1012: runas_pw, temporarily_use_uid, restore_uid)) == 0)
1.50 djm 1013: goto out;
1.31 djm 1014:
1.50 djm 1015: uid_swapped = 1;
1.77 djm 1016: temporarily_use_uid(runas_pw);
1.31 djm 1017:
1.114 ! djm 1018: ok = check_authkeys_file(user_pw, f,
! 1019: options.authorized_keys_command, key, remote_ip,
! 1020: remote_host, authoptsp);
1.61 djm 1021:
1022: fclose(f);
1023: f = NULL;
1.31 djm 1024:
1.70 djm 1025: if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0)
1.31 djm 1026: goto out;
1.50 djm 1027:
1028: /* Read completed successfully */
1.31 djm 1029: found_key = ok;
1030: out:
1.50 djm 1031: if (f != NULL)
1032: fclose(f);
1.98 dtucker 1033: ssh_signal(SIGCHLD, osigchld);
1.50 djm 1034: for (i = 0; i < ac; i++)
1035: free(av[i]);
1036: free(av);
1037: if (uid_swapped)
1038: restore_uid();
1039: free(command);
1040: free(username);
1041: free(key_fp);
1042: free(keytext);
1.31 djm 1043: return found_key;
1044: }
1045:
1046: /*
1047: * Check whether key authenticates and authorises the user.
1048: */
1.1 markus 1049: int
1.114 ! djm 1050: user_key_allowed(struct passwd *pw, struct sshkey *key,
! 1051: int auth_attempt, const char *remote_ip, const char *remote_host,
! 1052: struct sshauthopt **authoptsp)
1.1 markus 1053: {
1.89 djm 1054: u_int success = 0, i;
1.1 markus 1055: char *file;
1.77 djm 1056: struct sshauthopt *opts = NULL;
1.89 djm 1057:
1.77 djm 1058: if (authoptsp != NULL)
1059: *authoptsp = NULL;
1.21 djm 1060:
1061: if (auth_key_is_revoked(key))
1062: return 0;
1.64 markus 1063: if (sshkey_is_cert(key) &&
1064: auth_key_is_revoked(key->cert->signature_key))
1.21 djm 1065: return 0;
1066:
1.89 djm 1067: for (i = 0; !success && i < options.num_authkeys_files; i++) {
1068: if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1069: continue;
1070: file = expand_authorized_keys(
1071: options.authorized_keys_files[i], pw);
1.114 ! djm 1072: success = user_key_allowed2(pw, key, file,
! 1073: remote_ip, remote_host, &opts);
1.89 djm 1074: free(file);
1075: if (!success) {
1076: sshauthopt_free(opts);
1077: opts = NULL;
1078: }
1079: }
1080: if (success)
1081: goto out;
1082:
1.114 ! djm 1083: if ((success = user_cert_trusted_ca(pw, key, remote_ip, remote_host,
! 1084: &opts)) != 0)
1.77 djm 1085: goto out;
1086: sshauthopt_free(opts);
1087: opts = NULL;
1088:
1.114 ! djm 1089: if ((success = user_key_command_allowed2(pw, key, remote_ip,
! 1090: remote_host, &opts)) != 0)
1.77 djm 1091: goto out;
1092: sshauthopt_free(opts);
1093: opts = NULL;
1.1 markus 1094:
1.77 djm 1095: out:
1096: if (success && authoptsp != NULL) {
1097: *authoptsp = opts;
1098: opts = NULL;
1099: }
1100: sshauthopt_free(opts);
1.1 markus 1101: return success;
1102: }
1.2 markus 1103:
1104: Authmethod method_pubkey = {
1105: "publickey",
1.112 djm 1106: "publickey-hostbound-v00@openssh.com",
1.2 markus 1107: userauth_pubkey,
1108: &options.pubkey_authentication
1109: };