[openssh-commits] [openssh] 02/02: upstream: split the low-level file handling functions out from
git+noreply at mindrot.org
git+noreply at mindrot.org
Fri May 27 16:38:25 AEST 2022
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit c83d8c4d6f3ccceef84d46de107f6b71cda06359
Author: djm at openbsd.org <djm at openbsd.org>
Date: Fri May 27 05:02:46 2022 +0000
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
---
Makefile.in | 2 +-
auth.c | 94 +-----------
auth.h | 18 ++-
auth2-pubkey.c | 311 +------------------------------------
auth2-pubkeyfile.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 465 insertions(+), 402 deletions(-)
diff --git a/Makefile.in b/Makefile.in
index 7250d3f3..3c285682 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -123,7 +123,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
auth.o auth2.o auth-options.o session.o \
auth2-chall.o groupaccess.o \
auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
- auth2-none.o auth2-passwd.o auth2-pubkey.o \
+ auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-pubkeyfile.o \
monitor.o monitor_wrap.o auth-krb5.o \
auth2-gss.o gss-serv.o gss-serv-krb5.o \
loginrec.o auth-pam.o auth-shadow.o auth-sia.o \
diff --git a/auth.c b/auth.c
index 57ade8db..9ad9034a 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.156 2022/05/27 05:01:25 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.157 2022/05/27 05:02:46 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -912,95 +912,3 @@ auth_restrict_session(struct ssh *ssh)
fatal_f("failed to restrict session");
sshauthopt_free(restricted);
}
-
-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)
-{
- time_t now = time(NULL);
- char buf[64];
-
- /*
- * Check keys/principals file expiry time.
- * NB. validity interval in certificate is handled elsewhere.
- */
- if (opts->valid_before && now > 0 &&
- opts->valid_before < (uint64_t)now) {
- format_absolute_time(opts->valid_before, buf, sizeof(buf));
- debug("%s: entry expired at %s", loc, buf);
- auth_debug_add("%s: entry expired at %s", loc, buf);
- return -1;
- }
- /* Consistency checks */
- if (opts->cert_principals != NULL && !opts->cert_authority) {
- debug("%s: principals on non-CA key", loc);
- auth_debug_add("%s: principals on non-CA key", loc);
- /* deny access */
- return -1;
- }
- /* cert-authority flag isn't valid in authorized_principals files */
- if (!allow_cert_authority && opts->cert_authority) {
- debug("%s: cert-authority flag invalid here", loc);
- auth_debug_add("%s: cert-authority flag invalid here", loc);
- /* deny access */
- return -1;
- }
-
- /* Perform from= checks */
- if (opts->required_from_host_keys != NULL) {
- switch (match_host_and_ip(remote_host, remote_ip,
- opts->required_from_host_keys )) {
- case 1:
- /* Host name matches. */
- break;
- case -1:
- default:
- debug("%s: invalid from criteria", loc);
- auth_debug_add("%s: invalid from criteria", loc);
- /* FALLTHROUGH */
- case 0:
- logit("%s: Authentication tried for %.100s with "
- "correct key but not from a permitted "
- "host (host=%.200s, ip=%.200s, required=%.200s).",
- loc, pw->pw_name, remote_host, remote_ip,
- opts->required_from_host_keys);
- auth_debug_add("%s: Your host '%.200s' is not "
- "permitted to use this key for login.",
- loc, remote_host);
- /* deny access */
- return -1;
- }
- }
- /* Check source-address restriction from certificate */
- if (opts->required_from_host_cert != NULL) {
- switch (addr_match_cidr_list(remote_ip,
- opts->required_from_host_cert)) {
- case 1:
- /* accepted */
- break;
- case -1:
- default:
- /* invalid */
- error("%s: Certificate source-address invalid", loc);
- /* FALLTHROUGH */
- case 0:
- logit("%s: Authentication tried for %.100s with valid "
- "certificate but not from a permitted source "
- "address (%.200s).", loc, pw->pw_name, remote_ip);
- auth_debug_add("%s: Your address '%.200s' is not "
- "permitted to use this certificate for login.",
- loc, remote_ip);
- return -1;
- }
- }
- /*
- *
- * XXX this is spammy. We should report remotely only for keys
- * that are successful in actual auth attempts, and not PK_OK
- * tests.
- */
- auth_log_authopts(loc, opts, 1);
-
- return 0;
-}
diff --git a/auth.h b/auth.h
index a52ba7c2..b8eec4a6 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.103 2022/05/27 05:01:25 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.104 2022/05/27 05:02:46 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -29,6 +29,7 @@
#define AUTH_H
#include <signal.h>
+#include <stdio.h>
#ifdef HAVE_LOGIN_CAP
#include <login_cap.h>
@@ -44,6 +45,7 @@ struct passwd;
struct ssh;
struct sshbuf;
struct sshkey;
+struct sshkey_cert;
struct sshauthopt;
typedef struct Authctxt Authctxt;
@@ -214,8 +216,6 @@ int sshd_hostkey_sign(struct ssh *, struct sshkey *, struct sshkey *,
const struct sshauthopt *auth_options(struct ssh *);
int auth_activate_options(struct ssh *, struct sshauthopt *);
void auth_restrict_session(struct ssh *);
-int auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *, int,
- const char *, const char *, const char *);
void auth_log_authopts(const char *, const struct sshauthopt *, int);
/* debug messages during authentication */
@@ -226,6 +226,18 @@ void auth_debug_reset(void);
struct passwd *fakepw(void);
+/* auth2-pubkeyfile.c */
+int auth_authorise_keyopts(struct passwd *, struct sshauthopt *, int,
+ const char *, const char *, const char *);
+int auth_check_principals_line(char *, const struct sshkey_cert *,
+ const char *, struct sshauthopt **);
+int auth_process_principals(FILE *, const char *,
+ const struct sshkey_cert *, struct sshauthopt **);
+int auth_check_authkey_line(struct passwd *, struct sshkey *,
+ char *, const char *, const char *, const char *, struct sshauthopt **);
+int auth_check_authkeys_file(struct passwd *, FILE *, char *,
+ struct sshkey *, const char *, const char *, struct sshauthopt **);
+
int sys_auth_passwd(struct ssh *, const char *);
#if defined(KRB5) && !defined(HEIMDAL)
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"
@@ -321,120 +319,6 @@ done:
return authenticated;
}
-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);
diff --git a/auth2-pubkeyfile.c b/auth2-pubkeyfile.c
new file mode 100644
index 00000000..a304d095
--- /dev/null
+++ b/auth2-pubkeyfile.c
@@ -0,0 +1,442 @@
+/* $OpenBSD: auth2-pubkeyfile.c,v 1.1 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ssh.h"
+#include "log.h"
+#include "misc.h"
+#include "compat.h"
+#include "sshkey.h"
+#include "digest.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "auth-options.h"
+#include "authfile.h"
+#include "match.h"
+#include "ssherr.h"
+
+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)
+{
+ time_t now = time(NULL);
+ char buf[64];
+
+ /*
+ * Check keys/principals file expiry time.
+ * NB. validity interval in certificate is handled elsewhere.
+ */
+ if (opts->valid_before && now > 0 &&
+ opts->valid_before < (uint64_t)now) {
+ format_absolute_time(opts->valid_before, buf, sizeof(buf));
+ debug("%s: entry expired at %s", loc, buf);
+ auth_debug_add("%s: entry expired at %s", loc, buf);
+ return -1;
+ }
+ /* Consistency checks */
+ if (opts->cert_principals != NULL && !opts->cert_authority) {
+ debug("%s: principals on non-CA key", loc);
+ auth_debug_add("%s: principals on non-CA key", loc);
+ /* deny access */
+ return -1;
+ }
+ /* cert-authority flag isn't valid in authorized_principals files */
+ if (!allow_cert_authority && opts->cert_authority) {
+ debug("%s: cert-authority flag invalid here", loc);
+ auth_debug_add("%s: cert-authority flag invalid here", loc);
+ /* deny access */
+ return -1;
+ }
+
+ /* Perform from= checks */
+ if (opts->required_from_host_keys != NULL) {
+ switch (match_host_and_ip(remote_host, remote_ip,
+ opts->required_from_host_keys )) {
+ case 1:
+ /* Host name matches. */
+ break;
+ case -1:
+ default:
+ debug("%s: invalid from criteria", loc);
+ auth_debug_add("%s: invalid from criteria", loc);
+ /* FALLTHROUGH */
+ case 0:
+ logit("%s: Authentication tried for %.100s with "
+ "correct key but not from a permitted "
+ "host (host=%.200s, ip=%.200s, required=%.200s).",
+ loc, pw->pw_name, remote_host, remote_ip,
+ opts->required_from_host_keys);
+ auth_debug_add("%s: Your host '%.200s' is not "
+ "permitted to use this key for login.",
+ loc, remote_host);
+ /* deny access */
+ return -1;
+ }
+ }
+ /* Check source-address restriction from certificate */
+ if (opts->required_from_host_cert != NULL) {
+ switch (addr_match_cidr_list(remote_ip,
+ opts->required_from_host_cert)) {
+ case 1:
+ /* accepted */
+ break;
+ case -1:
+ default:
+ /* invalid */
+ error("%s: Certificate source-address invalid", loc);
+ /* FALLTHROUGH */
+ case 0:
+ logit("%s: Authentication tried for %.100s with valid "
+ "certificate but not from a permitted source "
+ "address (%.200s).", loc, pw->pw_name, remote_ip);
+ auth_debug_add("%s: Your address '%.200s' is not "
+ "permitted to use this certificate for login.",
+ loc, remote_ip);
+ return -1;
+ }
+ }
+ /*
+ *
+ * XXX this is spammy. We should report remotely only for keys
+ * that are successful in actual auth attempts, and not PK_OK
+ * tests.
+ */
+ auth_log_authopts(loc, opts, 1);
+
+ return 0;
+}
+
+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.
+ */
+int
+auth_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;
+}
+
+int
+auth_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 (auth_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;
+}
+
+/*
+ * 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.
+ */
+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)
+{
+ 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,
+ SSH_FP_HASH_DEFAULT, 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.
+ */
+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)
+{
+ 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 (auth_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;
+}
+
+
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list