xref: /openssh-portable/auth2-pubkey.c (revision 31d8d231)
1*31d8d231Sdjm@openbsd.org /* $OpenBSD: auth2-pubkey.c,v 1.107 2021/04/03 06:18:40 djm Exp $ */
2855bf3acSBen Lindstrom /*
3855bf3acSBen Lindstrom  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4855bf3acSBen Lindstrom  *
5855bf3acSBen Lindstrom  * Redistribution and use in source and binary forms, with or without
6855bf3acSBen Lindstrom  * modification, are permitted provided that the following conditions
7855bf3acSBen Lindstrom  * are met:
8855bf3acSBen Lindstrom  * 1. Redistributions of source code must retain the above copyright
9855bf3acSBen Lindstrom  *    notice, this list of conditions and the following disclaimer.
10855bf3acSBen Lindstrom  * 2. Redistributions in binary form must reproduce the above copyright
11855bf3acSBen Lindstrom  *    notice, this list of conditions and the following disclaimer in the
12855bf3acSBen Lindstrom  *    documentation and/or other materials provided with the distribution.
13855bf3acSBen Lindstrom  *
14855bf3acSBen Lindstrom  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15855bf3acSBen Lindstrom  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16855bf3acSBen Lindstrom  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17855bf3acSBen Lindstrom  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18855bf3acSBen Lindstrom  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19855bf3acSBen Lindstrom  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20855bf3acSBen Lindstrom  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21855bf3acSBen Lindstrom  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22855bf3acSBen Lindstrom  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23855bf3acSBen Lindstrom  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24855bf3acSBen Lindstrom  */
25855bf3acSBen Lindstrom 
26855bf3acSBen Lindstrom #include "includes.h"
27f17883e6SDamien Miller 
28f17883e6SDamien Miller #include <sys/types.h>
29f17883e6SDamien Miller #include <sys/stat.h>
30855bf3acSBen Lindstrom 
31be02d7cbSdjm@openbsd.org #include <stdlib.h>
3209d3e125SDamien Miller #include <errno.h>
3306db584eSDarren Tucker #include <fcntl.h>
34737f7affSDarren Tucker #ifdef HAVE_PATHS_H
3509d3e125SDamien Miller # include <paths.h>
36737f7affSDarren Tucker #endif
379f2abc47SDamien Miller #include <pwd.h>
3809d3e125SDamien Miller #include <signal.h>
39a7a73ee3SDamien Miller #include <stdio.h>
40d7834353SDamien Miller #include <stdarg.h>
410a80ca19SDamien Miller #include <string.h>
420a80ca19SDamien Miller #include <time.h>
43d9526a5eSDarren Tucker #include <unistd.h>
44f69b69b8Sdjm@openbsd.org #include <limits.h>
459f2abc47SDamien Miller 
46d7834353SDamien Miller #include "xmalloc.h"
4722cc7410SDarren Tucker #include "ssh.h"
48855bf3acSBen Lindstrom #include "ssh2.h"
49855bf3acSBen Lindstrom #include "packet.h"
5039be3dc2Sdjm@openbsd.org #include "kex.h"
51c7d39ac8Smarkus@openbsd.org #include "sshbuf.h"
52855bf3acSBen Lindstrom #include "log.h"
537acefbbcSDamien Miller #include "misc.h"
54855bf3acSBen Lindstrom #include "servconf.h"
55855bf3acSBen Lindstrom #include "compat.h"
5600ed75c9Smarkus@openbsd.org #include "sshkey.h"
57d7834353SDamien Miller #include "hostfile.h"
58d7834353SDamien Miller #include "auth.h"
59855bf3acSBen Lindstrom #include "pathnames.h"
60855bf3acSBen Lindstrom #include "uidswap.h"
61855bf3acSBen Lindstrom #include "auth-options.h"
62855bf3acSBen Lindstrom #include "canohost.h"
63d7834353SDamien Miller #ifdef GSSAPI
64d7834353SDamien Miller #include "ssh-gss.h"
65d7834353SDamien Miller #endif
66855bf3acSBen Lindstrom #include "monitor_wrap.h"
671aed65ebSDamien Miller #include "authfile.h"
6830da3447SDamien Miller #include "match.h"
6924232a3eSdjm@openbsd.org #include "ssherr.h"
7024232a3eSdjm@openbsd.org #include "channels.h" /* XXX for session.h */
7124232a3eSdjm@openbsd.org #include "session.h" /* XXX for child_set_env(); refactor? */
720fddf296Sdjm@openbsd.org #include "sk-api.h"
73855bf3acSBen Lindstrom 
74855bf3acSBen Lindstrom /* import */
75855bf3acSBen Lindstrom extern ServerOptions options;
76855bf3acSBen Lindstrom 
7727885632Sdjm@openbsd.org static char *
format_key(const struct sshkey * key)7827885632Sdjm@openbsd.org format_key(const struct sshkey *key)
7927885632Sdjm@openbsd.org {
8027885632Sdjm@openbsd.org 	char *ret, *fp = sshkey_fingerprint(key,
8127885632Sdjm@openbsd.org 	    options.fingerprint_hash, SSH_FP_DEFAULT);
8227885632Sdjm@openbsd.org 
8327885632Sdjm@openbsd.org 	xasprintf(&ret, "%s %s", sshkey_type(key), fp);
8427885632Sdjm@openbsd.org 	free(fp);
8527885632Sdjm@openbsd.org 	return ret;
8627885632Sdjm@openbsd.org }
8727885632Sdjm@openbsd.org 
88855bf3acSBen Lindstrom static int
userauth_pubkey(struct ssh * ssh)89eb272ea4Smarkus@openbsd.org userauth_pubkey(struct ssh *ssh)
90855bf3acSBen Lindstrom {
91eb272ea4Smarkus@openbsd.org 	Authctxt *authctxt = ssh->authctxt;
927c856857Sdjm@openbsd.org 	struct passwd *pw = authctxt->pw;
9374287f5dSdjm@openbsd.org 	struct sshbuf *b = NULL;
9454d90aceSmarkus@openbsd.org 	struct sshkey *key = NULL;
9574287f5dSdjm@openbsd.org 	char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
9674287f5dSdjm@openbsd.org 	u_char *pkblob = NULL, *sig = NULL, have_sig;
9700ed75c9Smarkus@openbsd.org 	size_t blen, slen;
9800ed75c9Smarkus@openbsd.org 	int r, pktype;
99801c9f09Sdjm@openbsd.org 	int req_presence = 0, req_verify = 0, authenticated = 0;
1007c856857Sdjm@openbsd.org 	struct sshauthopt *authopts = NULL;
101b7e74ea0Sdjm@openbsd.org 	struct sshkey_sig_details *sig_details = NULL;
102855bf3acSBen Lindstrom 
10314b5c635Sdjm@openbsd.org 	if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
10414b5c635Sdjm@openbsd.org 	    (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
10500ed75c9Smarkus@openbsd.org 	    (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
106816036f1Sdjm@openbsd.org 		fatal_fr(r, "parse packet");
107ff5d2cf4Sdjm@openbsd.org 
108ff5d2cf4Sdjm@openbsd.org 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) {
109ff5d2cf4Sdjm@openbsd.org 		char *keystring;
110ff5d2cf4Sdjm@openbsd.org 		struct sshbuf *pkbuf;
111ff5d2cf4Sdjm@openbsd.org 
112ff5d2cf4Sdjm@openbsd.org 		if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL)
113816036f1Sdjm@openbsd.org 			fatal_f("sshbuf_from failed");
11416dd8b2cSdjm@openbsd.org 		if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL)
115816036f1Sdjm@openbsd.org 			fatal_f("sshbuf_dtob64 failed");
116816036f1Sdjm@openbsd.org 		debug2_f("%s user %s %s public key %s %s",
117ff5d2cf4Sdjm@openbsd.org 		    authctxt->valid ? "valid" : "invalid", authctxt->user,
118ff5d2cf4Sdjm@openbsd.org 		    have_sig ? "attempting" : "querying", pkalg, keystring);
119ff5d2cf4Sdjm@openbsd.org 		sshbuf_free(pkbuf);
120ff5d2cf4Sdjm@openbsd.org 		free(keystring);
121ff5d2cf4Sdjm@openbsd.org 	}
122ff5d2cf4Sdjm@openbsd.org 
12300ed75c9Smarkus@openbsd.org 	pktype = sshkey_type_from_name(pkalg);
124855bf3acSBen Lindstrom 	if (pktype == KEY_UNSPEC) {
125855bf3acSBen Lindstrom 		/* this is perfectly legal */
126816036f1Sdjm@openbsd.org 		verbose_f("unsupported public key algorithm: %s", pkalg);
127855bf3acSBen Lindstrom 		goto done;
128855bf3acSBen Lindstrom 	}
12900ed75c9Smarkus@openbsd.org 	if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
130816036f1Sdjm@openbsd.org 		error_fr(r, "parse key");
13100ed75c9Smarkus@openbsd.org 		goto done;
13200ed75c9Smarkus@openbsd.org 	}
133855bf3acSBen Lindstrom 	if (key == NULL) {
134816036f1Sdjm@openbsd.org 		error_f("cannot decode key: %s", pkalg);
135855bf3acSBen Lindstrom 		goto done;
136855bf3acSBen Lindstrom 	}
137855bf3acSBen Lindstrom 	if (key->type != pktype) {
138816036f1Sdjm@openbsd.org 		error_f("type mismatch for decoded key "
139816036f1Sdjm@openbsd.org 		    "(received %d, expected %d)", key->type, pktype);
140855bf3acSBen Lindstrom 		goto done;
141855bf3acSBen Lindstrom 	}
14200ed75c9Smarkus@openbsd.org 	if (sshkey_type_plain(key->type) == KEY_RSA &&
14300ed75c9Smarkus@openbsd.org 	    (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
144324541e5SDamien Miller 		logit("Refusing RSA key because client uses unsafe "
145324541e5SDamien Miller 		    "signature scheme");
146324541e5SDamien Miller 		goto done;
147324541e5SDamien Miller 	}
1488f574959Sdjm@openbsd.org 	if (auth2_key_already_used(authctxt, key)) {
14900ed75c9Smarkus@openbsd.org 		logit("refusing previously-used %s key", sshkey_type(key));
150f69b69b8Sdjm@openbsd.org 		goto done;
151f69b69b8Sdjm@openbsd.org 	}
152ee9c0da8Sdtucker@openbsd.org 	if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) {
153ee9c0da8Sdtucker@openbsd.org 		logit_f("key type %s not in PubkeyAcceptedAlgorithms",
154816036f1Sdjm@openbsd.org 		    sshkey_ssh_name(key));
1551f729f06Sdjm@openbsd.org 		goto done;
1561f729f06Sdjm@openbsd.org 	}
15786e5737cSdjm@openbsd.org 	if ((r = sshkey_check_cert_sigtype(key,
15886e5737cSdjm@openbsd.org 	    options.ca_sign_algorithms)) != 0) {
159816036f1Sdjm@openbsd.org 		logit_fr(r, "certificate signature algorithm %s",
16086e5737cSdjm@openbsd.org 		    (key->cert == NULL || key->cert->signature_type == NULL) ?
161816036f1Sdjm@openbsd.org 		    "(null)" : key->cert->signature_type);
16286e5737cSdjm@openbsd.org 		goto done;
16386e5737cSdjm@openbsd.org 	}
16427885632Sdjm@openbsd.org 	key_s = format_key(key);
16527885632Sdjm@openbsd.org 	if (sshkey_is_cert(key))
16627885632Sdjm@openbsd.org 		ca_s = format_key(key->cert->signature_key);
16727885632Sdjm@openbsd.org 
168855bf3acSBen Lindstrom 	if (have_sig) {
169816036f1Sdjm@openbsd.org 		debug3_f("have %s signature for %s%s%s", pkalg, key_s,
170816036f1Sdjm@openbsd.org 		    ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
17100ed75c9Smarkus@openbsd.org 		if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
17200ed75c9Smarkus@openbsd.org 		    (r = sshpkt_get_end(ssh)) != 0)
173816036f1Sdjm@openbsd.org 			fatal_fr(r, "parse signature packet");
17400ed75c9Smarkus@openbsd.org 		if ((b = sshbuf_new()) == NULL)
175816036f1Sdjm@openbsd.org 			fatal_f("sshbuf_new failed");
17600ed75c9Smarkus@openbsd.org 		if (ssh->compat & SSH_OLD_SESSIONID) {
17739be3dc2Sdjm@openbsd.org 			if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0)
178816036f1Sdjm@openbsd.org 				fatal_fr(r, "put old session id");
179855bf3acSBen Lindstrom 		} else {
18039be3dc2Sdjm@openbsd.org 			if ((r = sshbuf_put_stringb(b,
18139be3dc2Sdjm@openbsd.org 			    ssh->kex->session_id)) != 0)
182816036f1Sdjm@openbsd.org 				fatal_fr(r, "put session id");
183855bf3acSBen Lindstrom 		}
18474287f5dSdjm@openbsd.org 		if (!authctxt->valid || authctxt->user == NULL) {
185816036f1Sdjm@openbsd.org 			debug2_f("disabled because of invalid user");
18674287f5dSdjm@openbsd.org 			goto done;
18774287f5dSdjm@openbsd.org 		}
188855bf3acSBen Lindstrom 		/* reconstruct packet */
1894ce189d9SDamien Miller 		xasprintf(&userstyle, "%s%s%s", authctxt->user,
1904ce189d9SDamien Miller 		    authctxt->style ? ":" : "",
1914ce189d9SDamien Miller 		    authctxt->style ? authctxt->style : "");
19200ed75c9Smarkus@openbsd.org 		if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
19300ed75c9Smarkus@openbsd.org 		    (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
19414b5c635Sdjm@openbsd.org 		    (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
19514b5c635Sdjm@openbsd.org 		    (r = sshbuf_put_cstring(b, "publickey")) != 0 ||
19600ed75c9Smarkus@openbsd.org 		    (r = sshbuf_put_u8(b, have_sig)) != 0 ||
197db8bb80eSmestre@openbsd.org 		    (r = sshbuf_put_cstring(b, pkalg)) != 0 ||
19814b5c635Sdjm@openbsd.org 		    (r = sshbuf_put_string(b, pkblob, blen)) != 0)
199816036f1Sdjm@openbsd.org 			fatal_fr(r, "reconstruct packet");
200855bf3acSBen Lindstrom #ifdef DEBUG_PK
20100ed75c9Smarkus@openbsd.org 		sshbuf_dump(b, stderr);
202855bf3acSBen Lindstrom #endif
203855bf3acSBen Lindstrom 		/* test for correct signature */
204855bf3acSBen Lindstrom 		authenticated = 0;
2057c856857Sdjm@openbsd.org 		if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
2064ba0d547Sdjm@openbsd.org 		    PRIVSEP(sshkey_verify(key, sig, slen,
2074ba0d547Sdjm@openbsd.org 		    sshbuf_ptr(b), sshbuf_len(b),
2084ba0d547Sdjm@openbsd.org 		    (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
209b7e74ea0Sdjm@openbsd.org 		    ssh->compat, &sig_details)) == 0) {
210855bf3acSBen Lindstrom 			authenticated = 1;
211f69b69b8Sdjm@openbsd.org 		}
2120fddf296Sdjm@openbsd.org 		if (authenticated == 1 && sig_details != NULL) {
2130fddf296Sdjm@openbsd.org 			auth2_record_info(authctxt, "signature count = %u",
2140fddf296Sdjm@openbsd.org 			    sig_details->sk_counter);
215816036f1Sdjm@openbsd.org 			debug_f("sk_counter = %u, sk_flags = 0x%02x",
216816036f1Sdjm@openbsd.org 			    sig_details->sk_counter, sig_details->sk_flags);
2170fddf296Sdjm@openbsd.org 			req_presence = (options.pubkey_auth_options &
2182e71263bSdjm@openbsd.org 			    PUBKEYAUTH_TOUCH_REQUIRED) ||
2192e71263bSdjm@openbsd.org 			    !authopts->no_require_user_presence;
2200fddf296Sdjm@openbsd.org 			if (req_presence && (sig_details->sk_flags &
2210fddf296Sdjm@openbsd.org 			    SSH_SK_USER_PRESENCE_REQD) == 0) {
2220fddf296Sdjm@openbsd.org 				error("public key %s signature for %s%s from "
2230fddf296Sdjm@openbsd.org 				    "%.128s port %d rejected: user presence "
224a47f6a6cSnaddy@openbsd.org 				    "(authenticator touch) requirement "
225a47f6a6cSnaddy@openbsd.org 				    "not met ", key_s,
2260fddf296Sdjm@openbsd.org 				    authctxt->valid ? "" : "invalid user ",
2270fddf296Sdjm@openbsd.org 				    authctxt->user, ssh_remote_ipaddr(ssh),
2280fddf296Sdjm@openbsd.org 				    ssh_remote_port(ssh));
2290fddf296Sdjm@openbsd.org 				authenticated = 0;
2300fddf296Sdjm@openbsd.org 				goto done;
2310fddf296Sdjm@openbsd.org 			}
232801c9f09Sdjm@openbsd.org 			req_verify = (options.pubkey_auth_options &
233801c9f09Sdjm@openbsd.org 			    PUBKEYAUTH_VERIFY_REQUIRED) ||
234801c9f09Sdjm@openbsd.org 			    authopts->require_verify;
235801c9f09Sdjm@openbsd.org 			if (req_verify && (sig_details->sk_flags &
236801c9f09Sdjm@openbsd.org 			    SSH_SK_USER_VERIFICATION_REQD) == 0) {
237801c9f09Sdjm@openbsd.org 				error("public key %s signature for %s%s from "
238801c9f09Sdjm@openbsd.org 				    "%.128s port %d rejected: user "
239801c9f09Sdjm@openbsd.org 				    "verification requirement not met ", key_s,
240801c9f09Sdjm@openbsd.org 				    authctxt->valid ? "" : "invalid user ",
241801c9f09Sdjm@openbsd.org 				    authctxt->user, ssh_remote_ipaddr(ssh),
242801c9f09Sdjm@openbsd.org 				    ssh_remote_port(ssh));
243801c9f09Sdjm@openbsd.org 				authenticated = 0;
244801c9f09Sdjm@openbsd.org 				goto done;
245801c9f09Sdjm@openbsd.org 			}
246b7e74ea0Sdjm@openbsd.org 		}
2478f574959Sdjm@openbsd.org 		auth2_record_key(authctxt, authenticated, key);
248855bf3acSBen Lindstrom 	} else {
249816036f1Sdjm@openbsd.org 		debug_f("test pkalg %s pkblob %s%s%s", pkalg, key_s,
250816036f1Sdjm@openbsd.org 		    ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
25127885632Sdjm@openbsd.org 
25200ed75c9Smarkus@openbsd.org 		if ((r = sshpkt_get_end(ssh)) != 0)
253816036f1Sdjm@openbsd.org 			fatal_fr(r, "parse packet");
254855bf3acSBen Lindstrom 
25574287f5dSdjm@openbsd.org 		if (!authctxt->valid || authctxt->user == NULL) {
256816036f1Sdjm@openbsd.org 			debug2_f("disabled because of invalid user");
25774287f5dSdjm@openbsd.org 			goto done;
25874287f5dSdjm@openbsd.org 		}
259855bf3acSBen Lindstrom 		/* XXX fake reply and always send PK_OK ? */
260855bf3acSBen Lindstrom 		/*
261855bf3acSBen Lindstrom 		 * XXX this allows testing whether a user is allowed
262855bf3acSBen Lindstrom 		 * to login: if you happen to have a valid pubkey this
263855bf3acSBen Lindstrom 		 * message is sent. the message is NEVER sent at all
264855bf3acSBen Lindstrom 		 * if a user is not allowed to login. is this an
265855bf3acSBen Lindstrom 		 * issue? -markus
266855bf3acSBen Lindstrom 		 */
2677c856857Sdjm@openbsd.org 		if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
26800ed75c9Smarkus@openbsd.org 			if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
26900ed75c9Smarkus@openbsd.org 			    != 0 ||
27000ed75c9Smarkus@openbsd.org 			    (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
27100ed75c9Smarkus@openbsd.org 			    (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
272394a842eSmarkus@openbsd.org 			    (r = sshpkt_send(ssh)) != 0 ||
273394a842eSmarkus@openbsd.org 			    (r = ssh_packet_write_wait(ssh)) != 0)
274816036f1Sdjm@openbsd.org 				fatal_fr(r, "send packet");
275855bf3acSBen Lindstrom 			authctxt->postponed = 1;
276855bf3acSBen Lindstrom 		}
277855bf3acSBen Lindstrom 	}
278855bf3acSBen Lindstrom done:
2797c856857Sdjm@openbsd.org 	if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) {
280816036f1Sdjm@openbsd.org 		debug_f("key options inconsistent with existing");
2817c856857Sdjm@openbsd.org 		authenticated = 0;
2827c856857Sdjm@openbsd.org 	}
283816036f1Sdjm@openbsd.org 	debug2_f("authenticated %d pkalg %s", authenticated, pkalg);
2847c856857Sdjm@openbsd.org 
2857fef173cSdjm@openbsd.org 	sshbuf_free(b);
2867c856857Sdjm@openbsd.org 	sshauthopt_free(authopts);
28700ed75c9Smarkus@openbsd.org 	sshkey_free(key);
28800ed75c9Smarkus@openbsd.org 	free(userstyle);
289a627d42eSDarren Tucker 	free(pkalg);
290a627d42eSDarren Tucker 	free(pkblob);
29127885632Sdjm@openbsd.org 	free(key_s);
29227885632Sdjm@openbsd.org 	free(ca_s);
29374287f5dSdjm@openbsd.org 	free(sig);
294b7e74ea0Sdjm@openbsd.org 	sshkey_sig_details_free(sig_details);
295855bf3acSBen Lindstrom 	return authenticated;
296855bf3acSBen Lindstrom }
297855bf3acSBen Lindstrom 
29830da3447SDamien Miller static int
match_principals_option(const char * principal_list,struct sshkey_cert * cert)2998668706dSDamien Miller match_principals_option(const char *principal_list, struct sshkey_cert *cert)
30030da3447SDamien Miller {
30130da3447SDamien Miller 	char *result;
30230da3447SDamien Miller 	u_int i;
30330da3447SDamien Miller 
30430da3447SDamien Miller 	/* XXX percent_expand() sequences for authorized_principals? */
30530da3447SDamien Miller 
30630da3447SDamien Miller 	for (i = 0; i < cert->nprincipals; i++) {
30730da3447SDamien Miller 		if ((result = match_list(cert->principals[i],
30830da3447SDamien Miller 		    principal_list, NULL)) != NULL) {
30930da3447SDamien Miller 			debug3("matched principal from key options \"%.100s\"",
31030da3447SDamien Miller 			    result);
311a627d42eSDarren Tucker 			free(result);
31230da3447SDamien Miller 			return 1;
31330da3447SDamien Miller 		}
31430da3447SDamien Miller 	}
31530da3447SDamien Miller 	return 0;
31630da3447SDamien Miller }
31730da3447SDamien Miller 
3187c856857Sdjm@openbsd.org /*
3197c856857Sdjm@openbsd.org  * Process a single authorized_principals format line. Returns 0 and sets
3207c856857Sdjm@openbsd.org  * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
3217c856857Sdjm@openbsd.org  * log preamble for file/line information.
3227c856857Sdjm@openbsd.org  */
32330da3447SDamien Miller static int
check_principals_line(struct ssh * ssh,char * cp,const struct sshkey_cert * cert,const char * loc,struct sshauthopt ** authoptsp)3247c856857Sdjm@openbsd.org check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert,
3257c856857Sdjm@openbsd.org     const char *loc, struct sshauthopt **authoptsp)
32630da3447SDamien Miller {
3277c856857Sdjm@openbsd.org 	u_int i, found = 0;
3287c856857Sdjm@openbsd.org 	char *ep, *line_opts;
3297c856857Sdjm@openbsd.org 	const char *reason = NULL;
3307c856857Sdjm@openbsd.org 	struct sshauthopt *opts = NULL;
33130da3447SDamien Miller 
3327c856857Sdjm@openbsd.org 	if (authoptsp != NULL)
3337c856857Sdjm@openbsd.org 		*authoptsp = NULL;
3347c856857Sdjm@openbsd.org 
3356018a368SDamien Miller 	/* Trim trailing whitespace. */
3366018a368SDamien Miller 	ep = cp + strlen(cp) - 1;
3376018a368SDamien Miller 	while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
3386018a368SDamien Miller 		*ep-- = '\0';
3397c856857Sdjm@openbsd.org 
3406018a368SDamien Miller 	/*
3416018a368SDamien Miller 	 * If the line has internal whitespace then assume it has
3426018a368SDamien Miller 	 * key options.
3436018a368SDamien Miller 	 */
3446018a368SDamien Miller 	line_opts = NULL;
3456018a368SDamien Miller 	if ((ep = strrchr(cp, ' ')) != NULL ||
3466018a368SDamien Miller 	    (ep = strrchr(cp, '\t')) != NULL) {
3476018a368SDamien Miller 		for (; *ep == ' ' || *ep == '\t'; ep++)
348188ea814SDamien Miller 			;
3496018a368SDamien Miller 		line_opts = cp;
3506018a368SDamien Miller 		cp = ep;
3516018a368SDamien Miller 	}
3527c856857Sdjm@openbsd.org 	if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
3537c856857Sdjm@openbsd.org 		debug("%s: bad principals options: %s", loc, reason);
3547c856857Sdjm@openbsd.org 		auth_debug_add("%s: bad principals options: %s", loc, reason);
3557c856857Sdjm@openbsd.org 		return -1;
3567c856857Sdjm@openbsd.org 	}
3577c856857Sdjm@openbsd.org 	/* Check principals in cert against those on line */
35830da3447SDamien Miller 	for (i = 0; i < cert->nprincipals; i++) {
3597c856857Sdjm@openbsd.org 		if (strcmp(cp, cert->principals[i]) != 0)
3606018a368SDamien Miller 			continue;
3617c856857Sdjm@openbsd.org 		debug3("%s: matched principal \"%.100s\"",
3627c856857Sdjm@openbsd.org 		    loc, cert->principals[i]);
3637c856857Sdjm@openbsd.org 		found = 1;
3647c856857Sdjm@openbsd.org 	}
3657c856857Sdjm@openbsd.org 	if (found && authoptsp != NULL) {
3667c856857Sdjm@openbsd.org 		*authoptsp = opts;
3677c856857Sdjm@openbsd.org 		opts = NULL;
3687c856857Sdjm@openbsd.org 	}
3697c856857Sdjm@openbsd.org 	sshauthopt_free(opts);
3707c856857Sdjm@openbsd.org 	return found ? 0 : -1;
3717c856857Sdjm@openbsd.org }
3727c856857Sdjm@openbsd.org 
3737c856857Sdjm@openbsd.org static int
process_principals(struct ssh * ssh,FILE * f,const char * file,const struct sshkey_cert * cert,struct sshauthopt ** authoptsp)3747c856857Sdjm@openbsd.org process_principals(struct ssh *ssh, FILE *f, const char *file,
3757c856857Sdjm@openbsd.org     const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
3767c856857Sdjm@openbsd.org {
3777f906352Smarkus@openbsd.org 	char loc[256], *line = NULL, *cp, *ep;
3787f906352Smarkus@openbsd.org 	size_t linesize = 0;
3797c856857Sdjm@openbsd.org 	u_long linenum = 0;
3807c856857Sdjm@openbsd.org 	u_int found_principal = 0;
3817c856857Sdjm@openbsd.org 
3827c856857Sdjm@openbsd.org 	if (authoptsp != NULL)
3837c856857Sdjm@openbsd.org 		*authoptsp = NULL;
3847c856857Sdjm@openbsd.org 
3857f906352Smarkus@openbsd.org 	while (getline(&line, &linesize, f) != -1) {
3867f906352Smarkus@openbsd.org 		linenum++;
3877c856857Sdjm@openbsd.org 		/* Always consume entire input */
3887c856857Sdjm@openbsd.org 		if (found_principal)
3897c856857Sdjm@openbsd.org 			continue;
3907c856857Sdjm@openbsd.org 
3917c856857Sdjm@openbsd.org 		/* Skip leading whitespace. */
3927c856857Sdjm@openbsd.org 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
3937c856857Sdjm@openbsd.org 			;
3947c856857Sdjm@openbsd.org 		/* Skip blank and comment lines. */
3957c856857Sdjm@openbsd.org 		if ((ep = strchr(cp, '#')) != NULL)
3967c856857Sdjm@openbsd.org 			*ep = '\0';
3977c856857Sdjm@openbsd.org 		if (!*cp || *cp == '\n')
3987c856857Sdjm@openbsd.org 			continue;
3997c856857Sdjm@openbsd.org 
4007c856857Sdjm@openbsd.org 		snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
4017c856857Sdjm@openbsd.org 		if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0)
40252763dd3Sdjm@openbsd.org 			found_principal = 1;
40330da3447SDamien Miller 	}
4047f906352Smarkus@openbsd.org 	free(line);
40552763dd3Sdjm@openbsd.org 	return found_principal;
40630da3447SDamien Miller }
40730da3447SDamien Miller 
4087c856857Sdjm@openbsd.org /* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
4097c856857Sdjm@openbsd.org 
410bcc50d81Sdjm@openbsd.org static int
match_principals_file(struct ssh * ssh,struct passwd * pw,char * file,struct sshkey_cert * cert,struct sshauthopt ** authoptsp)4117c856857Sdjm@openbsd.org match_principals_file(struct ssh *ssh, struct passwd *pw, char *file,
4127c856857Sdjm@openbsd.org     struct sshkey_cert *cert, struct sshauthopt **authoptsp)
413bcc50d81Sdjm@openbsd.org {
414bcc50d81Sdjm@openbsd.org 	FILE *f;
415bcc50d81Sdjm@openbsd.org 	int success;
416bcc50d81Sdjm@openbsd.org 
4177c856857Sdjm@openbsd.org 	if (authoptsp != NULL)
4187c856857Sdjm@openbsd.org 		*authoptsp = NULL;
4197c856857Sdjm@openbsd.org 
420bcc50d81Sdjm@openbsd.org 	temporarily_use_uid(pw);
421bcc50d81Sdjm@openbsd.org 	debug("trying authorized principals file %s", file);
422bcc50d81Sdjm@openbsd.org 	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
423bcc50d81Sdjm@openbsd.org 		restore_uid();
424bcc50d81Sdjm@openbsd.org 		return 0;
425bcc50d81Sdjm@openbsd.org 	}
4267c856857Sdjm@openbsd.org 	success = process_principals(ssh, f, file, cert, authoptsp);
427bcc50d81Sdjm@openbsd.org 	fclose(f);
428bcc50d81Sdjm@openbsd.org 	restore_uid();
429bcc50d81Sdjm@openbsd.org 	return success;
430bcc50d81Sdjm@openbsd.org }
431bcc50d81Sdjm@openbsd.org 
432bcc50d81Sdjm@openbsd.org /*
433bcc50d81Sdjm@openbsd.org  * Checks whether principal is allowed in output of command.
434bcc50d81Sdjm@openbsd.org  * returns 1 if the principal is allowed or 0 otherwise.
435bcc50d81Sdjm@openbsd.org  */
436bcc50d81Sdjm@openbsd.org static int
match_principals_command(struct ssh * ssh,struct passwd * user_pw,const struct sshkey * key,struct sshauthopt ** authoptsp)4377c856857Sdjm@openbsd.org match_principals_command(struct ssh *ssh, struct passwd *user_pw,
4387c856857Sdjm@openbsd.org     const struct sshkey *key, struct sshauthopt **authoptsp)
439bcc50d81Sdjm@openbsd.org {
4407c856857Sdjm@openbsd.org 	struct passwd *runas_pw = NULL;
441e7907c1cSdjm@openbsd.org 	const struct sshkey_cert *cert = key->cert;
442bcc50d81Sdjm@openbsd.org 	FILE *f = NULL;
443e7907c1cSdjm@openbsd.org 	int r, ok, found_principal = 0;
444bcc50d81Sdjm@openbsd.org 	int i, ac = 0, uid_swapped = 0;
445bcc50d81Sdjm@openbsd.org 	pid_t pid;
446bcc50d81Sdjm@openbsd.org 	char *tmp, *username = NULL, *command = NULL, **av = NULL;
447e7907c1cSdjm@openbsd.org 	char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
44830615295Sdjm@openbsd.org 	char serial_s[32], uidstr[32];
449bcc50d81Sdjm@openbsd.org 	void (*osigchld)(int);
450bcc50d81Sdjm@openbsd.org 
4517c856857Sdjm@openbsd.org 	if (authoptsp != NULL)
4527c856857Sdjm@openbsd.org 		*authoptsp = NULL;
453bcc50d81Sdjm@openbsd.org 	if (options.authorized_principals_command == NULL)
454bcc50d81Sdjm@openbsd.org 		return 0;
455bcc50d81Sdjm@openbsd.org 	if (options.authorized_principals_command_user == NULL) {
456bcc50d81Sdjm@openbsd.org 		error("No user for AuthorizedPrincipalsCommand specified, "
457bcc50d81Sdjm@openbsd.org 		    "skipping");
458bcc50d81Sdjm@openbsd.org 		return 0;
459bcc50d81Sdjm@openbsd.org 	}
460bcc50d81Sdjm@openbsd.org 
461bcc50d81Sdjm@openbsd.org 	/*
462bcc50d81Sdjm@openbsd.org 	 * NB. all returns later this function should go via "out" to
463bcc50d81Sdjm@openbsd.org 	 * ensure the original SIGCHLD handler is restored properly.
464bcc50d81Sdjm@openbsd.org 	 */
4653bf2a6acSdtucker@openbsd.org 	osigchld = ssh_signal(SIGCHLD, SIG_DFL);
466bcc50d81Sdjm@openbsd.org 
467bcc50d81Sdjm@openbsd.org 	/* Prepare and verify the user for the command */
468bcc50d81Sdjm@openbsd.org 	username = percent_expand(options.authorized_principals_command_user,
469bcc50d81Sdjm@openbsd.org 	    "u", user_pw->pw_name, (char *)NULL);
4707c856857Sdjm@openbsd.org 	runas_pw = getpwnam(username);
4717c856857Sdjm@openbsd.org 	if (runas_pw == NULL) {
472bcc50d81Sdjm@openbsd.org 		error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
473bcc50d81Sdjm@openbsd.org 		    username, strerror(errno));
474bcc50d81Sdjm@openbsd.org 		goto out;
475bcc50d81Sdjm@openbsd.org 	}
476bcc50d81Sdjm@openbsd.org 
477bcc50d81Sdjm@openbsd.org 	/* Turn the command into an argument vector */
478de4ae07fSdjm@openbsd.org 	if (argv_split(options.authorized_principals_command, &ac, &av) != 0) {
479bcc50d81Sdjm@openbsd.org 		error("AuthorizedPrincipalsCommand \"%s\" contains "
4804cd6b12cSdjm@openbsd.org 		    "invalid quotes", options.authorized_principals_command);
481bcc50d81Sdjm@openbsd.org 		goto out;
482bcc50d81Sdjm@openbsd.org 	}
483bcc50d81Sdjm@openbsd.org 	if (ac == 0) {
484bcc50d81Sdjm@openbsd.org 		error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
4854cd6b12cSdjm@openbsd.org 		    options.authorized_principals_command);
486bcc50d81Sdjm@openbsd.org 		goto out;
487bcc50d81Sdjm@openbsd.org 	}
488e7907c1cSdjm@openbsd.org 	if ((ca_fp = sshkey_fingerprint(cert->signature_key,
489e7907c1cSdjm@openbsd.org 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
490816036f1Sdjm@openbsd.org 		error_f("sshkey_fingerprint failed");
491e7907c1cSdjm@openbsd.org 		goto out;
492e7907c1cSdjm@openbsd.org 	}
49300df97ffSdjm@openbsd.org 	if ((key_fp = sshkey_fingerprint(key,
494e7907c1cSdjm@openbsd.org 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
495816036f1Sdjm@openbsd.org 		error_f("sshkey_fingerprint failed");
496e7907c1cSdjm@openbsd.org 		goto out;
497e7907c1cSdjm@openbsd.org 	}
498e7907c1cSdjm@openbsd.org 	if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
499816036f1Sdjm@openbsd.org 		error_fr(r, "sshkey_to_base64 failed");
500e7907c1cSdjm@openbsd.org 		goto out;
501e7907c1cSdjm@openbsd.org 	}
502e7907c1cSdjm@openbsd.org 	if ((r = sshkey_to_base64(key, &keytext)) != 0) {
503816036f1Sdjm@openbsd.org 		error_fr(r, "sshkey_to_base64 failed");
504e7907c1cSdjm@openbsd.org 		goto out;
505e7907c1cSdjm@openbsd.org 	}
506f83a0cfeSdjm@openbsd.org 	snprintf(serial_s, sizeof(serial_s), "%llu",
507f83a0cfeSdjm@openbsd.org 	    (unsigned long long)cert->serial);
5089c935dd9Sdjm@openbsd.org 	snprintf(uidstr, sizeof(uidstr), "%llu",
5099c935dd9Sdjm@openbsd.org 	    (unsigned long long)user_pw->pw_uid);
510bcc50d81Sdjm@openbsd.org 	for (i = 1; i < ac; i++) {
511bcc50d81Sdjm@openbsd.org 		tmp = percent_expand(av[i],
5129c935dd9Sdjm@openbsd.org 		    "U", uidstr,
513bcc50d81Sdjm@openbsd.org 		    "u", user_pw->pw_name,
514bcc50d81Sdjm@openbsd.org 		    "h", user_pw->pw_dir,
515e7907c1cSdjm@openbsd.org 		    "t", sshkey_ssh_name(key),
516e7907c1cSdjm@openbsd.org 		    "T", sshkey_ssh_name(cert->signature_key),
517e7907c1cSdjm@openbsd.org 		    "f", key_fp,
518e7907c1cSdjm@openbsd.org 		    "F", ca_fp,
519e7907c1cSdjm@openbsd.org 		    "k", keytext,
520e7907c1cSdjm@openbsd.org 		    "K", catext,
521bfa9d969Sdjm@openbsd.org 		    "i", cert->key_id,
522bfa9d969Sdjm@openbsd.org 		    "s", serial_s,
523bcc50d81Sdjm@openbsd.org 		    (char *)NULL);
524bcc50d81Sdjm@openbsd.org 		if (tmp == NULL)
525816036f1Sdjm@openbsd.org 			fatal_f("percent_expand failed");
526bcc50d81Sdjm@openbsd.org 		free(av[i]);
527bcc50d81Sdjm@openbsd.org 		av[i] = tmp;
528bcc50d81Sdjm@openbsd.org 	}
529bcc50d81Sdjm@openbsd.org 	/* Prepare a printable command for logs, etc. */
530de4ae07fSdjm@openbsd.org 	command = argv_assemble(ac, av);
531bcc50d81Sdjm@openbsd.org 
532a34e14a5Sdjm@openbsd.org 	if ((pid = subprocess("AuthorizedPrincipalsCommand", command,
533de4ae07fSdjm@openbsd.org 	    ac, av, &f,
534a34e14a5Sdjm@openbsd.org 	    SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
535a34e14a5Sdjm@openbsd.org 	    runas_pw, temporarily_use_uid, restore_uid)) == 0)
536bcc50d81Sdjm@openbsd.org 		goto out;
537bcc50d81Sdjm@openbsd.org 
538bcc50d81Sdjm@openbsd.org 	uid_swapped = 1;
5397c856857Sdjm@openbsd.org 	temporarily_use_uid(runas_pw);
540bcc50d81Sdjm@openbsd.org 
5417c856857Sdjm@openbsd.org 	ok = process_principals(ssh, f, "(command)", cert, authoptsp);
542bcc50d81Sdjm@openbsd.org 
543ddd3d34eSdjm@openbsd.org 	fclose(f);
544ddd3d34eSdjm@openbsd.org 	f = NULL;
545ddd3d34eSdjm@openbsd.org 
546b074c3c3Sdjm@openbsd.org 	if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
547bcc50d81Sdjm@openbsd.org 		goto out;
548bcc50d81Sdjm@openbsd.org 
549bcc50d81Sdjm@openbsd.org 	/* Read completed successfully */
550bcc50d81Sdjm@openbsd.org 	found_principal = ok;
551bcc50d81Sdjm@openbsd.org  out:
552bcc50d81Sdjm@openbsd.org 	if (f != NULL)
553bcc50d81Sdjm@openbsd.org 		fclose(f);
5543bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGCHLD, osigchld);
555bcc50d81Sdjm@openbsd.org 	for (i = 0; i < ac; i++)
556bcc50d81Sdjm@openbsd.org 		free(av[i]);
557bcc50d81Sdjm@openbsd.org 	free(av);
558bcc50d81Sdjm@openbsd.org 	if (uid_swapped)
559bcc50d81Sdjm@openbsd.org 		restore_uid();
560bcc50d81Sdjm@openbsd.org 	free(command);
561bcc50d81Sdjm@openbsd.org 	free(username);
562e7907c1cSdjm@openbsd.org 	free(ca_fp);
563e7907c1cSdjm@openbsd.org 	free(key_fp);
564e7907c1cSdjm@openbsd.org 	free(catext);
565e7907c1cSdjm@openbsd.org 	free(keytext);
566bcc50d81Sdjm@openbsd.org 	return found_principal;
567bcc50d81Sdjm@openbsd.org }
5687c856857Sdjm@openbsd.org 
56909d3e125SDamien Miller /*
5707c856857Sdjm@openbsd.org  * Check a single line of an authorized_keys-format file. Returns 0 if key
5717c856857Sdjm@openbsd.org  * matches, -1 otherwise. Will return key/cert options via *authoptsp
5727c856857Sdjm@openbsd.org  * on success. "loc" is used as file/line location in log messages.
5737c856857Sdjm@openbsd.org  */
5747c856857Sdjm@openbsd.org static int
check_authkey_line(struct ssh * ssh,struct passwd * pw,struct sshkey * key,char * cp,const char * loc,struct sshauthopt ** authoptsp)5757c856857Sdjm@openbsd.org check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
5767c856857Sdjm@openbsd.org     char *cp, const char *loc, struct sshauthopt **authoptsp)
5777c856857Sdjm@openbsd.org {
5787c856857Sdjm@openbsd.org 	int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
5797c856857Sdjm@openbsd.org 	struct sshkey *found = NULL;
5807c856857Sdjm@openbsd.org 	struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
5817c856857Sdjm@openbsd.org 	char *key_options = NULL, *fp = NULL;
5827c856857Sdjm@openbsd.org 	const char *reason = NULL;
5837c856857Sdjm@openbsd.org 	int ret = -1;
5847c856857Sdjm@openbsd.org 
5857c856857Sdjm@openbsd.org 	if (authoptsp != NULL)
5867c856857Sdjm@openbsd.org 		*authoptsp = NULL;
5877c856857Sdjm@openbsd.org 
5887c856857Sdjm@openbsd.org 	if ((found = sshkey_new(want_keytype)) == NULL) {
589816036f1Sdjm@openbsd.org 		debug3_f("keytype %d failed", want_keytype);
5907c856857Sdjm@openbsd.org 		goto out;
5917c856857Sdjm@openbsd.org 	}
5927c856857Sdjm@openbsd.org 
5937c856857Sdjm@openbsd.org 	/* XXX djm: peek at key type in line and skip if unwanted */
5947c856857Sdjm@openbsd.org 
59500ed75c9Smarkus@openbsd.org 	if (sshkey_read(found, &cp) != 0) {
5967c856857Sdjm@openbsd.org 		/* no key?  check for options */
5977c856857Sdjm@openbsd.org 		debug2("%s: check options: '%s'", loc, cp);
5987c856857Sdjm@openbsd.org 		key_options = cp;
599dd8002fbSdjm@openbsd.org 		if (sshkey_advance_past_options(&cp) != 0) {
6007c856857Sdjm@openbsd.org 			reason = "invalid key option string";
6017c856857Sdjm@openbsd.org 			goto fail_reason;
6027c856857Sdjm@openbsd.org 		}
6037c856857Sdjm@openbsd.org 		skip_space(&cp);
6047c856857Sdjm@openbsd.org 		if (sshkey_read(found, &cp) != 0) {
605855bf3acSBen Lindstrom 			/* still no key?  advance to next line*/
6067c856857Sdjm@openbsd.org 			debug2("%s: advance: '%s'", loc, cp);
6077c856857Sdjm@openbsd.org 			goto out;
608855bf3acSBen Lindstrom 		}
609855bf3acSBen Lindstrom 	}
6107c856857Sdjm@openbsd.org 	/* Parse key options now; we need to know if this is a CA key */
6117c856857Sdjm@openbsd.org 	if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
6127c856857Sdjm@openbsd.org 		debug("%s: bad key options: %s", loc, reason);
6137c856857Sdjm@openbsd.org 		auth_debug_add("%s: bad key options: %s", loc, reason);
6147c856857Sdjm@openbsd.org 		goto out;
6157c856857Sdjm@openbsd.org 	}
6167c856857Sdjm@openbsd.org 	/* Ignore keys that don't match or incorrectly marked as CAs */
61700ed75c9Smarkus@openbsd.org 	if (sshkey_is_cert(key)) {
6187c856857Sdjm@openbsd.org 		/* Certificate; check signature key against CA */
6197c856857Sdjm@openbsd.org 		if (!sshkey_equal(found, key->cert->signature_key) ||
6207c856857Sdjm@openbsd.org 		    !keyopts->cert_authority)
6217c856857Sdjm@openbsd.org 			goto out;
6227c856857Sdjm@openbsd.org 	} else {
6237c856857Sdjm@openbsd.org 		/* Plain key: check it against key found in file */
6247c856857Sdjm@openbsd.org 		if (!sshkey_equal(found, key) || keyopts->cert_authority)
6257c856857Sdjm@openbsd.org 			goto out;
6267c856857Sdjm@openbsd.org 	}
6277c856857Sdjm@openbsd.org 
6287c856857Sdjm@openbsd.org 	/* We have a candidate key, perform authorisation checks */
6299ce86c92Sdjm@openbsd.org 	if ((fp = sshkey_fingerprint(found,
6309ce86c92Sdjm@openbsd.org 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
631816036f1Sdjm@openbsd.org 		fatal_f("fingerprint failed");
6327c856857Sdjm@openbsd.org 
6337c856857Sdjm@openbsd.org 	debug("%s: matching %s found: %s %s", loc,
6347c856857Sdjm@openbsd.org 	    sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
6357c856857Sdjm@openbsd.org 
6367c856857Sdjm@openbsd.org 	if (auth_authorise_keyopts(ssh, pw, keyopts,
6377c856857Sdjm@openbsd.org 	    sshkey_is_cert(key), loc) != 0) {
6387c856857Sdjm@openbsd.org 		reason = "Refused by key options";
6397c856857Sdjm@openbsd.org 		goto fail_reason;
6407c856857Sdjm@openbsd.org 	}
6417c856857Sdjm@openbsd.org 	/* That's all we need for plain keys. */
6427c856857Sdjm@openbsd.org 	if (!sshkey_is_cert(key)) {
6437c856857Sdjm@openbsd.org 		verbose("Accepted key %s %s found at %s",
6447c856857Sdjm@openbsd.org 		    sshkey_type(found), fp, loc);
6457c856857Sdjm@openbsd.org 		finalopts = keyopts;
6467c856857Sdjm@openbsd.org 		keyopts = NULL;
6477c856857Sdjm@openbsd.org 		goto success;
6487c856857Sdjm@openbsd.org 	}
6497c856857Sdjm@openbsd.org 
6507c856857Sdjm@openbsd.org 	/*
6517c856857Sdjm@openbsd.org 	 * Additional authorisation for certificates.
6527c856857Sdjm@openbsd.org 	 */
6537c856857Sdjm@openbsd.org 
6547c856857Sdjm@openbsd.org 	/* Parse and check options present in certificate */
6557c856857Sdjm@openbsd.org 	if ((certopts = sshauthopt_from_cert(key)) == NULL) {
6567c856857Sdjm@openbsd.org 		reason = "Invalid certificate options";
6577c856857Sdjm@openbsd.org 		goto fail_reason;
6587c856857Sdjm@openbsd.org 	}
6597c856857Sdjm@openbsd.org 	if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) {
6607c856857Sdjm@openbsd.org 		reason = "Refused by certificate options";
6617c856857Sdjm@openbsd.org 		goto fail_reason;
6627c856857Sdjm@openbsd.org 	}
6637c856857Sdjm@openbsd.org 	if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
6647c856857Sdjm@openbsd.org 		goto fail_reason;
6657c856857Sdjm@openbsd.org 
66630da3447SDamien Miller 	/*
66730da3447SDamien Miller 	 * If the user has specified a list of principals as
66830da3447SDamien Miller 	 * a key option, then prefer that list to matching
66930da3447SDamien Miller 	 * their username in the certificate principals list.
670