upstream: support for requiring user verified FIDO keys in sshd

This adds a "verify-required" authorized_keys flag and a corresponding
sshd_config option that tells sshd to require that FIDO keys verify the
user identity before completing the signing/authentication attempt.
Whether or not user verification was performed is already baked into the
signature made on the FIDO token, so this is just plumbing that flag
through and adding ways to require it.

feedback and ok markus@

OpenBSD-Commit-ID: 3a2313aae153e043d57763d766bb6d55c4e276e6
This commit is contained in:
djm@openbsd.org 2020-08-27 01:07:09 +00:00 committed by Damien Miller
parent 9b8ad93824
commit 801c9f095e
9 changed files with 88 additions and 25 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth-options.c,v 1.92 2020/03/06 18:15:38 markus Exp $ */ /* $OpenBSD: auth-options.c,v 1.93 2020/08/27 01:07:09 djm Exp $ */
/* /*
* Copyright (c) 2018 Damien Miller <djm@mindrot.org> * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
* *
@ -119,7 +119,10 @@ cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
} }
} }
if (!found && (which & OPTIONS_CRITICAL) != 0) { if (!found && (which & OPTIONS_CRITICAL) != 0) {
if (strcmp(name, "force-command") == 0) { if (strcmp(name, "verify-required") == 0) {
opts->require_verify = 1;
found = 1;
} else if (strcmp(name, "force-command") == 0) {
if ((r = sshbuf_get_cstring(data, &command, if ((r = sshbuf_get_cstring(data, &command,
NULL)) != 0) { NULL)) != 0) {
error("Unable to parse \"%s\" " error("Unable to parse \"%s\" "
@ -134,8 +137,7 @@ cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
} }
opts->force_command = command; opts->force_command = command;
found = 1; found = 1;
} } else if (strcmp(name, "source-address") == 0) {
if (strcmp(name, "source-address") == 0) {
if ((r = sshbuf_get_cstring(data, &allowed, if ((r = sshbuf_get_cstring(data, &allowed,
NULL)) != 0) { NULL)) != 0) {
error("Unable to parse \"%s\" " error("Unable to parse \"%s\" "
@ -351,6 +353,8 @@ sshauthopt_parse(const char *opts, const char **errstrp)
ret->permit_x11_forwarding_flag = r == 1; ret->permit_x11_forwarding_flag = r == 1;
} else if ((r = opt_flag("touch-required", 1, &opts)) != -1) { } else if ((r = opt_flag("touch-required", 1, &opts)) != -1) {
ret->no_require_user_presence = r != 1; /* NB. flip */ ret->no_require_user_presence = r != 1; /* NB. flip */
} else if ((r = opt_flag("verify-required", 1, &opts)) != -1) {
ret->require_verify = r == 1;
} else if ((r = opt_flag("pty", 1, &opts)) != -1) { } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
ret->permit_pty_flag = r == 1; ret->permit_pty_flag = r == 1;
} else if ((r = opt_flag("user-rc", 1, &opts)) != -1) { } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
@ -572,6 +576,7 @@ sshauthopt_merge(const struct sshauthopt *primary,
} }
#define OPTFLAG_AND(x) ret->x = (primary->x == 1) && (additional->x == 1) #define OPTFLAG_AND(x) ret->x = (primary->x == 1) && (additional->x == 1)
#define OPTFLAG_OR(x) ret->x = (primary->x == 1) || (additional->x == 1)
/* Permissive flags are logical-AND (i.e. must be set in both) */ /* Permissive flags are logical-AND (i.e. must be set in both) */
OPTFLAG_AND(permit_port_forwarding_flag); OPTFLAG_AND(permit_port_forwarding_flag);
OPTFLAG_AND(permit_agent_forwarding_flag); OPTFLAG_AND(permit_agent_forwarding_flag);
@ -579,6 +584,8 @@ sshauthopt_merge(const struct sshauthopt *primary,
OPTFLAG_AND(permit_pty_flag); OPTFLAG_AND(permit_pty_flag);
OPTFLAG_AND(permit_user_rc); OPTFLAG_AND(permit_user_rc);
OPTFLAG_AND(no_require_user_presence); OPTFLAG_AND(no_require_user_presence);
/* Restrictive flags are logical-OR (i.e. must be set in either) */
OPTFLAG_OR(require_verify);
#undef OPTFLAG_AND #undef OPTFLAG_AND
/* Earliest expiry time should win */ /* Earliest expiry time should win */
@ -649,6 +656,7 @@ sshauthopt_copy(const struct sshauthopt *orig)
OPTSCALAR(force_tun_device); OPTSCALAR(force_tun_device);
OPTSCALAR(valid_before); OPTSCALAR(valid_before);
OPTSCALAR(no_require_user_presence); OPTSCALAR(no_require_user_presence);
OPTSCALAR(require_verify);
#undef OPTSCALAR #undef OPTSCALAR
#define OPTSTRING(x) \ #define OPTSTRING(x) \
do { \ do { \
@ -781,7 +789,8 @@ sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
(r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
(r = sshbuf_put_u8(m, opts->restricted)) != 0 || (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
(r = sshbuf_put_u8(m, opts->cert_authority)) != 0 || (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 ||
(r = sshbuf_put_u8(m, opts->no_require_user_presence)) != 0) (r = sshbuf_put_u8(m, opts->no_require_user_presence)) != 0 ||
(r = sshbuf_put_u8(m, opts->require_verify)) != 0)
return r; return r;
/* Simple integer options */ /* Simple integer options */
@ -844,6 +853,7 @@ sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
OPT_FLAG(restricted); OPT_FLAG(restricted);
OPT_FLAG(cert_authority); OPT_FLAG(cert_authority);
OPT_FLAG(no_require_user_presence); OPT_FLAG(no_require_user_presence);
OPT_FLAG(require_verify);
#undef OPT_FLAG #undef OPT_FLAG
/* Simple integer options */ /* Simple integer options */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth-options.h,v 1.29 2019/11/25 00:54:23 djm Exp $ */ /* $OpenBSD: auth-options.h,v 1.30 2020/08/27 01:07:09 djm Exp $ */
/* /*
* Copyright (c) 2018 Damien Miller <djm@mindrot.org> * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
@ -71,6 +71,8 @@ struct sshauthopt {
/* Key requires user presence asserted */ /* Key requires user presence asserted */
int no_require_user_presence; int no_require_user_presence;
/* Key requires user verification (e.g. PIN) */
int require_verify;
}; };
struct sshauthopt *sshauthopt_new(void); struct sshauthopt *sshauthopt_new(void);

9
auth.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth.c,v 1.146 2020/01/31 22:42:45 djm Exp $ */ /* $OpenBSD: auth.c,v 1.147 2020/08/27 01:07:09 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -1006,21 +1006,22 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
snprintf(buf, sizeof(buf), "%d", opts->force_tun_device); snprintf(buf, sizeof(buf), "%d", opts->force_tun_device);
/* Try to keep this alphabetically sorted */ /* Try to keep this alphabetically sorted */
snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s", snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
opts->permit_agent_forwarding_flag ? " agent-forwarding" : "", opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
opts->force_command == NULL ? "" : " command", opts->force_command == NULL ? "" : " command",
do_env ? " environment" : "", do_env ? " environment" : "",
opts->valid_before == 0 ? "" : "expires", opts->valid_before == 0 ? "" : "expires",
opts->no_require_user_presence ? " no-touch-required" : "",
do_permitopen ? " permitopen" : "", do_permitopen ? " permitopen" : "",
do_permitlisten ? " permitlisten" : "", do_permitlisten ? " permitlisten" : "",
opts->permit_port_forwarding_flag ? " port-forwarding" : "", opts->permit_port_forwarding_flag ? " port-forwarding" : "",
opts->cert_principals == NULL ? "" : " principals", opts->cert_principals == NULL ? "" : " principals",
opts->permit_pty_flag ? " pty" : "", opts->permit_pty_flag ? " pty" : "",
opts->require_verify ? " uv" : "",
opts->force_tun_device == -1 ? "" : " tun=", opts->force_tun_device == -1 ? "" : " tun=",
opts->force_tun_device == -1 ? "" : buf, opts->force_tun_device == -1 ? "" : buf,
opts->permit_user_rc ? " user-rc" : "", opts->permit_user_rc ? " user-rc" : "",
opts->permit_x11_forwarding_flag ? " x11-forwarding" : "", opts->permit_x11_forwarding_flag ? " x11-forwarding" : "");
opts->no_require_user_presence ? " no-touch-required" : "");
debug("%s: %s", loc, msg); debug("%s: %s", loc, msg);
if (do_remote) if (do_remote)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-pubkey.c,v 1.99 2020/02/06 22:30:54 naddy Exp $ */ /* $OpenBSD: auth2-pubkey.c,v 1.100 2020/08/27 01:07:09 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -97,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 req_presence = 0, authenticated = 0; int req_presence = 0, req_verify = 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;
@ -239,6 +239,20 @@ userauth_pubkey(struct ssh *ssh)
authenticated = 0; authenticated = 0;
goto done; goto done;
} }
req_verify = (options.pubkey_auth_options &
PUBKEYAUTH_VERIFY_REQUIRED) ||
authopts->require_verify;
if (req_verify && (sig_details->sk_flags &
SSH_SK_USER_VERIFICATION_REQD) == 0) {
error("public key %s signature for %s%s from "
"%.128s port %d rejected: user "
"verification 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.213 2020/08/27 01:06:18 djm Exp $ */ /* $OpenBSD: monitor.c,v 1.214 2020/08/27 01:07:09 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>
@ -1387,7 +1387,8 @@ mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m)
const u_char *signature, *data, *blob; const u_char *signature, *data, *blob;
char *sigalg = NULL, *fp = NULL; char *sigalg = NULL, *fp = NULL;
size_t signaturelen, datalen, bloblen; size_t signaturelen, datalen, bloblen;
int r, ret, req_presence = 0, valid_data = 0, encoded_ret; int r, ret, req_presence = 0, req_verify = 0, valid_data = 0;
int encoded_ret;
struct sshkey_sig_details *sig_details = NULL; struct sshkey_sig_details *sig_details = NULL;
if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 || if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 ||
@ -1452,6 +1453,18 @@ mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m)
ssh_remote_port(ssh)); ssh_remote_port(ssh));
ret = SSH_ERR_SIGNATURE_INVALID; ret = SSH_ERR_SIGNATURE_INVALID;
} }
req_verify = (options.pubkey_auth_options &
PUBKEYAUTH_VERIFY_REQUIRED) || key_opts->require_verify;
if (req_verify &&
(sig_details->sk_flags & SSH_SK_USER_VERIFICATION_REQD) == 0) {
error("public key %s %s signature for %s%s from %.128s "
"port %d rejected: user verification 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); auth2_record_key(authctxt, ret == 0, key);

View File

@ -1,5 +1,5 @@
/* $OpenBSD: servconf.c,v 1.367 2020/07/05 23:59:45 djm Exp $ */ /* $OpenBSD: servconf.c,v 1.368 2020/08/27 01:07:09 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
@ -1527,6 +1527,8 @@ process_server_config_line_depth(ServerOptions *options, char *line,
continue; continue;
if (strcasecmp(arg, "touch-required") == 0) if (strcasecmp(arg, "touch-required") == 0)
value |= PUBKEYAUTH_TOUCH_REQUIRED; value |= PUBKEYAUTH_TOUCH_REQUIRED;
else if (strcasecmp(arg, "verify-required") == 0)
value |= PUBKEYAUTH_VERIFY_REQUIRED;
else { else {
fatal("%s line %d: unsupported " fatal("%s line %d: unsupported "
"PubkeyAuthOptions option %s", "PubkeyAuthOptions option %s",
@ -2937,5 +2939,7 @@ dump_config(ServerOptions *o)
printf(" none"); printf(" none");
if (o->pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED) if (o->pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED)
printf(" touch-required"); printf(" touch-required");
if (o->pubkey_auth_options & PUBKEYAUTH_VERIFY_REQUIRED)
printf(" verify-required");
printf("\n"); printf("\n");
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.145 2020/07/05 23:59:45 djm Exp $ */ /* $OpenBSD: servconf.h,v 1.146 2020/08/27 01:07:10 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -50,7 +50,8 @@
#define INTERNAL_SFTP_NAME "internal-sftp" #define INTERNAL_SFTP_NAME "internal-sftp"
/* PubkeyAuthOptions flags */ /* PubkeyAuthOptions flags */
#define PUBKEYAUTH_TOUCH_REQUIRED 1 #define PUBKEYAUTH_TOUCH_REQUIRED (1)
#define PUBKEYAUTH_VERIFY_REQUIRED (1<<1)
struct ssh; struct ssh;
struct fwd_perm_list; struct fwd_perm_list;

11
sshd.8
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.8,v 1.312 2020/01/25 06:03:10 djm Exp $ .\" $OpenBSD: sshd.8,v 1.313 2020/08/27 01:07:10 djm Exp $
.Dd $Mdocdate: January 25 2020 $ .Dd $Mdocdate: August 27 2020 $
.Dt SSHD 8 .Dt SSHD 8
.Os .Os
.Sh NAME .Sh NAME
@ -631,6 +631,13 @@ This option only makes sense for the FIDO authenticator algorithms
.Cm ecdsa-sk .Cm ecdsa-sk
and and
.Cm ed25519-sk . .Cm ed25519-sk .
.It Cm verify-required
Require that signatures made using this key attest that they verified
the user, e.g. via a PIN.
This option only makes sense for the FIDO authenticator algorithms
.Cm ecdsa-sk
and
.Cm ed25519-sk .
.It Cm restrict .It Cm restrict
Enable all restrictions, i.e. disable port, agent and X11 forwarding, Enable all restrictions, i.e. disable port, agent and X11 forwarding,
as well as disabling PTY allocation as well as disabling PTY allocation

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.312 2020/05/29 05:37:03 djm Exp $ .\" $OpenBSD: sshd_config.5,v 1.313 2020/08/27 01:07:10 djm Exp $
.Dd $Mdocdate: May 29 2020 $ .Dd $Mdocdate: August 27 2020 $
.Dt SSHD_CONFIG 5 .Dt SSHD_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -1476,11 +1476,12 @@ The list of available key types may also be obtained using
.Qq ssh -Q PubkeyAcceptedKeyTypes . .Qq ssh -Q PubkeyAcceptedKeyTypes .
.It Cm PubkeyAuthOptions .It Cm PubkeyAuthOptions
Sets one or more public key authentication options. Sets one or more public key authentication options.
Two option keywords are currently supported: The supported keywords are:
.Cm none .Cm none
(the default; indicating no additional options are enabled) (the default; indicating no additional options are enabled),
.Cm touch-required
and and
.Cm touch-required . .Cm verify-required .
.Pp .Pp
The The
.Cm touch-required .Cm touch-required
@ -1497,7 +1498,17 @@ requires user presence unless overridden with an authorized_keys option.
The The
.Cm touch-required .Cm touch-required
flag disables this override. flag disables this override.
This option has no effect for other, non-authenticator public key types. .Pp
The
.Cm verify-required
option requires a FIDO key signature attest that verified the user, e.g.
via a PIN.
.Pp
Neither the
.Cm touch-required
or
.Cm verify-required
options have any effect for other, non-FIDO 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