xref: /dflybsd-src/crypto/openssh/auth2-pubkey.c (revision 856ea928c995e02ea88a6865c1f1983842decc66)
1*856ea928SPeter Avalos /* $OpenBSD: auth2-pubkey.c,v 1.26 2010/06/29 23:16:46 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
618de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
718de8d7fSPeter Avalos  * are met:
818de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
918de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1018de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1218de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1318de8d7fSPeter Avalos  *
1418de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1518de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1818de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2018de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418de8d7fSPeter Avalos  */
2518de8d7fSPeter Avalos 
2618de8d7fSPeter Avalos #include "includes.h"
2718de8d7fSPeter Avalos 
2818de8d7fSPeter Avalos #include <sys/types.h>
2918de8d7fSPeter Avalos #include <sys/stat.h>
3018de8d7fSPeter Avalos 
3118de8d7fSPeter Avalos #include <fcntl.h>
3218de8d7fSPeter Avalos #include <pwd.h>
3318de8d7fSPeter Avalos #include <stdio.h>
3418de8d7fSPeter Avalos #include <stdarg.h>
35*856ea928SPeter Avalos #include <string.h>
36*856ea928SPeter Avalos #include <time.h>
3718de8d7fSPeter Avalos #include <unistd.h>
3818de8d7fSPeter Avalos 
3918de8d7fSPeter Avalos #include "xmalloc.h"
4018de8d7fSPeter Avalos #include "ssh.h"
4118de8d7fSPeter Avalos #include "ssh2.h"
4218de8d7fSPeter Avalos #include "packet.h"
4318de8d7fSPeter Avalos #include "buffer.h"
4418de8d7fSPeter Avalos #include "log.h"
4518de8d7fSPeter Avalos #include "servconf.h"
4618de8d7fSPeter Avalos #include "compat.h"
4718de8d7fSPeter Avalos #include "key.h"
4818de8d7fSPeter Avalos #include "hostfile.h"
4918de8d7fSPeter Avalos #include "auth.h"
5018de8d7fSPeter Avalos #include "pathnames.h"
5118de8d7fSPeter Avalos #include "uidswap.h"
5218de8d7fSPeter Avalos #include "auth-options.h"
5318de8d7fSPeter Avalos #include "canohost.h"
5418de8d7fSPeter Avalos #ifdef GSSAPI
5518de8d7fSPeter Avalos #include "ssh-gss.h"
5618de8d7fSPeter Avalos #endif
5718de8d7fSPeter Avalos #include "monitor_wrap.h"
5818de8d7fSPeter Avalos #include "misc.h"
59*856ea928SPeter Avalos #include "authfile.h"
60*856ea928SPeter Avalos #include "match.h"
6118de8d7fSPeter Avalos 
6218de8d7fSPeter Avalos /* import */
6318de8d7fSPeter Avalos extern ServerOptions options;
6418de8d7fSPeter Avalos extern u_char *session_id2;
6518de8d7fSPeter Avalos extern u_int session_id2_len;
6618de8d7fSPeter Avalos 
6718de8d7fSPeter Avalos static int
6818de8d7fSPeter Avalos userauth_pubkey(Authctxt *authctxt)
6918de8d7fSPeter Avalos {
7018de8d7fSPeter Avalos 	Buffer b;
7118de8d7fSPeter Avalos 	Key *key = NULL;
7218de8d7fSPeter Avalos 	char *pkalg;
7318de8d7fSPeter Avalos 	u_char *pkblob, *sig;
7418de8d7fSPeter Avalos 	u_int alen, blen, slen;
7518de8d7fSPeter Avalos 	int have_sig, pktype;
7618de8d7fSPeter Avalos 	int authenticated = 0;
7718de8d7fSPeter Avalos 
7818de8d7fSPeter Avalos 	if (!authctxt->valid) {
7918de8d7fSPeter Avalos 		debug2("userauth_pubkey: disabled because of invalid user");
8018de8d7fSPeter Avalos 		return 0;
8118de8d7fSPeter Avalos 	}
8218de8d7fSPeter Avalos 	have_sig = packet_get_char();
8318de8d7fSPeter Avalos 	if (datafellows & SSH_BUG_PKAUTH) {
8418de8d7fSPeter Avalos 		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
8518de8d7fSPeter Avalos 		/* no explicit pkalg given */
8618de8d7fSPeter Avalos 		pkblob = packet_get_string(&blen);
8718de8d7fSPeter Avalos 		buffer_init(&b);
8818de8d7fSPeter Avalos 		buffer_append(&b, pkblob, blen);
8918de8d7fSPeter Avalos 		/* so we have to extract the pkalg from the pkblob */
9018de8d7fSPeter Avalos 		pkalg = buffer_get_string(&b, &alen);
9118de8d7fSPeter Avalos 		buffer_free(&b);
9218de8d7fSPeter Avalos 	} else {
9318de8d7fSPeter Avalos 		pkalg = packet_get_string(&alen);
9418de8d7fSPeter Avalos 		pkblob = packet_get_string(&blen);
9518de8d7fSPeter Avalos 	}
9618de8d7fSPeter Avalos 	pktype = key_type_from_name(pkalg);
9718de8d7fSPeter Avalos 	if (pktype == KEY_UNSPEC) {
9818de8d7fSPeter Avalos 		/* this is perfectly legal */
9918de8d7fSPeter Avalos 		logit("userauth_pubkey: unsupported public key algorithm: %s",
10018de8d7fSPeter Avalos 		    pkalg);
10118de8d7fSPeter Avalos 		goto done;
10218de8d7fSPeter Avalos 	}
10318de8d7fSPeter Avalos 	key = key_from_blob(pkblob, blen);
10418de8d7fSPeter Avalos 	if (key == NULL) {
10518de8d7fSPeter Avalos 		error("userauth_pubkey: cannot decode key: %s", pkalg);
10618de8d7fSPeter Avalos 		goto done;
10718de8d7fSPeter Avalos 	}
10818de8d7fSPeter Avalos 	if (key->type != pktype) {
10918de8d7fSPeter Avalos 		error("userauth_pubkey: type mismatch for decoded key "
11018de8d7fSPeter Avalos 		    "(received %d, expected %d)", key->type, pktype);
11118de8d7fSPeter Avalos 		goto done;
11218de8d7fSPeter Avalos 	}
11318de8d7fSPeter Avalos 	if (have_sig) {
11418de8d7fSPeter Avalos 		sig = packet_get_string(&slen);
11518de8d7fSPeter Avalos 		packet_check_eom();
11618de8d7fSPeter Avalos 		buffer_init(&b);
11718de8d7fSPeter Avalos 		if (datafellows & SSH_OLD_SESSIONID) {
11818de8d7fSPeter Avalos 			buffer_append(&b, session_id2, session_id2_len);
11918de8d7fSPeter Avalos 		} else {
12018de8d7fSPeter Avalos 			buffer_put_string(&b, session_id2, session_id2_len);
12118de8d7fSPeter Avalos 		}
12218de8d7fSPeter Avalos 		/* reconstruct packet */
12318de8d7fSPeter Avalos 		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
12418de8d7fSPeter Avalos 		buffer_put_cstring(&b, authctxt->user);
12518de8d7fSPeter Avalos 		buffer_put_cstring(&b,
12618de8d7fSPeter Avalos 		    datafellows & SSH_BUG_PKSERVICE ?
12718de8d7fSPeter Avalos 		    "ssh-userauth" :
12818de8d7fSPeter Avalos 		    authctxt->service);
12918de8d7fSPeter Avalos 		if (datafellows & SSH_BUG_PKAUTH) {
13018de8d7fSPeter Avalos 			buffer_put_char(&b, have_sig);
13118de8d7fSPeter Avalos 		} else {
13218de8d7fSPeter Avalos 			buffer_put_cstring(&b, "publickey");
13318de8d7fSPeter Avalos 			buffer_put_char(&b, have_sig);
13418de8d7fSPeter Avalos 			buffer_put_cstring(&b, pkalg);
13518de8d7fSPeter Avalos 		}
13618de8d7fSPeter Avalos 		buffer_put_string(&b, pkblob, blen);
13718de8d7fSPeter Avalos #ifdef DEBUG_PK
13818de8d7fSPeter Avalos 		buffer_dump(&b);
13918de8d7fSPeter Avalos #endif
14018de8d7fSPeter Avalos 		/* test for correct signature */
14118de8d7fSPeter Avalos 		authenticated = 0;
14218de8d7fSPeter Avalos 		if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
14318de8d7fSPeter Avalos 		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
14418de8d7fSPeter Avalos 		    buffer_len(&b))) == 1)
14518de8d7fSPeter Avalos 			authenticated = 1;
14618de8d7fSPeter Avalos 		buffer_free(&b);
14718de8d7fSPeter Avalos 		xfree(sig);
14818de8d7fSPeter Avalos 	} else {
14918de8d7fSPeter Avalos 		debug("test whether pkalg/pkblob are acceptable");
15018de8d7fSPeter Avalos 		packet_check_eom();
15118de8d7fSPeter Avalos 
15218de8d7fSPeter Avalos 		/* XXX fake reply and always send PK_OK ? */
15318de8d7fSPeter Avalos 		/*
15418de8d7fSPeter Avalos 		 * XXX this allows testing whether a user is allowed
15518de8d7fSPeter Avalos 		 * to login: if you happen to have a valid pubkey this
15618de8d7fSPeter Avalos 		 * message is sent. the message is NEVER sent at all
15718de8d7fSPeter Avalos 		 * if a user is not allowed to login. is this an
15818de8d7fSPeter Avalos 		 * issue? -markus
15918de8d7fSPeter Avalos 		 */
16018de8d7fSPeter Avalos 		if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
16118de8d7fSPeter Avalos 			packet_start(SSH2_MSG_USERAUTH_PK_OK);
16218de8d7fSPeter Avalos 			packet_put_string(pkalg, alen);
16318de8d7fSPeter Avalos 			packet_put_string(pkblob, blen);
16418de8d7fSPeter Avalos 			packet_send();
16518de8d7fSPeter Avalos 			packet_write_wait();
16618de8d7fSPeter Avalos 			authctxt->postponed = 1;
16718de8d7fSPeter Avalos 		}
16818de8d7fSPeter Avalos 	}
16918de8d7fSPeter Avalos 	if (authenticated != 1)
17018de8d7fSPeter Avalos 		auth_clear_options();
17118de8d7fSPeter Avalos done:
17218de8d7fSPeter Avalos 	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
17318de8d7fSPeter Avalos 	if (key != NULL)
17418de8d7fSPeter Avalos 		key_free(key);
17518de8d7fSPeter Avalos 	xfree(pkalg);
17618de8d7fSPeter Avalos 	xfree(pkblob);
17718de8d7fSPeter Avalos 	return authenticated;
17818de8d7fSPeter Avalos }
17918de8d7fSPeter Avalos 
180*856ea928SPeter Avalos static int
181*856ea928SPeter Avalos match_principals_option(const char *principal_list, struct KeyCert *cert)
182*856ea928SPeter Avalos {
183*856ea928SPeter Avalos 	char *result;
184*856ea928SPeter Avalos 	u_int i;
185*856ea928SPeter Avalos 
186*856ea928SPeter Avalos 	/* XXX percent_expand() sequences for authorized_principals? */
187*856ea928SPeter Avalos 
188*856ea928SPeter Avalos 	for (i = 0; i < cert->nprincipals; i++) {
189*856ea928SPeter Avalos 		if ((result = match_list(cert->principals[i],
190*856ea928SPeter Avalos 		    principal_list, NULL)) != NULL) {
191*856ea928SPeter Avalos 			debug3("matched principal from key options \"%.100s\"",
192*856ea928SPeter Avalos 			    result);
193*856ea928SPeter Avalos 			xfree(result);
194*856ea928SPeter Avalos 			return 1;
195*856ea928SPeter Avalos 		}
196*856ea928SPeter Avalos 	}
197*856ea928SPeter Avalos 	return 0;
198*856ea928SPeter Avalos }
199*856ea928SPeter Avalos 
200*856ea928SPeter Avalos static int
201*856ea928SPeter Avalos match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
202*856ea928SPeter Avalos {
203*856ea928SPeter Avalos 	FILE *f;
204*856ea928SPeter Avalos 	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
205*856ea928SPeter Avalos 	u_long linenum = 0;
206*856ea928SPeter Avalos 	u_int i;
207*856ea928SPeter Avalos 
208*856ea928SPeter Avalos 	temporarily_use_uid(pw);
209*856ea928SPeter Avalos 	debug("trying authorized principals file %s", file);
210*856ea928SPeter Avalos 	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
211*856ea928SPeter Avalos 		restore_uid();
212*856ea928SPeter Avalos 		return 0;
213*856ea928SPeter Avalos 	}
214*856ea928SPeter Avalos 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
215*856ea928SPeter Avalos 		/* Skip leading whitespace. */
216*856ea928SPeter Avalos 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
217*856ea928SPeter Avalos 			;
218*856ea928SPeter Avalos 		/* Skip blank and comment lines. */
219*856ea928SPeter Avalos 		if ((ep = strchr(cp, '#')) != NULL)
220*856ea928SPeter Avalos 			*ep = '\0';
221*856ea928SPeter Avalos 		if (!*cp || *cp == '\n')
222*856ea928SPeter Avalos 			continue;
223*856ea928SPeter Avalos 		/* Trim trailing whitespace. */
224*856ea928SPeter Avalos 		ep = cp + strlen(cp) - 1;
225*856ea928SPeter Avalos 		while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
226*856ea928SPeter Avalos 			*ep-- = '\0';
227*856ea928SPeter Avalos 		/*
228*856ea928SPeter Avalos 		 * If the line has internal whitespace then assume it has
229*856ea928SPeter Avalos 		 * key options.
230*856ea928SPeter Avalos 		 */
231*856ea928SPeter Avalos 		line_opts = NULL;
232*856ea928SPeter Avalos 		if ((ep = strrchr(cp, ' ')) != NULL ||
233*856ea928SPeter Avalos 		    (ep = strrchr(cp, '\t')) != NULL) {
234*856ea928SPeter Avalos 			for (; *ep == ' ' || *ep == '\t'; ep++)
235*856ea928SPeter Avalos 				;;
236*856ea928SPeter Avalos 			line_opts = cp;
237*856ea928SPeter Avalos 			cp = ep;
238*856ea928SPeter Avalos 		}
239*856ea928SPeter Avalos 		for (i = 0; i < cert->nprincipals; i++) {
240*856ea928SPeter Avalos 			if (strcmp(cp, cert->principals[i]) == 0) {
241*856ea928SPeter Avalos 				debug3("matched principal from file \"%.100s\"",
242*856ea928SPeter Avalos 			    	    cert->principals[i]);
243*856ea928SPeter Avalos 				if (auth_parse_options(pw, line_opts,
244*856ea928SPeter Avalos 				    file, linenum) != 1)
245*856ea928SPeter Avalos 					continue;
246*856ea928SPeter Avalos 				fclose(f);
247*856ea928SPeter Avalos 				restore_uid();
248*856ea928SPeter Avalos 				return 1;
249*856ea928SPeter Avalos 			}
250*856ea928SPeter Avalos 		}
251*856ea928SPeter Avalos 	}
252*856ea928SPeter Avalos 	fclose(f);
253*856ea928SPeter Avalos 	restore_uid();
254*856ea928SPeter Avalos 	return 0;
255*856ea928SPeter Avalos }
256*856ea928SPeter Avalos 
25718de8d7fSPeter Avalos /* return 1 if user allows given key */
25818de8d7fSPeter Avalos static int
25918de8d7fSPeter Avalos user_key_allowed2(struct passwd *pw, Key *key, char *file)
26018de8d7fSPeter Avalos {
26118de8d7fSPeter Avalos 	char line[SSH_MAX_PUBKEY_BYTES];
262*856ea928SPeter Avalos 	const char *reason;
26318de8d7fSPeter Avalos 	int found_key = 0;
26418de8d7fSPeter Avalos 	FILE *f;
26518de8d7fSPeter Avalos 	u_long linenum = 0;
26618de8d7fSPeter Avalos 	Key *found;
26718de8d7fSPeter Avalos 	char *fp;
26818de8d7fSPeter Avalos 
26918de8d7fSPeter Avalos 	/* Temporarily use the user's uid. */
27018de8d7fSPeter Avalos 	temporarily_use_uid(pw);
27118de8d7fSPeter Avalos 
27218de8d7fSPeter Avalos 	debug("trying public key file %s", file);
27318de8d7fSPeter Avalos 	f = auth_openkeyfile(file, pw, options.strict_modes);
27418de8d7fSPeter Avalos 
27518de8d7fSPeter Avalos 	if (!f) {
27618de8d7fSPeter Avalos 		restore_uid();
27718de8d7fSPeter Avalos 		return 0;
27818de8d7fSPeter Avalos 	}
27918de8d7fSPeter Avalos 
28018de8d7fSPeter Avalos 	found_key = 0;
281*856ea928SPeter Avalos 	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
28218de8d7fSPeter Avalos 
28318de8d7fSPeter Avalos 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
28418de8d7fSPeter Avalos 		char *cp, *key_options = NULL;
28518de8d7fSPeter Avalos 
286*856ea928SPeter Avalos 		auth_clear_options();
287*856ea928SPeter Avalos 
28818de8d7fSPeter Avalos 		/* Skip leading whitespace, empty and comment lines. */
28918de8d7fSPeter Avalos 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
29018de8d7fSPeter Avalos 			;
29118de8d7fSPeter Avalos 		if (!*cp || *cp == '\n' || *cp == '#')
29218de8d7fSPeter Avalos 			continue;
29318de8d7fSPeter Avalos 
29418de8d7fSPeter Avalos 		if (key_read(found, &cp) != 1) {
29518de8d7fSPeter Avalos 			/* no key?  check if there are options for this key */
29618de8d7fSPeter Avalos 			int quoted = 0;
29718de8d7fSPeter Avalos 			debug2("user_key_allowed: check options: '%s'", cp);
29818de8d7fSPeter Avalos 			key_options = cp;
29918de8d7fSPeter Avalos 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
30018de8d7fSPeter Avalos 				if (*cp == '\\' && cp[1] == '"')
30118de8d7fSPeter Avalos 					cp++;	/* Skip both */
30218de8d7fSPeter Avalos 				else if (*cp == '"')
30318de8d7fSPeter Avalos 					quoted = !quoted;
30418de8d7fSPeter Avalos 			}
30518de8d7fSPeter Avalos 			/* Skip remaining whitespace. */
30618de8d7fSPeter Avalos 			for (; *cp == ' ' || *cp == '\t'; cp++)
30718de8d7fSPeter Avalos 				;
30818de8d7fSPeter Avalos 			if (key_read(found, &cp) != 1) {
30918de8d7fSPeter Avalos 				debug2("user_key_allowed: advance: '%s'", cp);
31018de8d7fSPeter Avalos 				/* still no key?  advance to next line*/
31118de8d7fSPeter Avalos 				continue;
31218de8d7fSPeter Avalos 			}
31318de8d7fSPeter Avalos 		}
314*856ea928SPeter Avalos 		if (key_is_cert(key)) {
315*856ea928SPeter Avalos 			if (!key_equal(found, key->cert->signature_key))
316*856ea928SPeter Avalos 				continue;
317*856ea928SPeter Avalos 			if (auth_parse_options(pw, key_options, file,
318*856ea928SPeter Avalos 			    linenum) != 1)
319*856ea928SPeter Avalos 				continue;
320*856ea928SPeter Avalos 			if (!key_is_cert_authority)
321*856ea928SPeter Avalos 				continue;
322*856ea928SPeter Avalos 			fp = key_fingerprint(found, SSH_FP_MD5,
323*856ea928SPeter Avalos 			    SSH_FP_HEX);
324*856ea928SPeter Avalos 			debug("matching CA found: file %s, line %lu, %s %s",
325*856ea928SPeter Avalos 			    file, linenum, key_type(found), fp);
326*856ea928SPeter Avalos 			/*
327*856ea928SPeter Avalos 			 * If the user has specified a list of principals as
328*856ea928SPeter Avalos 			 * a key option, then prefer that list to matching
329*856ea928SPeter Avalos 			 * their username in the certificate principals list.
330*856ea928SPeter Avalos 			 */
331*856ea928SPeter Avalos 			if (authorized_principals != NULL &&
332*856ea928SPeter Avalos 			    !match_principals_option(authorized_principals,
333*856ea928SPeter Avalos 			    key->cert)) {
334*856ea928SPeter Avalos 				reason = "Certificate does not contain an "
335*856ea928SPeter Avalos 				    "authorized principal";
336*856ea928SPeter Avalos  fail_reason:
337*856ea928SPeter Avalos 				xfree(fp);
338*856ea928SPeter Avalos 				error("%s", reason);
339*856ea928SPeter Avalos 				auth_debug_add("%s", reason);
340*856ea928SPeter Avalos 				continue;
341*856ea928SPeter Avalos 			}
342*856ea928SPeter Avalos 			if (key_cert_check_authority(key, 0, 0,
343*856ea928SPeter Avalos 			    authorized_principals == NULL ? pw->pw_name : NULL,
344*856ea928SPeter Avalos 			    &reason) != 0)
345*856ea928SPeter Avalos 				goto fail_reason;
346*856ea928SPeter Avalos 			if (auth_cert_options(key, pw) != 0) {
347*856ea928SPeter Avalos 				xfree(fp);
348*856ea928SPeter Avalos 				continue;
349*856ea928SPeter Avalos 			}
350*856ea928SPeter Avalos 			verbose("Accepted certificate ID \"%s\" "
351*856ea928SPeter Avalos 			    "signed by %s CA %s via %s", key->cert->key_id,
352*856ea928SPeter Avalos 			    key_type(found), fp, file);
353*856ea928SPeter Avalos 			xfree(fp);
354*856ea928SPeter Avalos 			found_key = 1;
355*856ea928SPeter Avalos 			break;
356*856ea928SPeter Avalos 		} else if (key_equal(found, key)) {
357*856ea928SPeter Avalos 			if (auth_parse_options(pw, key_options, file,
358*856ea928SPeter Avalos 			    linenum) != 1)
359*856ea928SPeter Avalos 				continue;
360*856ea928SPeter Avalos 			if (key_is_cert_authority)
361*856ea928SPeter Avalos 				continue;
36218de8d7fSPeter Avalos 			found_key = 1;
36318de8d7fSPeter Avalos 			debug("matching key found: file %s, line %lu",
36418de8d7fSPeter Avalos 			    file, linenum);
36518de8d7fSPeter Avalos 			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
36618de8d7fSPeter Avalos 			verbose("Found matching %s key: %s",
36718de8d7fSPeter Avalos 			    key_type(found), fp);
36818de8d7fSPeter Avalos 			xfree(fp);
36918de8d7fSPeter Avalos 			break;
37018de8d7fSPeter Avalos 		}
37118de8d7fSPeter Avalos 	}
37218de8d7fSPeter Avalos 	restore_uid();
37318de8d7fSPeter Avalos 	fclose(f);
37418de8d7fSPeter Avalos 	key_free(found);
37518de8d7fSPeter Avalos 	if (!found_key)
37618de8d7fSPeter Avalos 		debug2("key not found");
37718de8d7fSPeter Avalos 	return found_key;
37818de8d7fSPeter Avalos }
37918de8d7fSPeter Avalos 
380*856ea928SPeter Avalos /* Authenticate a certificate key against TrustedUserCAKeys */
381*856ea928SPeter Avalos static int
382*856ea928SPeter Avalos user_cert_trusted_ca(struct passwd *pw, Key *key)
383*856ea928SPeter Avalos {
384*856ea928SPeter Avalos 	char *ca_fp, *principals_file = NULL;
385*856ea928SPeter Avalos 	const char *reason;
386*856ea928SPeter Avalos 	int ret = 0;
387*856ea928SPeter Avalos 
388*856ea928SPeter Avalos 	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
389*856ea928SPeter Avalos 		return 0;
390*856ea928SPeter Avalos 
391*856ea928SPeter Avalos 	ca_fp = key_fingerprint(key->cert->signature_key,
392*856ea928SPeter Avalos 	    SSH_FP_MD5, SSH_FP_HEX);
393*856ea928SPeter Avalos 
394*856ea928SPeter Avalos 	if (key_in_file(key->cert->signature_key,
395*856ea928SPeter Avalos 	    options.trusted_user_ca_keys, 1) != 1) {
396*856ea928SPeter Avalos 		debug2("%s: CA %s %s is not listed in %s", __func__,
397*856ea928SPeter Avalos 		    key_type(key->cert->signature_key), ca_fp,
398*856ea928SPeter Avalos 		    options.trusted_user_ca_keys);
399*856ea928SPeter Avalos 		goto out;
400*856ea928SPeter Avalos 	}
401*856ea928SPeter Avalos 	/*
402*856ea928SPeter Avalos 	 * If AuthorizedPrincipals is in use, then compare the certificate
403*856ea928SPeter Avalos 	 * principals against the names in that file rather than matching
404*856ea928SPeter Avalos 	 * against the username.
405*856ea928SPeter Avalos 	 */
406*856ea928SPeter Avalos 	if ((principals_file = authorized_principals_file(pw)) != NULL) {
407*856ea928SPeter Avalos 		if (!match_principals_file(principals_file, pw, key->cert)) {
408*856ea928SPeter Avalos 			reason = "Certificate does not contain an "
409*856ea928SPeter Avalos 			    "authorized principal";
410*856ea928SPeter Avalos  fail_reason:
411*856ea928SPeter Avalos 			error("%s", reason);
412*856ea928SPeter Avalos 			auth_debug_add("%s", reason);
413*856ea928SPeter Avalos 			goto out;
414*856ea928SPeter Avalos 		}
415*856ea928SPeter Avalos 	}
416*856ea928SPeter Avalos 	if (key_cert_check_authority(key, 0, 1,
417*856ea928SPeter Avalos 	    principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
418*856ea928SPeter Avalos 		goto fail_reason;
419*856ea928SPeter Avalos 	if (auth_cert_options(key, pw) != 0)
420*856ea928SPeter Avalos 		goto out;
421*856ea928SPeter Avalos 
422*856ea928SPeter Avalos 	verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
423*856ea928SPeter Avalos 	    key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
424*856ea928SPeter Avalos 	    options.trusted_user_ca_keys);
425*856ea928SPeter Avalos 	ret = 1;
426*856ea928SPeter Avalos 
427*856ea928SPeter Avalos  out:
428*856ea928SPeter Avalos 	if (principals_file != NULL)
429*856ea928SPeter Avalos 		xfree(principals_file);
430*856ea928SPeter Avalos 	if (ca_fp != NULL)
431*856ea928SPeter Avalos 		xfree(ca_fp);
432*856ea928SPeter Avalos 	return ret;
433*856ea928SPeter Avalos }
434*856ea928SPeter Avalos 
43518de8d7fSPeter Avalos /* check whether given key is in .ssh/authorized_keys* */
43618de8d7fSPeter Avalos int
43718de8d7fSPeter Avalos user_key_allowed(struct passwd *pw, Key *key)
43818de8d7fSPeter Avalos {
43918de8d7fSPeter Avalos 	int success;
44018de8d7fSPeter Avalos 	char *file;
44118de8d7fSPeter Avalos 
442*856ea928SPeter Avalos 	if (auth_key_is_revoked(key))
443*856ea928SPeter Avalos 		return 0;
444*856ea928SPeter Avalos 	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
445*856ea928SPeter Avalos 		return 0;
446*856ea928SPeter Avalos 
447*856ea928SPeter Avalos 	success = user_cert_trusted_ca(pw, key);
448*856ea928SPeter Avalos 	if (success)
449*856ea928SPeter Avalos 		return success;
450*856ea928SPeter Avalos 
45118de8d7fSPeter Avalos 	file = authorized_keys_file(pw);
45218de8d7fSPeter Avalos 	success = user_key_allowed2(pw, key, file);
45318de8d7fSPeter Avalos 	xfree(file);
45418de8d7fSPeter Avalos 	if (success)
45518de8d7fSPeter Avalos 		return success;
45618de8d7fSPeter Avalos 
45718de8d7fSPeter Avalos 	/* try suffix "2" for backward compat, too */
45818de8d7fSPeter Avalos 	file = authorized_keys_file2(pw);
45918de8d7fSPeter Avalos 	success = user_key_allowed2(pw, key, file);
46018de8d7fSPeter Avalos 	xfree(file);
46118de8d7fSPeter Avalos 	return success;
46218de8d7fSPeter Avalos }
46318de8d7fSPeter Avalos 
46418de8d7fSPeter Avalos Authmethod method_pubkey = {
46518de8d7fSPeter Avalos 	"publickey",
46618de8d7fSPeter Avalos 	userauth_pubkey,
46718de8d7fSPeter Avalos 	&options.pubkey_authentication
46818de8d7fSPeter Avalos };
469