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