summaryrefslogtreecommitdiff
path: root/auth2-pubkey.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2022-05-27 05:02:46 +0000
committerDamien Miller <djm@mindrot.org>2022-05-27 16:38:03 +1000
commitc83d8c4d6f3ccceef84d46de107f6b71cda06359 (patch)
tree81872e6e3f2382f879831c09ae21e422ed41b900 /auth2-pubkey.c
parent3b0b142d2a0767d8cd838e2f3aefde8a0aaa41e1 (diff)
downloadopenssh-git-c83d8c4d6f3ccceef84d46de107f6b71cda06359.tar.gz
upstream: split the low-level file handling functions out from
auth2-pubkey.c Put them in a new auth2-pubkeyfile.c to make it easier to refer to them (e.g. in unit/fuzz tests) without having to refer to everything else pubkey auth brings in. ok dtucker@ OpenBSD-Commit-ID: 3fdca2c61ad97dc1b8d4a7346816f83dc4ce2217
Diffstat (limited to 'auth2-pubkey.c')
-rw-r--r--auth2-pubkey.c311
1 files changed, 6 insertions, 305 deletions
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 2f58a138..952af119 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.114 2022/05/27 05:01:25 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.115 2022/05/27 05:02:46 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2010 Damien Miller. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,11 +27,9 @@
#include "includes.h"
#include <sys/types.h>
-#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
-#include <fcntl.h>
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif
@@ -67,7 +66,6 @@
#include "authfile.h"
#include "match.h"
#include "ssherr.h"
-#include "kex.h"
#include "channels.h" /* XXX for session.h */
#include "session.h" /* XXX for child_set_env(); refactor? */
#include "sk-api.h"
@@ -322,120 +320,6 @@ done:
}
static int
-match_principals_option(const char *principal_list, struct sshkey_cert *cert)
-{
- char *result;
- u_int i;
-
- /* XXX percent_expand() sequences for authorized_principals? */
-
- for (i = 0; i < cert->nprincipals; i++) {
- if ((result = match_list(cert->principals[i],
- principal_list, NULL)) != NULL) {
- debug3("matched principal from key options \"%.100s\"",
- result);
- free(result);
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Process a single authorized_principals format line. Returns 0 and sets
- * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
- * log preamble for file/line information.
- */
-static int
-check_principals_line(char *cp, const struct sshkey_cert *cert,
- const char *loc, struct sshauthopt **authoptsp)
-{
- u_int i, found = 0;
- char *ep, *line_opts;
- const char *reason = NULL;
- struct sshauthopt *opts = NULL;
-
- if (authoptsp != NULL)
- *authoptsp = NULL;
-
- /* Trim trailing whitespace. */
- ep = cp + strlen(cp) - 1;
- while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
- *ep-- = '\0';
-
- /*
- * If the line has internal whitespace then assume it has
- * key options.
- */
- line_opts = NULL;
- if ((ep = strrchr(cp, ' ')) != NULL ||
- (ep = strrchr(cp, '\t')) != NULL) {
- for (; *ep == ' ' || *ep == '\t'; ep++)
- ;
- line_opts = cp;
- cp = ep;
- }
- if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
- debug("%s: bad principals options: %s", loc, reason);
- auth_debug_add("%s: bad principals options: %s", loc, reason);
- return -1;
- }
- /* Check principals in cert against those on line */
- for (i = 0; i < cert->nprincipals; i++) {
- if (strcmp(cp, cert->principals[i]) != 0)
- continue;
- debug3("%s: matched principal \"%.100s\"",
- loc, cert->principals[i]);
- found = 1;
- }
- if (found && authoptsp != NULL) {
- *authoptsp = opts;
- opts = NULL;
- }
- sshauthopt_free(opts);
- return found ? 0 : -1;
-}
-
-static int
-process_principals(FILE *f, const char *file,
- const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
-{
- char loc[256], *line = NULL, *cp, *ep;
- size_t linesize = 0;
- u_long linenum = 0, nonblank = 0;
- u_int found_principal = 0;
-
- if (authoptsp != NULL)
- *authoptsp = NULL;
-
- while (getline(&line, &linesize, f) != -1) {
- linenum++;
- /* Always consume entire input */
- if (found_principal)
- continue;
-
- /* Skip leading whitespace. */
- for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
- ;
- /* Skip blank and comment lines. */
- if ((ep = strchr(cp, '#')) != NULL)
- *ep = '\0';
- if (!*cp || *cp == '\n')
- continue;
-
- nonblank++;
- snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
- if (check_principals_line(cp, cert, loc, authoptsp) == 0)
- found_principal = 1;
- }
- debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
- free(line);
- return found_principal;
-}
-
-/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
-
-static int
match_principals_file(struct passwd *pw, char *file,
struct sshkey_cert *cert, struct sshauthopt **authoptsp)
{
@@ -451,7 +335,7 @@ match_principals_file(struct passwd *pw, char *file,
restore_uid();
return 0;
}
- success = process_principals(f, file, cert, authoptsp);
+ success = auth_process_principals(f, file, cert, authoptsp);
fclose(f);
restore_uid();
return success;
@@ -567,7 +451,7 @@ match_principals_command(struct passwd *user_pw,
uid_swapped = 1;
temporarily_use_uid(runas_pw);
- ok = process_principals(f, "(command)", cert, authoptsp);
+ ok = auth_process_principals(f, "(command)", cert, authoptsp);
fclose(f);
f = NULL;
@@ -595,189 +479,6 @@ match_principals_command(struct passwd *user_pw,
return found_principal;
}
-/*
- * Check a single line of an authorized_keys-format file. Returns 0 if key
- * matches, -1 otherwise. Will return key/cert options via *authoptsp
- * on success. "loc" is used as file/line location in log messages.
- */
-static int
-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)
-{
- int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
- struct sshkey *found = NULL;
- struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
- char *key_options = NULL, *fp = NULL;
- const char *reason = NULL;
- int ret = -1;
-
- if (authoptsp != NULL)
- *authoptsp = NULL;
-
- if ((found = sshkey_new(want_keytype)) == NULL) {
- debug3_f("keytype %d failed", want_keytype);
- goto out;
- }
-
- /* XXX djm: peek at key type in line and skip if unwanted */
-
- if (sshkey_read(found, &cp) != 0) {
- /* no key? check for options */
- debug2("%s: check options: '%s'", loc, cp);
- key_options = cp;
- if (sshkey_advance_past_options(&cp) != 0) {
- reason = "invalid key option string";
- goto fail_reason;
- }
- skip_space(&cp);
- if (sshkey_read(found, &cp) != 0) {
- /* still no key? advance to next line*/
- debug2("%s: advance: '%s'", loc, cp);
- goto out;
- }
- }
- /* Parse key options now; we need to know if this is a CA key */
- if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
- debug("%s: bad key options: %s", loc, reason);
- auth_debug_add("%s: bad key options: %s", loc, reason);
- goto out;
- }
- /* Ignore keys that don't match or incorrectly marked as CAs */
- if (sshkey_is_cert(key)) {
- /* Certificate; check signature key against CA */
- if (!sshkey_equal(found, key->cert->signature_key) ||
- !keyopts->cert_authority)
- goto out;
- } else {
- /* Plain key: check it against key found in file */
- if (!sshkey_equal(found, key) || keyopts->cert_authority)
- goto out;
- }
-
- /* We have a candidate key, perform authorisation checks */
- if ((fp = sshkey_fingerprint(found,
- options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
- fatal_f("fingerprint failed");
-
- debug("%s: matching %s found: %s %s", loc,
- sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
-
- if (auth_authorise_keyopts(pw, keyopts,
- sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {
- reason = "Refused by key options";
- goto fail_reason;
- }
- /* That's all we need for plain keys. */
- if (!sshkey_is_cert(key)) {
- verbose("Accepted key %s %s found at %s",
- sshkey_type(found), fp, loc);
- finalopts = keyopts;
- keyopts = NULL;
- goto success;
- }
-
- /*
- * Additional authorisation for certificates.
- */
-
- /* Parse and check options present in certificate */
- if ((certopts = sshauthopt_from_cert(key)) == NULL) {
- reason = "Invalid certificate options";
- goto fail_reason;
- }
- if (auth_authorise_keyopts(pw, certopts, 0,
- remote_ip, remote_host, loc) != 0) {
- reason = "Refused by certificate options";
- goto fail_reason;
- }
- if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
- goto fail_reason;
-
- /*
- * If the user has specified a list of principals as
- * a key option, then prefer that list to matching
- * their username in the certificate principals list.
- */
- if (keyopts->cert_principals != NULL &&
- !match_principals_option(keyopts->cert_principals, key->cert)) {
- reason = "Certificate does not contain an authorized principal";
- goto fail_reason;
- }
- if (sshkey_cert_check_authority_now(key, 0, 0, 0,
- keyopts->cert_principals == NULL ? pw->pw_name : NULL,
- &reason) != 0)
- goto fail_reason;
-
- verbose("Accepted certificate ID \"%s\" (serial %llu) "
- "signed by CA %s %s found at %s",
- key->cert->key_id,
- (unsigned long long)key->cert->serial,
- sshkey_type(found), fp, loc);
-
- success:
- if (finalopts == NULL)
- fatal_f("internal error: missing options");
- if (authoptsp != NULL) {
- *authoptsp = finalopts;
- finalopts = NULL;
- }
- /* success */
- ret = 0;
- goto out;
-
- fail_reason:
- error("%s", reason);
- auth_debug_add("%s", reason);
- out:
- free(fp);
- sshauthopt_free(keyopts);
- sshauthopt_free(certopts);
- sshauthopt_free(finalopts);
- sshkey_free(found);
- return ret;
-}
-
-/*
- * Checks whether key is allowed in authorized_keys-format file,
- * returns 1 if the key is allowed or 0 otherwise.
- */
-static int
-check_authkeys_file(struct passwd *pw, FILE *f, char *file,
- struct sshkey *key, const char *remote_ip,
- const char *remote_host, struct sshauthopt **authoptsp)
-{
- char *cp, *line = NULL, loc[256];
- size_t linesize = 0;
- int found_key = 0;
- u_long linenum = 0, nonblank = 0;
-
- if (authoptsp != NULL)
- *authoptsp = NULL;
-
- while (getline(&line, &linesize, f) != -1) {
- linenum++;
- /* Always consume entire file */
- if (found_key)
- continue;
-
- /* Skip leading whitespace, empty and comment lines. */
- cp = line;
- skip_space(&cp);
- if (!*cp || *cp == '\n' || *cp == '#')
- continue;
-
- nonblank++;
- snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
- if (check_authkey_line(pw, key, cp,
- remote_ip, remote_host, loc, authoptsp) == 0)
- found_key = 1;
- }
- free(line);
- debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
- return found_key;
-}
-
/* Authenticate a certificate key against TrustedUserCAKeys */
static int
user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,
@@ -902,7 +603,7 @@ user_key_allowed2(struct passwd *pw, struct sshkey *key,
debug("trying public key file %s", file);
if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
- found_key = check_authkeys_file(pw, f, file,
+ found_key = auth_check_authkeys_file(pw, f, file,
key, remote_ip, remote_host, authoptsp);
fclose(f);
}
@@ -1018,7 +719,7 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key,
uid_swapped = 1;
temporarily_use_uid(runas_pw);
- ok = check_authkeys_file(user_pw, f,
+ ok = auth_check_authkeys_file(user_pw, f,
options.authorized_keys_command, key, remote_ip,
remote_host, authoptsp);