mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-27 15:54:22 +02:00
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
This commit is contained in:
parent
3b0b142d2a
commit
c83d8c4d6f
@ -123,7 +123,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
|
|||||||
auth.o auth2.o auth-options.o session.o \
|
auth.o auth2.o auth-options.o session.o \
|
||||||
auth2-chall.o groupaccess.o \
|
auth2-chall.o groupaccess.o \
|
||||||
auth-bsdauth.o auth2-hostbased.o auth2-kbdint.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 \
|
monitor.o monitor_wrap.o auth-krb5.o \
|
||||||
auth2-gss.o gss-serv.o gss-serv-krb5.o \
|
auth2-gss.o gss-serv.o gss-serv-krb5.o \
|
||||||
loginrec.o auth-pam.o auth-shadow.o auth-sia.o \
|
loginrec.o auth-pam.o auth-shadow.o auth-sia.o \
|
||||||
|
94
auth.c
94
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.
|
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -912,95 +912,3 @@ auth_restrict_session(struct ssh *ssh)
|
|||||||
fatal_f("failed to restrict session");
|
fatal_f("failed to restrict session");
|
||||||
sshauthopt_free(restricted);
|
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;
|
|
||||||
}
|
|
||||||
|
18
auth.h
18
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.
|
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||||
@ -29,6 +29,7 @@
|
|||||||
#define AUTH_H
|
#define AUTH_H
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef HAVE_LOGIN_CAP
|
#ifdef HAVE_LOGIN_CAP
|
||||||
#include <login_cap.h>
|
#include <login_cap.h>
|
||||||
@ -44,6 +45,7 @@ struct passwd;
|
|||||||
struct ssh;
|
struct ssh;
|
||||||
struct sshbuf;
|
struct sshbuf;
|
||||||
struct sshkey;
|
struct sshkey;
|
||||||
|
struct sshkey_cert;
|
||||||
struct sshauthopt;
|
struct sshauthopt;
|
||||||
|
|
||||||
typedef struct Authctxt Authctxt;
|
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 *);
|
const struct sshauthopt *auth_options(struct ssh *);
|
||||||
int auth_activate_options(struct ssh *, struct sshauthopt *);
|
int auth_activate_options(struct ssh *, struct sshauthopt *);
|
||||||
void auth_restrict_session(struct ssh *);
|
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);
|
void auth_log_authopts(const char *, const struct sshauthopt *, int);
|
||||||
|
|
||||||
/* debug messages during authentication */
|
/* debug messages during authentication */
|
||||||
@ -226,6 +226,18 @@ void auth_debug_reset(void);
|
|||||||
|
|
||||||
struct passwd *fakepw(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 *);
|
int sys_auth_passwd(struct ssh *, const char *);
|
||||||
|
|
||||||
#if defined(KRB5) && !defined(HEIMDAL)
|
#if defined(KRB5) && !defined(HEIMDAL)
|
||||||
|
311
auth2-pubkey.c
311
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) 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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@ -26,11 +27,9 @@
|
|||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#ifdef HAVE_PATHS_H
|
#ifdef HAVE_PATHS_H
|
||||||
# include <paths.h>
|
# include <paths.h>
|
||||||
#endif
|
#endif
|
||||||
@ -67,7 +66,6 @@
|
|||||||
#include "authfile.h"
|
#include "authfile.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "ssherr.h"
|
#include "ssherr.h"
|
||||||
#include "kex.h"
|
|
||||||
#include "channels.h" /* XXX for session.h */
|
#include "channels.h" /* XXX for session.h */
|
||||||
#include "session.h" /* XXX for child_set_env(); refactor? */
|
#include "session.h" /* XXX for child_set_env(); refactor? */
|
||||||
#include "sk-api.h"
|
#include "sk-api.h"
|
||||||
@ -321,120 +319,6 @@ done:
|
|||||||
return authenticated;
|
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
|
static int
|
||||||
match_principals_file(struct passwd *pw, char *file,
|
match_principals_file(struct passwd *pw, char *file,
|
||||||
struct sshkey_cert *cert, struct sshauthopt **authoptsp)
|
struct sshkey_cert *cert, struct sshauthopt **authoptsp)
|
||||||
@ -451,7 +335,7 @@ match_principals_file(struct passwd *pw, char *file,
|
|||||||
restore_uid();
|
restore_uid();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
success = process_principals(f, file, cert, authoptsp);
|
success = auth_process_principals(f, file, cert, authoptsp);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
restore_uid();
|
restore_uid();
|
||||||
return success;
|
return success;
|
||||||
@ -567,7 +451,7 @@ match_principals_command(struct passwd *user_pw,
|
|||||||
uid_swapped = 1;
|
uid_swapped = 1;
|
||||||
temporarily_use_uid(runas_pw);
|
temporarily_use_uid(runas_pw);
|
||||||
|
|
||||||
ok = process_principals(f, "(command)", cert, authoptsp);
|
ok = auth_process_principals(f, "(command)", cert, authoptsp);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
f = NULL;
|
f = NULL;
|
||||||
@ -595,189 +479,6 @@ match_principals_command(struct passwd *user_pw,
|
|||||||
return found_principal;
|
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 */
|
/* Authenticate a certificate key against TrustedUserCAKeys */
|
||||||
static int
|
static int
|
||||||
user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,
|
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);
|
debug("trying public key file %s", file);
|
||||||
if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
|
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);
|
key, remote_ip, remote_host, authoptsp);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
@ -1018,7 +719,7 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key,
|
|||||||
uid_swapped = 1;
|
uid_swapped = 1;
|
||||||
temporarily_use_uid(runas_pw);
|
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,
|
options.authorized_keys_command, key, remote_ip,
|
||||||
remote_host, authoptsp);
|
remote_host, authoptsp);
|
||||||
|
|
||||||
|
442
auth2-pubkeyfile.c
Normal file
442
auth2-pubkeyfile.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user