upstream: Add a sshd_config PubkeyAuthOptions directive

This directive has a single valid option "no-touch-required" that
causes sshd to skip checking whether user presence was tested before
a security key signature was made (usually by the user touching the
key).

ok markus@

OpenBSD-Commit-ID: 46e434a49802d4ed82bc0aa38cb985c198c407de
This commit is contained in:
djm@openbsd.org 2019-11-25 00:52:46 +00:00 committed by Damien Miller
parent b7e74ea072
commit 0fddf2967a
5 changed files with 119 additions and 32 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-pubkey.c,v 1.95 2019/11/25 00:51:37 djm Exp $ */ /* $OpenBSD: auth2-pubkey.c,v 1.96 2019/11/25 00:52:46 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -68,6 +68,7 @@
#include "ssherr.h" #include "ssherr.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"
/* import */ /* import */
extern ServerOptions options; extern ServerOptions options;
@ -96,7 +97,7 @@ userauth_pubkey(struct ssh *ssh)
u_char *pkblob = NULL, *sig = NULL, have_sig; u_char *pkblob = NULL, *sig = NULL, have_sig;
size_t blen, slen; size_t blen, slen;
int r, pktype; int r, pktype;
int authenticated = 0; int req_presence = 0, authenticated = 0;
struct sshauthopt *authopts = NULL; struct sshauthopt *authopts = NULL;
struct sshkey_sig_details *sig_details = NULL; struct sshkey_sig_details *sig_details = NULL;
@ -217,10 +218,25 @@ userauth_pubkey(struct ssh *ssh)
ssh->compat, &sig_details)) == 0) { ssh->compat, &sig_details)) == 0) {
authenticated = 1; authenticated = 1;
} }
if (sig_details != NULL) { if (authenticated == 1 && sig_details != NULL) {
auth2_record_info(authctxt, "signature count = %u",
sig_details->sk_counter);
debug("%s: sk_counter = %u, sk_flags = 0x%02x", debug("%s: sk_counter = %u, sk_flags = 0x%02x",
__func__, sig_details->sk_counter, __func__, sig_details->sk_counter,
sig_details->sk_flags); sig_details->sk_flags);
req_presence = (options.pubkey_auth_options &
PUBKEYAUTH_TOUCH_REQUIRED);
if (req_presence && (sig_details->sk_flags &
SSH_SK_USER_PRESENCE_REQD) == 0) {
error("public key %s signature for %s%s from "
"%.128s port %d rejected: user presence "
"(key touch) requirement not met ", key_s,
authctxt->valid ? "" : "invalid user ",
authctxt->user, ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh));
authenticated = 0;
goto done;
}
} }
auth2_record_key(authctxt, authenticated, key); auth2_record_key(authctxt, authenticated, key);
} else { } else {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.202 2019/11/25 00:51:37 djm Exp $ */ /* $OpenBSD: monitor.c,v 1.203 2019/11/25 00:52:46 djm Exp $ */
/* /*
* Copyright 2002 Niels Provos <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -95,6 +95,7 @@
#include "authfd.h" #include "authfd.h"
#include "match.h" #include "match.h"
#include "ssherr.h" #include "ssherr.h"
#include "sk-api.h"
#ifdef GSSAPI #ifdef GSSAPI
static Gssctxt *gsscontext = NULL; static Gssctxt *gsscontext = NULL;
@ -542,7 +543,7 @@ monitor_read(struct ssh *ssh, struct monitor *pmonitor, struct mon_table *ent,
/* allowed key state */ /* allowed key state */
static int static int
monitor_allowed_key(u_char *blob, u_int bloblen) monitor_allowed_key(const u_char *blob, u_int bloblen)
{ {
/* make sure key is allowed */ /* make sure key is allowed */
if (key_blob == NULL || key_bloblen != bloblen || if (key_blob == NULL || key_bloblen != bloblen ||
@ -1247,7 +1248,7 @@ mm_answer_keyallowed(struct ssh *ssh, int sock, struct sshbuf *m)
} }
static int static int
monitor_valid_userblob(u_char *data, u_int datalen) monitor_valid_userblob(const u_char *data, u_int datalen)
{ {
struct sshbuf *b; struct sshbuf *b;
const u_char *p; const u_char *p;
@ -1256,10 +1257,8 @@ monitor_valid_userblob(u_char *data, u_int datalen)
u_char type; u_char type;
int r, fail = 0; int r, fail = 0;
if ((b = sshbuf_new()) == NULL) if ((b = sshbuf_from(data, datalen)) == NULL)
fatal("%s: sshbuf_new", __func__); fatal("%s: sshbuf_from", __func__);
if ((r = sshbuf_put(b, data, datalen)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (datafellows & SSH_OLD_SESSIONID) { if (datafellows & SSH_OLD_SESSIONID) {
p = sshbuf_ptr(b); p = sshbuf_ptr(b);
@ -1314,8 +1313,8 @@ monitor_valid_userblob(u_char *data, u_int datalen)
} }
static int static int
monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, monitor_valid_hostbasedblob(const u_char *data, u_int datalen,
char *chost) const char *cuser, const char *chost)
{ {
struct sshbuf *b; struct sshbuf *b;
const u_char *p; const u_char *p;
@ -1324,10 +1323,9 @@ monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser,
int r, fail = 0; int r, fail = 0;
u_char type; u_char type;
if ((b = sshbuf_new()) == NULL) if ((b = sshbuf_from(data, datalen)) == NULL)
fatal("%s: sshbuf_new", __func__); fatal("%s: sshbuf_new", __func__);
if ((r = sshbuf_put(b, data, datalen)) != 0 || if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0)
(r = sshbuf_get_string_direct(b, &p, &len)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
if ((session_id2 == NULL) || if ((session_id2 == NULL) ||
@ -1387,15 +1385,15 @@ int
mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m) mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m)
{ {
struct sshkey *key; struct sshkey *key;
u_char *signature, *data, *blob; const u_char *signature, *data, *blob;
char *sigalg; char *sigalg = NULL, *fp = NULL;
size_t signaturelen, datalen, bloblen; size_t signaturelen, datalen, bloblen;
int r, ret, valid_data = 0, encoded_ret; int r, ret, req_presence = 0, valid_data = 0, encoded_ret;
struct sshkey_sig_details *sig_details = NULL; struct sshkey_sig_details *sig_details = NULL;
if ((r = sshbuf_get_string(m, &blob, &bloblen)) != 0 || if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 ||
(r = sshbuf_get_string(m, &signature, &signaturelen)) != 0 || (r = sshbuf_get_string_direct(m, &signature, &signaturelen)) != 0 ||
(r = sshbuf_get_string(m, &data, &datalen)) != 0 || (r = sshbuf_get_string_direct(m, &data, &datalen)) != 0 ||
(r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
@ -1430,23 +1428,36 @@ mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m)
if (!valid_data) if (!valid_data)
fatal("%s: bad signature data blob", __func__); fatal("%s: bad signature data blob", __func__);
if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
fatal("%s: sshkey_fingerprint failed", __func__);
ret = sshkey_verify(key, signature, signaturelen, data, datalen, ret = sshkey_verify(key, signature, signaturelen, data, datalen,
sigalg, ssh->compat, &sig_details); sigalg, ssh->compat, &sig_details);
debug3("%s: %s %p signature %s%s%s", __func__, auth_method, key, debug3("%s: %s %p signature %s%s%s", __func__, auth_method, key,
(ret == 0) ? "verified" : "unverified", (ret == 0) ? "verified" : "unverified",
(ret != 0) ? ": " : "", (ret != 0) ? ssh_err(ret) : ""); (ret != 0) ? ": " : "", (ret != 0) ? ssh_err(ret) : "");
auth2_record_key(authctxt, ret == 0, key);
free(blob); if (ret == 0 && key_blobtype == MM_USERKEY && sig_details != NULL) {
free(signature); req_presence = (options.pubkey_auth_options &
free(data); PUBKEYAUTH_TOUCH_REQUIRED);
free(sigalg); if (req_presence &&
(sig_details->sk_flags & SSH_SK_USER_PRESENCE_REQD) == 0) {
error("public key %s %s signature for %s%s from %.128s "
"port %d rejected: user presence (key touch) "
"requirement not met ", sshkey_type(key), fp,
authctxt->valid ? "" : "invalid user ",
authctxt->user, ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh));
ret = SSH_ERR_SIGNATURE_INVALID;
}
}
auth2_record_key(authctxt, ret == 0, key);
if (key_blobtype == MM_USERKEY) if (key_blobtype == MM_USERKEY)
auth_activate_options(ssh, key_opts); auth_activate_options(ssh, key_opts);
monitor_reset_key_state(); monitor_reset_key_state();
sshkey_free(key);
sshbuf_reset(m); sshbuf_reset(m);
/* encode ret != 0 as positive integer, since we're sending u32 */ /* encode ret != 0 as positive integer, since we're sending u32 */
@ -1462,6 +1473,10 @@ mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m)
sshkey_sig_details_free(sig_details); sshkey_sig_details_free(sig_details);
mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m); mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m);
free(sigalg);
free(fp);
sshkey_free(key);
return ret == 0; return ret == 0;
} }

View File

@ -1,5 +1,5 @@
/* $OpenBSD: servconf.c,v 1.353 2019/10/31 21:17:49 djm Exp $ */ /* $OpenBSD: servconf.c,v 1.354 2019/11/25 00:52:46 djm Exp $ */
/* /*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved * All rights reserved
@ -118,6 +118,7 @@ initialize_server_options(ServerOptions *options)
options->hostbased_key_types = NULL; options->hostbased_key_types = NULL;
options->hostkeyalgorithms = NULL; options->hostkeyalgorithms = NULL;
options->pubkey_authentication = -1; options->pubkey_authentication = -1;
options->pubkey_auth_options = -1;
options->pubkey_key_types = NULL; options->pubkey_key_types = NULL;
options->kerberos_authentication = -1; options->kerberos_authentication = -1;
options->kerberos_or_local_passwd = -1; options->kerberos_or_local_passwd = -1;
@ -341,6 +342,8 @@ fill_default_server_options(ServerOptions *options)
options->hostbased_uses_name_from_packet_only = 0; options->hostbased_uses_name_from_packet_only = 0;
if (options->pubkey_authentication == -1) if (options->pubkey_authentication == -1)
options->pubkey_authentication = 1; options->pubkey_authentication = 1;
if (options->pubkey_auth_options == -1)
options->pubkey_auth_options = 0;
if (options->kerberos_authentication == -1) if (options->kerberos_authentication == -1)
options->kerberos_authentication = 0; options->kerberos_authentication = 0;
if (options->kerberos_or_local_passwd == -1) if (options->kerberos_or_local_passwd == -1)
@ -509,7 +512,7 @@ typedef enum {
sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
sStreamLocalBindMask, sStreamLocalBindUnlink, sStreamLocalBindMask, sStreamLocalBindUnlink,
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
sExposeAuthInfo, sRDomain, sExposeAuthInfo, sRDomain, sPubkeyAuthOptions,
sDeprecated, sIgnore, sUnsupported sDeprecated, sIgnore, sUnsupported
} ServerOpCodes; } ServerOpCodes;
@ -551,6 +554,7 @@ static struct {
{ "rsaauthentication", sDeprecated, SSHCFG_ALL }, { "rsaauthentication", sDeprecated, SSHCFG_ALL },
{ "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
{ "pubkeyacceptedkeytypes", sPubkeyAcceptedKeyTypes, SSHCFG_ALL }, { "pubkeyacceptedkeytypes", sPubkeyAcceptedKeyTypes, SSHCFG_ALL },
{ "pubkeyauthoptions", sPubkeyAuthOptions, SSHCFG_ALL },
{ "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */
#ifdef KRB5 #ifdef KRB5
{ "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
@ -1468,6 +1472,24 @@ process_server_config_line(ServerOptions *options, char *line,
charptr = &options->pubkey_key_types; charptr = &options->pubkey_key_types;
goto parse_keytypes; goto parse_keytypes;
case sPubkeyAuthOptions:
intptr = &options->pubkey_auth_options;
value = 0;
while ((arg = strdelim(&cp)) && *arg != '\0') {
if (strcasecmp(arg, "none") == 0)
continue;
if (strcasecmp(arg, "touch-required") == 0)
value |= PUBKEYAUTH_TOUCH_REQUIRED;
else {
fatal("%s line %d: unsupported "
"PubkeyAuthOptions option %s",
filename, linenum, arg);
}
}
if (*activep && *intptr == -1)
*intptr = value;
break;
case sKerberosAuthentication: case sKerberosAuthentication:
intptr = &options->kerberos_authentication; intptr = &options->kerberos_authentication;
goto parse_flag; goto parse_flag;
@ -2290,6 +2312,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
M_CP_INTOPT(password_authentication); M_CP_INTOPT(password_authentication);
M_CP_INTOPT(gss_authentication); M_CP_INTOPT(gss_authentication);
M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(pubkey_authentication);
M_CP_INTOPT(pubkey_auth_options);
M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(kerberos_authentication);
M_CP_INTOPT(hostbased_authentication); M_CP_INTOPT(hostbased_authentication);
M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(hostbased_uses_name_from_packet_only);
@ -2711,4 +2734,10 @@ dump_config(ServerOptions *o)
o->permit_user_env_whitelist); o->permit_user_env_whitelist);
} }
printf("pubkeyauthoptions");
if (o->pubkey_auth_options == 0)
printf(" none");
if (o->pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED)
printf(" touch-required");
printf("\n");
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.140 2019/04/18 18:56:16 dtucker Exp $ */ /* $OpenBSD: servconf.h,v 1.141 2019/11/25 00:52:46 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -42,6 +42,9 @@
/* Magic name for internal sftp-server */ /* Magic name for internal sftp-server */
#define INTERNAL_SFTP_NAME "internal-sftp" #define INTERNAL_SFTP_NAME "internal-sftp"
/* PubkeyAuthOptions flags */
#define PUBKEYAUTH_TOUCH_REQUIRED 1
struct ssh; struct ssh;
struct fwd_perm_list; struct fwd_perm_list;
@ -114,6 +117,7 @@ typedef struct {
char *ca_sign_algorithms; /* Allowed CA signature algorithms */ char *ca_sign_algorithms; /* Allowed CA signature algorithms */
int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */
char *pubkey_key_types; /* Key types allowed for public key */ char *pubkey_key_types; /* Key types allowed for public key */
int pubkey_auth_options; /* -1 or mask of PUBKEYAUTH_* flags */
int kerberos_authentication; /* If true, permit Kerberos int kerberos_authentication; /* If true, permit Kerberos
* authentication. */ * authentication. */
int kerberos_or_local_passwd; /* If true, permit kerberos int kerberos_or_local_passwd; /* If true, permit kerberos

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: sshd_config.5,v 1.292 2019/11/18 04:55:02 djm Exp $ .\" $OpenBSD: sshd_config.5,v 1.293 2019/11/25 00:52:46 djm Exp $
.Dd $Mdocdate: November 18 2019 $ .Dd $Mdocdate: November 25 2019 $
.Dt SSHD_CONFIG 5 .Dt SSHD_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -1444,6 +1444,29 @@ ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Pp .Pp
The list of available key types may also be obtained using The list of available key types may also be obtained using
.Qq ssh -Q key . .Qq ssh -Q key .
.It Cm PubkeyAuthOptions
Sets one or more public key authentication options.
Two option keywords are currently supported:
.Cm none (the default; indicating no additional options are enabled)
and
.Cm touch-required .
.Pp
The
.Cm touch-required
option causes public key authentication using a security key algorithm
(i.e.
.Cm ecdsa-sk
or
.Cm ed25519-sk )
to always require the signature to attest that a physically present user
explicitly confirmed the authentication (usually by touching the security key).
By default,
.Xr sshd 8
requires key touch unless overridden with an authorized_keys option.
The
.Cm touch-required
flag disables this override.
This option has no effect for other, non-security key public key types.
.It Cm PubkeyAuthentication .It Cm PubkeyAuthentication
Specifies whether public key authentication is allowed. Specifies whether public key authentication is allowed.
The default is The default is