xref: /freebsd/crypto/openssh/auth2-pubkeyfile.c (revision 4d3fc8b0570b29fb0d6ee9525f104d52176ff0d4)
1*4d3fc8b0SEd Maste /* $OpenBSD: auth2-pubkeyfile.c,v 1.4 2023/03/05 05:34:09 dtucker Exp $ */
238a52bd3SEd Maste /*
338a52bd3SEd Maste  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
438a52bd3SEd Maste  * Copyright (c) 2010 Damien Miller.  All rights reserved.
538a52bd3SEd Maste  *
638a52bd3SEd Maste  * Redistribution and use in source and binary forms, with or without
738a52bd3SEd Maste  * modification, are permitted provided that the following conditions
838a52bd3SEd Maste  * are met:
938a52bd3SEd Maste  * 1. Redistributions of source code must retain the above copyright
1038a52bd3SEd Maste  *    notice, this list of conditions and the following disclaimer.
1138a52bd3SEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
1238a52bd3SEd Maste  *    notice, this list of conditions and the following disclaimer in the
1338a52bd3SEd Maste  *    documentation and/or other materials provided with the distribution.
1438a52bd3SEd Maste  *
1538a52bd3SEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1638a52bd3SEd Maste  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1738a52bd3SEd Maste  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1838a52bd3SEd Maste  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1938a52bd3SEd Maste  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2038a52bd3SEd Maste  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2138a52bd3SEd Maste  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2238a52bd3SEd Maste  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2338a52bd3SEd Maste  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2438a52bd3SEd Maste  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2538a52bd3SEd Maste  */
2638a52bd3SEd Maste 
2738a52bd3SEd Maste #include "includes.h"
2838a52bd3SEd Maste 
2938a52bd3SEd Maste #include <sys/types.h>
3038a52bd3SEd Maste #include <sys/stat.h>
3138a52bd3SEd Maste 
3238a52bd3SEd Maste #include <stdlib.h>
3338a52bd3SEd Maste #include <errno.h>
3438a52bd3SEd Maste #include <fcntl.h>
3538a52bd3SEd Maste #include <pwd.h>
3638a52bd3SEd Maste #include <stdio.h>
3738a52bd3SEd Maste #include <stdarg.h>
3838a52bd3SEd Maste #include <string.h>
3938a52bd3SEd Maste #include <time.h>
4038a52bd3SEd Maste #include <unistd.h>
4138a52bd3SEd Maste 
4238a52bd3SEd Maste #include "ssh.h"
4338a52bd3SEd Maste #include "log.h"
4438a52bd3SEd Maste #include "misc.h"
4538a52bd3SEd Maste #include "sshkey.h"
4638a52bd3SEd Maste #include "digest.h"
4738a52bd3SEd Maste #include "hostfile.h"
4838a52bd3SEd Maste #include "auth.h"
4938a52bd3SEd Maste #include "auth-options.h"
5038a52bd3SEd Maste #include "authfile.h"
5138a52bd3SEd Maste #include "match.h"
5238a52bd3SEd Maste #include "ssherr.h"
5338a52bd3SEd Maste 
5438a52bd3SEd Maste int
auth_authorise_keyopts(struct passwd * pw,struct sshauthopt * opts,int allow_cert_authority,const char * remote_ip,const char * remote_host,const char * loc)5538a52bd3SEd Maste auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *opts,
5638a52bd3SEd Maste     int allow_cert_authority, const char *remote_ip, const char *remote_host,
5738a52bd3SEd Maste     const char *loc)
5838a52bd3SEd Maste {
5938a52bd3SEd Maste 	time_t now = time(NULL);
6038a52bd3SEd Maste 	char buf[64];
6138a52bd3SEd Maste 
6238a52bd3SEd Maste 	/*
6338a52bd3SEd Maste 	 * Check keys/principals file expiry time.
6438a52bd3SEd Maste 	 * NB. validity interval in certificate is handled elsewhere.
6538a52bd3SEd Maste 	 */
6638a52bd3SEd Maste 	if (opts->valid_before && now > 0 &&
6738a52bd3SEd Maste 	    opts->valid_before < (uint64_t)now) {
6838a52bd3SEd Maste 		format_absolute_time(opts->valid_before, buf, sizeof(buf));
6938a52bd3SEd Maste 		debug("%s: entry expired at %s", loc, buf);
7038a52bd3SEd Maste 		auth_debug_add("%s: entry expired at %s", loc, buf);
7138a52bd3SEd Maste 		return -1;
7238a52bd3SEd Maste 	}
7338a52bd3SEd Maste 	/* Consistency checks */
7438a52bd3SEd Maste 	if (opts->cert_principals != NULL && !opts->cert_authority) {
7538a52bd3SEd Maste 		debug("%s: principals on non-CA key", loc);
7638a52bd3SEd Maste 		auth_debug_add("%s: principals on non-CA key", loc);
7738a52bd3SEd Maste 		/* deny access */
7838a52bd3SEd Maste 		return -1;
7938a52bd3SEd Maste 	}
8038a52bd3SEd Maste 	/* cert-authority flag isn't valid in authorized_principals files */
8138a52bd3SEd Maste 	if (!allow_cert_authority && opts->cert_authority) {
8238a52bd3SEd Maste 		debug("%s: cert-authority flag invalid here", loc);
8338a52bd3SEd Maste 		auth_debug_add("%s: cert-authority flag invalid here", loc);
8438a52bd3SEd Maste 		/* deny access */
8538a52bd3SEd Maste 		return -1;
8638a52bd3SEd Maste 	}
8738a52bd3SEd Maste 
8838a52bd3SEd Maste 	/* Perform from= checks */
8938a52bd3SEd Maste 	if (opts->required_from_host_keys != NULL) {
9038a52bd3SEd Maste 		switch (match_host_and_ip(remote_host, remote_ip,
9138a52bd3SEd Maste 		    opts->required_from_host_keys )) {
9238a52bd3SEd Maste 		case 1:
9338a52bd3SEd Maste 			/* Host name matches. */
9438a52bd3SEd Maste 			break;
9538a52bd3SEd Maste 		case -1:
9638a52bd3SEd Maste 		default:
9738a52bd3SEd Maste 			debug("%s: invalid from criteria", loc);
9838a52bd3SEd Maste 			auth_debug_add("%s: invalid from criteria", loc);
9938a52bd3SEd Maste 			/* FALLTHROUGH */
10038a52bd3SEd Maste 		case 0:
10138a52bd3SEd Maste 			logit("%s: Authentication tried for %.100s with "
10238a52bd3SEd Maste 			    "correct key but not from a permitted "
10338a52bd3SEd Maste 			    "host (host=%.200s, ip=%.200s, required=%.200s).",
10438a52bd3SEd Maste 			    loc, pw->pw_name, remote_host, remote_ip,
10538a52bd3SEd Maste 			    opts->required_from_host_keys);
10638a52bd3SEd Maste 			auth_debug_add("%s: Your host '%.200s' is not "
10738a52bd3SEd Maste 			    "permitted to use this key for login.",
10838a52bd3SEd Maste 			    loc, remote_host);
10938a52bd3SEd Maste 			/* deny access */
11038a52bd3SEd Maste 			return -1;
11138a52bd3SEd Maste 		}
11238a52bd3SEd Maste 	}
11338a52bd3SEd Maste 	/* Check source-address restriction from certificate */
11438a52bd3SEd Maste 	if (opts->required_from_host_cert != NULL) {
11538a52bd3SEd Maste 		switch (addr_match_cidr_list(remote_ip,
11638a52bd3SEd Maste 		    opts->required_from_host_cert)) {
11738a52bd3SEd Maste 		case 1:
11838a52bd3SEd Maste 			/* accepted */
11938a52bd3SEd Maste 			break;
12038a52bd3SEd Maste 		case -1:
12138a52bd3SEd Maste 		default:
12238a52bd3SEd Maste 			/* invalid */
12338a52bd3SEd Maste 			error("%s: Certificate source-address invalid", loc);
12438a52bd3SEd Maste 			/* FALLTHROUGH */
12538a52bd3SEd Maste 		case 0:
12638a52bd3SEd Maste 			logit("%s: Authentication tried for %.100s with valid "
12738a52bd3SEd Maste 			    "certificate but not from a permitted source "
12838a52bd3SEd Maste 			    "address (%.200s).", loc, pw->pw_name, remote_ip);
12938a52bd3SEd Maste 			auth_debug_add("%s: Your address '%.200s' is not "
13038a52bd3SEd Maste 			    "permitted to use this certificate for login.",
13138a52bd3SEd Maste 			    loc, remote_ip);
13238a52bd3SEd Maste 			return -1;
13338a52bd3SEd Maste 		}
13438a52bd3SEd Maste 	}
13538a52bd3SEd Maste 	/*
13638a52bd3SEd Maste 	 *
13738a52bd3SEd Maste 	 * XXX this is spammy. We should report remotely only for keys
13838a52bd3SEd Maste 	 *     that are successful in actual auth attempts, and not PK_OK
13938a52bd3SEd Maste 	 *     tests.
14038a52bd3SEd Maste 	 */
14138a52bd3SEd Maste 	auth_log_authopts(loc, opts, 1);
14238a52bd3SEd Maste 
14338a52bd3SEd Maste 	return 0;
14438a52bd3SEd Maste }
14538a52bd3SEd Maste 
14638a52bd3SEd Maste static int
match_principals_option(const char * principal_list,struct sshkey_cert * cert)14738a52bd3SEd Maste match_principals_option(const char *principal_list, struct sshkey_cert *cert)
14838a52bd3SEd Maste {
14938a52bd3SEd Maste 	char *result;
15038a52bd3SEd Maste 	u_int i;
15138a52bd3SEd Maste 
15238a52bd3SEd Maste 	/* XXX percent_expand() sequences for authorized_principals? */
15338a52bd3SEd Maste 
15438a52bd3SEd Maste 	for (i = 0; i < cert->nprincipals; i++) {
15538a52bd3SEd Maste 		if ((result = match_list(cert->principals[i],
15638a52bd3SEd Maste 		    principal_list, NULL)) != NULL) {
15738a52bd3SEd Maste 			debug3("matched principal from key options \"%.100s\"",
15838a52bd3SEd Maste 			    result);
15938a52bd3SEd Maste 			free(result);
16038a52bd3SEd Maste 			return 1;
16138a52bd3SEd Maste 		}
16238a52bd3SEd Maste 	}
16338a52bd3SEd Maste 	return 0;
16438a52bd3SEd Maste }
16538a52bd3SEd Maste 
16638a52bd3SEd Maste /*
16738a52bd3SEd Maste  * Process a single authorized_principals format line. Returns 0 and sets
16838a52bd3SEd Maste  * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
16938a52bd3SEd Maste  * log preamble for file/line information.
17038a52bd3SEd Maste  */
17138a52bd3SEd Maste int
auth_check_principals_line(char * cp,const struct sshkey_cert * cert,const char * loc,struct sshauthopt ** authoptsp)17238a52bd3SEd Maste auth_check_principals_line(char *cp, const struct sshkey_cert *cert,
17338a52bd3SEd Maste     const char *loc, struct sshauthopt **authoptsp)
17438a52bd3SEd Maste {
17538a52bd3SEd Maste 	u_int i, found = 0;
17638a52bd3SEd Maste 	char *ep, *line_opts;
17738a52bd3SEd Maste 	const char *reason = NULL;
17838a52bd3SEd Maste 	struct sshauthopt *opts = NULL;
17938a52bd3SEd Maste 
18038a52bd3SEd Maste 	if (authoptsp != NULL)
18138a52bd3SEd Maste 		*authoptsp = NULL;
18238a52bd3SEd Maste 
18338a52bd3SEd Maste 	/* Trim trailing whitespace. */
18438a52bd3SEd Maste 	ep = cp + strlen(cp) - 1;
18538a52bd3SEd Maste 	while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
18638a52bd3SEd Maste 		*ep-- = '\0';
18738a52bd3SEd Maste 
18838a52bd3SEd Maste 	/*
18938a52bd3SEd Maste 	 * If the line has internal whitespace then assume it has
19038a52bd3SEd Maste 	 * key options.
19138a52bd3SEd Maste 	 */
19238a52bd3SEd Maste 	line_opts = NULL;
19338a52bd3SEd Maste 	if ((ep = strrchr(cp, ' ')) != NULL ||
19438a52bd3SEd Maste 	    (ep = strrchr(cp, '\t')) != NULL) {
19538a52bd3SEd Maste 		for (; *ep == ' ' || *ep == '\t'; ep++)
19638a52bd3SEd Maste 			;
19738a52bd3SEd Maste 		line_opts = cp;
19838a52bd3SEd Maste 		cp = ep;
19938a52bd3SEd Maste 	}
20038a52bd3SEd Maste 	if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
20138a52bd3SEd Maste 		debug("%s: bad principals options: %s", loc, reason);
20238a52bd3SEd Maste 		auth_debug_add("%s: bad principals options: %s", loc, reason);
20338a52bd3SEd Maste 		return -1;
20438a52bd3SEd Maste 	}
20538a52bd3SEd Maste 	/* Check principals in cert against those on line */
20638a52bd3SEd Maste 	for (i = 0; i < cert->nprincipals; i++) {
20738a52bd3SEd Maste 		if (strcmp(cp, cert->principals[i]) != 0)
20838a52bd3SEd Maste 			continue;
20938a52bd3SEd Maste 		debug3("%s: matched principal \"%.100s\"",
21038a52bd3SEd Maste 		    loc, cert->principals[i]);
21138a52bd3SEd Maste 		found = 1;
21238a52bd3SEd Maste 	}
21338a52bd3SEd Maste 	if (found && authoptsp != NULL) {
21438a52bd3SEd Maste 		*authoptsp = opts;
21538a52bd3SEd Maste 		opts = NULL;
21638a52bd3SEd Maste 	}
21738a52bd3SEd Maste 	sshauthopt_free(opts);
21838a52bd3SEd Maste 	return found ? 0 : -1;
21938a52bd3SEd Maste }
22038a52bd3SEd Maste 
22138a52bd3SEd Maste int
auth_process_principals(FILE * f,const char * file,const struct sshkey_cert * cert,struct sshauthopt ** authoptsp)22238a52bd3SEd Maste auth_process_principals(FILE *f, const char *file,
22338a52bd3SEd Maste     const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
22438a52bd3SEd Maste {
22538a52bd3SEd Maste 	char loc[256], *line = NULL, *cp, *ep;
22638a52bd3SEd Maste 	size_t linesize = 0;
22738a52bd3SEd Maste 	u_long linenum = 0, nonblank = 0;
22838a52bd3SEd Maste 	u_int found_principal = 0;
22938a52bd3SEd Maste 
23038a52bd3SEd Maste 	if (authoptsp != NULL)
23138a52bd3SEd Maste 		*authoptsp = NULL;
23238a52bd3SEd Maste 
23338a52bd3SEd Maste 	while (getline(&line, &linesize, f) != -1) {
23438a52bd3SEd Maste 		linenum++;
23538a52bd3SEd Maste 		/* Always consume entire input */
23638a52bd3SEd Maste 		if (found_principal)
23738a52bd3SEd Maste 			continue;
23838a52bd3SEd Maste 
23938a52bd3SEd Maste 		/* Skip leading whitespace. */
24038a52bd3SEd Maste 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
24138a52bd3SEd Maste 			;
24238a52bd3SEd Maste 		/* Skip blank and comment lines. */
24338a52bd3SEd Maste 		if ((ep = strchr(cp, '#')) != NULL)
24438a52bd3SEd Maste 			*ep = '\0';
24538a52bd3SEd Maste 		if (!*cp || *cp == '\n')
24638a52bd3SEd Maste 			continue;
24738a52bd3SEd Maste 
24838a52bd3SEd Maste 		nonblank++;
24938a52bd3SEd Maste 		snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
25038a52bd3SEd Maste 		if (auth_check_principals_line(cp, cert, loc, authoptsp) == 0)
25138a52bd3SEd Maste 			found_principal = 1;
25238a52bd3SEd Maste 	}
25338a52bd3SEd Maste 	debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
25438a52bd3SEd Maste 	free(line);
25538a52bd3SEd Maste 	return found_principal;
25638a52bd3SEd Maste }
25738a52bd3SEd Maste 
25838a52bd3SEd Maste /*
25938a52bd3SEd Maste  * Check a single line of an authorized_keys-format file. Returns 0 if key
26038a52bd3SEd Maste  * matches, -1 otherwise. Will return key/cert options via *authoptsp
26138a52bd3SEd Maste  * on success. "loc" is used as file/line location in log messages.
26238a52bd3SEd Maste  */
26338a52bd3SEd Maste int
auth_check_authkey_line(struct passwd * pw,struct sshkey * key,char * cp,const char * remote_ip,const char * remote_host,const char * loc,struct sshauthopt ** authoptsp)26438a52bd3SEd Maste auth_check_authkey_line(struct passwd *pw, struct sshkey *key,
26538a52bd3SEd Maste     char *cp, const char *remote_ip, const char *remote_host, const char *loc,
26638a52bd3SEd Maste     struct sshauthopt **authoptsp)
26738a52bd3SEd Maste {
26838a52bd3SEd Maste 	int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
26938a52bd3SEd Maste 	struct sshkey *found = NULL;
27038a52bd3SEd Maste 	struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
27138a52bd3SEd Maste 	char *key_options = NULL, *fp = NULL;
27238a52bd3SEd Maste 	const char *reason = NULL;
27338a52bd3SEd Maste 	int ret = -1;
27438a52bd3SEd Maste 
27538a52bd3SEd Maste 	if (authoptsp != NULL)
27638a52bd3SEd Maste 		*authoptsp = NULL;
27738a52bd3SEd Maste 
27838a52bd3SEd Maste 	if ((found = sshkey_new(want_keytype)) == NULL) {
27938a52bd3SEd Maste 		debug3_f("keytype %d failed", want_keytype);
28038a52bd3SEd Maste 		goto out;
28138a52bd3SEd Maste 	}
28238a52bd3SEd Maste 
28338a52bd3SEd Maste 	/* XXX djm: peek at key type in line and skip if unwanted */
28438a52bd3SEd Maste 
28538a52bd3SEd Maste 	if (sshkey_read(found, &cp) != 0) {
28638a52bd3SEd Maste 		/* no key?  check for options */
28738a52bd3SEd Maste 		debug2("%s: check options: '%s'", loc, cp);
28838a52bd3SEd Maste 		key_options = cp;
28938a52bd3SEd Maste 		if (sshkey_advance_past_options(&cp) != 0) {
29038a52bd3SEd Maste 			reason = "invalid key option string";
29138a52bd3SEd Maste 			goto fail_reason;
29238a52bd3SEd Maste 		}
29338a52bd3SEd Maste 		skip_space(&cp);
29438a52bd3SEd Maste 		if (sshkey_read(found, &cp) != 0) {
29538a52bd3SEd Maste 			/* still no key?  advance to next line*/
29638a52bd3SEd Maste 			debug2("%s: advance: '%s'", loc, cp);
29738a52bd3SEd Maste 			goto out;
29838a52bd3SEd Maste 		}
29938a52bd3SEd Maste 	}
30038a52bd3SEd Maste 	/* Parse key options now; we need to know if this is a CA key */
30138a52bd3SEd Maste 	if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
30238a52bd3SEd Maste 		debug("%s: bad key options: %s", loc, reason);
30338a52bd3SEd Maste 		auth_debug_add("%s: bad key options: %s", loc, reason);
30438a52bd3SEd Maste 		goto out;
30538a52bd3SEd Maste 	}
30638a52bd3SEd Maste 	/* Ignore keys that don't match or incorrectly marked as CAs */
30738a52bd3SEd Maste 	if (sshkey_is_cert(key)) {
30838a52bd3SEd Maste 		/* Certificate; check signature key against CA */
30938a52bd3SEd Maste 		if (!sshkey_equal(found, key->cert->signature_key) ||
31038a52bd3SEd Maste 		    !keyopts->cert_authority)
31138a52bd3SEd Maste 			goto out;
31238a52bd3SEd Maste 	} else {
31338a52bd3SEd Maste 		/* Plain key: check it against key found in file */
31438a52bd3SEd Maste 		if (!sshkey_equal(found, key) || keyopts->cert_authority)
31538a52bd3SEd Maste 			goto out;
31638a52bd3SEd Maste 	}
31738a52bd3SEd Maste 
31838a52bd3SEd Maste 	/* We have a candidate key, perform authorisation checks */
31938a52bd3SEd Maste 	if ((fp = sshkey_fingerprint(found,
32038a52bd3SEd Maste 	    SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL)
32138a52bd3SEd Maste 		fatal_f("fingerprint failed");
32238a52bd3SEd Maste 
32338a52bd3SEd Maste 	debug("%s: matching %s found: %s %s", loc,
32438a52bd3SEd Maste 	    sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
32538a52bd3SEd Maste 
32638a52bd3SEd Maste 	if (auth_authorise_keyopts(pw, keyopts,
32738a52bd3SEd Maste 	    sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {
32838a52bd3SEd Maste 		reason = "Refused by key options";
32938a52bd3SEd Maste 		goto fail_reason;
33038a52bd3SEd Maste 	}
33138a52bd3SEd Maste 	/* That's all we need for plain keys. */
33238a52bd3SEd Maste 	if (!sshkey_is_cert(key)) {
33338a52bd3SEd Maste 		verbose("Accepted key %s %s found at %s",
33438a52bd3SEd Maste 		    sshkey_type(found), fp, loc);
33538a52bd3SEd Maste 		finalopts = keyopts;
33638a52bd3SEd Maste 		keyopts = NULL;
33738a52bd3SEd Maste 		goto success;
33838a52bd3SEd Maste 	}
33938a52bd3SEd Maste 
34038a52bd3SEd Maste 	/*
34138a52bd3SEd Maste 	 * Additional authorisation for certificates.
34238a52bd3SEd Maste 	 */
34338a52bd3SEd Maste 
34438a52bd3SEd Maste 	/* Parse and check options present in certificate */
34538a52bd3SEd Maste 	if ((certopts = sshauthopt_from_cert(key)) == NULL) {
34638a52bd3SEd Maste 		reason = "Invalid certificate options";
34738a52bd3SEd Maste 		goto fail_reason;
34838a52bd3SEd Maste 	}
34938a52bd3SEd Maste 	if (auth_authorise_keyopts(pw, certopts, 0,
35038a52bd3SEd Maste 	    remote_ip, remote_host, loc) != 0) {
35138a52bd3SEd Maste 		reason = "Refused by certificate options";
35238a52bd3SEd Maste 		goto fail_reason;
35338a52bd3SEd Maste 	}
35438a52bd3SEd Maste 	if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
35538a52bd3SEd Maste 		goto fail_reason;
35638a52bd3SEd Maste 
35738a52bd3SEd Maste 	/*
35838a52bd3SEd Maste 	 * If the user has specified a list of principals as
35938a52bd3SEd Maste 	 * a key option, then prefer that list to matching
36038a52bd3SEd Maste 	 * their username in the certificate principals list.
36138a52bd3SEd Maste 	 */
36238a52bd3SEd Maste 	if (keyopts->cert_principals != NULL &&
36338a52bd3SEd Maste 	    !match_principals_option(keyopts->cert_principals, key->cert)) {
36438a52bd3SEd Maste 		reason = "Certificate does not contain an authorized principal";
36538a52bd3SEd Maste 		goto fail_reason;
36638a52bd3SEd Maste 	}
36738a52bd3SEd Maste 	if (sshkey_cert_check_authority_now(key, 0, 0, 0,
36838a52bd3SEd Maste 	    keyopts->cert_principals == NULL ? pw->pw_name : NULL,
36938a52bd3SEd Maste 	    &reason) != 0)
37038a52bd3SEd Maste 		goto fail_reason;
37138a52bd3SEd Maste 
37238a52bd3SEd Maste 	verbose("Accepted certificate ID \"%s\" (serial %llu) "
37338a52bd3SEd Maste 	    "signed by CA %s %s found at %s",
37438a52bd3SEd Maste 	    key->cert->key_id,
37538a52bd3SEd Maste 	    (unsigned long long)key->cert->serial,
37638a52bd3SEd Maste 	    sshkey_type(found), fp, loc);
37738a52bd3SEd Maste 
37838a52bd3SEd Maste  success:
37938a52bd3SEd Maste 	if (finalopts == NULL)
38038a52bd3SEd Maste 		fatal_f("internal error: missing options");
38138a52bd3SEd Maste 	if (authoptsp != NULL) {
38238a52bd3SEd Maste 		*authoptsp = finalopts;
38338a52bd3SEd Maste 		finalopts = NULL;
38438a52bd3SEd Maste 	}
38538a52bd3SEd Maste 	/* success */
38638a52bd3SEd Maste 	ret = 0;
38738a52bd3SEd Maste 	goto out;
38838a52bd3SEd Maste 
38938a52bd3SEd Maste  fail_reason:
39038a52bd3SEd Maste 	error("%s", reason);
39138a52bd3SEd Maste 	auth_debug_add("%s", reason);
39238a52bd3SEd Maste  out:
39338a52bd3SEd Maste 	free(fp);
39438a52bd3SEd Maste 	sshauthopt_free(keyopts);
39538a52bd3SEd Maste 	sshauthopt_free(certopts);
39638a52bd3SEd Maste 	sshauthopt_free(finalopts);
39738a52bd3SEd Maste 	sshkey_free(found);
39838a52bd3SEd Maste 	return ret;
39938a52bd3SEd Maste }
40038a52bd3SEd Maste 
40138a52bd3SEd Maste /*
40238a52bd3SEd Maste  * Checks whether key is allowed in authorized_keys-format file,
40338a52bd3SEd Maste  * returns 1 if the key is allowed or 0 otherwise.
40438a52bd3SEd Maste  */
40538a52bd3SEd Maste int
auth_check_authkeys_file(struct passwd * pw,FILE * f,char * file,struct sshkey * key,const char * remote_ip,const char * remote_host,struct sshauthopt ** authoptsp)40638a52bd3SEd Maste auth_check_authkeys_file(struct passwd *pw, FILE *f, char *file,
40738a52bd3SEd Maste     struct sshkey *key, const char *remote_ip,
40838a52bd3SEd Maste     const char *remote_host, struct sshauthopt **authoptsp)
40938a52bd3SEd Maste {
41038a52bd3SEd Maste 	char *cp, *line = NULL, loc[256];
41138a52bd3SEd Maste 	size_t linesize = 0;
41238a52bd3SEd Maste 	int found_key = 0;
41338a52bd3SEd Maste 	u_long linenum = 0, nonblank = 0;
41438a52bd3SEd Maste 
41538a52bd3SEd Maste 	if (authoptsp != NULL)
41638a52bd3SEd Maste 		*authoptsp = NULL;
41738a52bd3SEd Maste 
41838a52bd3SEd Maste 	while (getline(&line, &linesize, f) != -1) {
41938a52bd3SEd Maste 		linenum++;
42038a52bd3SEd Maste 		/* Always consume entire file */
42138a52bd3SEd Maste 		if (found_key)
42238a52bd3SEd Maste 			continue;
42338a52bd3SEd Maste 
42438a52bd3SEd Maste 		/* Skip leading whitespace, empty and comment lines. */
42538a52bd3SEd Maste 		cp = line;
42638a52bd3SEd Maste 		skip_space(&cp);
42738a52bd3SEd Maste 		if (!*cp || *cp == '\n' || *cp == '#')
42838a52bd3SEd Maste 			continue;
42938a52bd3SEd Maste 
43038a52bd3SEd Maste 		nonblank++;
43138a52bd3SEd Maste 		snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
43238a52bd3SEd Maste 		if (auth_check_authkey_line(pw, key, cp,
43338a52bd3SEd Maste 		    remote_ip, remote_host, loc, authoptsp) == 0)
43438a52bd3SEd Maste 			found_key = 1;
43538a52bd3SEd Maste 	}
43638a52bd3SEd Maste 	free(line);
43738a52bd3SEd Maste 	debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
43838a52bd3SEd Maste 	return found_key;
43938a52bd3SEd Maste }
44038a52bd3SEd Maste 
44138a52bd3SEd Maste static FILE *
auth_openfile(const char * file,struct passwd * pw,int strict_modes,int log_missing,char * file_type)44238a52bd3SEd Maste auth_openfile(const char *file, struct passwd *pw, int strict_modes,
44338a52bd3SEd Maste     int log_missing, char *file_type)
44438a52bd3SEd Maste {
44538a52bd3SEd Maste 	char line[1024];
44638a52bd3SEd Maste 	struct stat st;
44738a52bd3SEd Maste 	int fd;
44838a52bd3SEd Maste 	FILE *f;
44938a52bd3SEd Maste 
45038a52bd3SEd Maste 	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
45138a52bd3SEd Maste 		if (errno != ENOENT) {
45238a52bd3SEd Maste 			logit("Could not open user '%s' %s '%s': %s",
45338a52bd3SEd Maste 			    pw->pw_name, file_type, file, strerror(errno));
45438a52bd3SEd Maste 		} else if (log_missing) {
45538a52bd3SEd Maste 			debug("Could not open user '%s' %s '%s': %s",
45638a52bd3SEd Maste 			    pw->pw_name, file_type, file, strerror(errno));
45738a52bd3SEd Maste 		}
45838a52bd3SEd Maste 		return NULL;
45938a52bd3SEd Maste 	}
46038a52bd3SEd Maste 
46138a52bd3SEd Maste 	if (fstat(fd, &st) == -1) {
46238a52bd3SEd Maste 		close(fd);
46338a52bd3SEd Maste 		return NULL;
46438a52bd3SEd Maste 	}
46538a52bd3SEd Maste 	if (!S_ISREG(st.st_mode)) {
46638a52bd3SEd Maste 		logit("User '%s' %s '%s' is not a regular file",
46738a52bd3SEd Maste 		    pw->pw_name, file_type, file);
46838a52bd3SEd Maste 		close(fd);
46938a52bd3SEd Maste 		return NULL;
47038a52bd3SEd Maste 	}
47138a52bd3SEd Maste 	unset_nonblock(fd);
47238a52bd3SEd Maste 	if ((f = fdopen(fd, "r")) == NULL) {
47338a52bd3SEd Maste 		close(fd);
47438a52bd3SEd Maste 		return NULL;
47538a52bd3SEd Maste 	}
47638a52bd3SEd Maste 	if (strict_modes &&
47738a52bd3SEd Maste 	    safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) {
47838a52bd3SEd Maste 		fclose(f);
47938a52bd3SEd Maste 		logit("Authentication refused: %s", line);
48038a52bd3SEd Maste 		auth_debug_add("Ignored %s: %s", file_type, line);
48138a52bd3SEd Maste 		return NULL;
48238a52bd3SEd Maste 	}
48338a52bd3SEd Maste 
48438a52bd3SEd Maste 	return f;
48538a52bd3SEd Maste }
48638a52bd3SEd Maste 
48738a52bd3SEd Maste 
48838a52bd3SEd Maste FILE *
auth_openkeyfile(const char * file,struct passwd * pw,int strict_modes)48938a52bd3SEd Maste auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
49038a52bd3SEd Maste {
49138a52bd3SEd Maste 	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
49238a52bd3SEd Maste }
49338a52bd3SEd Maste 
49438a52bd3SEd Maste FILE *
auth_openprincipals(const char * file,struct passwd * pw,int strict_modes)49538a52bd3SEd Maste auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
49638a52bd3SEd Maste {
49738a52bd3SEd Maste 	return auth_openfile(file, pw, strict_modes, 0,
49838a52bd3SEd Maste 	    "authorized principals");
49938a52bd3SEd Maste }
50038a52bd3SEd Maste 
501