upstream commit
add ssh_config CertificateFile option to explicitly list a certificate; patch from Meghana Bhat on bz#2436; ok markus@ Upstream-ID: 58648ec53c510b41c1f46d8fe293aadc87229ab8
This commit is contained in:
parent
e3cbb06ade
commit
4e44a79a07
47
readconf.c
47
readconf.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.c,v 1.240 2015/08/21 23:53:08 djm Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.241 2015/09/24 06:15:11 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -135,6 +135,7 @@ typedef enum {
|
|||
oPasswordAuthentication, oRSAAuthentication,
|
||||
oChallengeResponseAuthentication, oXAuthLocation,
|
||||
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
|
||||
oCertificateFile,
|
||||
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
|
||||
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
|
||||
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
|
||||
|
@ -202,6 +203,7 @@ static struct {
|
|||
{ "identityfile", oIdentityFile },
|
||||
{ "identityfile2", oIdentityFile }, /* obsolete */
|
||||
{ "identitiesonly", oIdentitiesOnly },
|
||||
{ "certificatefile", oCertificateFile },
|
||||
{ "hostname", oHostName },
|
||||
{ "hostkeyalias", oHostKeyAlias },
|
||||
{ "proxycommand", oProxyCommand },
|
||||
|
@ -365,6 +367,30 @@ clear_forwardings(Options *options)
|
|||
options->tun_open = SSH_TUNMODE_NO;
|
||||
}
|
||||
|
||||
void
|
||||
add_certificate_file(Options *options, const char *path, int userprovided)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
|
||||
fatal("Too many certificate files specified (max %d)",
|
||||
SSH_MAX_CERTIFICATE_FILES);
|
||||
|
||||
/* Avoid registering duplicates */
|
||||
for (i = 0; i < options->num_certificate_files; i++) {
|
||||
if (options->certificate_file_userprovided[i] == userprovided &&
|
||||
strcmp(options->certificate_files[i], path) == 0) {
|
||||
debug2("%s: ignoring duplicate key %s", __func__, path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
options->certificate_file_userprovided[options->num_certificate_files] =
|
||||
userprovided;
|
||||
options->certificate_files[options->num_certificate_files++] =
|
||||
xstrdup(path);
|
||||
}
|
||||
|
||||
void
|
||||
add_identity_file(Options *options, const char *dir, const char *filename,
|
||||
int userprovided)
|
||||
|
@ -981,6 +1007,24 @@ parse_time:
|
|||
}
|
||||
break;
|
||||
|
||||
case oCertificateFile:
|
||||
arg = strdelim(&s);
|
||||
if (!arg || *arg == '\0')
|
||||
fatal("%.200s line %d: Missing argument.",
|
||||
filename, linenum);
|
||||
if (*activep) {
|
||||
intptr = &options->num_certificate_files;
|
||||
if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
|
||||
fatal("%.200s line %d: Too many certificate "
|
||||
"files specified (max %d).",
|
||||
filename, linenum,
|
||||
SSH_MAX_CERTIFICATE_FILES);
|
||||
}
|
||||
add_certificate_file(options, arg,
|
||||
flags & SSHCONF_USERCONF);
|
||||
}
|
||||
break;
|
||||
|
||||
case oXAuthLocation:
|
||||
charptr=&options->xauth_location;
|
||||
goto parse_string;
|
||||
|
@ -1625,6 +1669,7 @@ initialize_options(Options * options)
|
|||
options->hostkeyalgorithms = NULL;
|
||||
options->protocol = SSH_PROTO_UNKNOWN;
|
||||
options->num_identity_files = 0;
|
||||
options->num_certificate_files = 0;
|
||||
options->hostname = NULL;
|
||||
options->host_key_alias = NULL;
|
||||
options->proxy_command = NULL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.h,v 1.110 2015/07/10 06:21:53 markus Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.111 2015/09/24 06:15:11 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -95,6 +95,11 @@ typedef struct {
|
|||
int identity_file_userprovided[SSH_MAX_IDENTITY_FILES];
|
||||
struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES];
|
||||
|
||||
int num_certificate_files; /* Number of extra certificates for ssh. */
|
||||
char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
|
||||
int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
|
||||
struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
|
||||
|
||||
/* Local TCP/IP forward requests. */
|
||||
int num_local_forwards;
|
||||
struct Forward *local_forwards;
|
||||
|
@ -194,5 +199,6 @@ void dump_client_config(Options *o, const char *host);
|
|||
void add_local_forward(Options *, const struct Forward *);
|
||||
void add_remote_forward(Options *, const struct Forward *);
|
||||
void add_identity_file(Options *, const char *, const char *, int);
|
||||
void add_certificate_file(Options *, const char *, int);
|
||||
|
||||
#endif /* READCONF_H */
|
||||
|
|
8
ssh.1
8
ssh.1
|
@ -33,8 +33,8 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh.1,v 1.362 2015/09/11 03:42:32 djm Exp $
|
||||
.Dd $Mdocdate: September 11 2015 $
|
||||
.\" $OpenBSD: ssh.1,v 1.363 2015/09/24 06:15:11 djm Exp $
|
||||
.Dd $Mdocdate: September 24 2015 $
|
||||
.Dt SSH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -304,6 +304,9 @@ It is possible to have multiple
|
|||
.Fl i
|
||||
options (and multiple identities specified in
|
||||
configuration files).
|
||||
If no certificates have been explicitly specified by
|
||||
.Cm CertificateFile
|
||||
directive,
|
||||
.Nm
|
||||
will also try to load certificate information from the filename obtained
|
||||
by appending
|
||||
|
@ -468,6 +471,7 @@ For full details of the options listed below, and their possible values, see
|
|||
.It CanonicalizeHostname
|
||||
.It CanonicalizeMaxDots
|
||||
.It CanonicalizePermittedCNAMEs
|
||||
.It CertificateFile
|
||||
.It ChallengeResponseAuthentication
|
||||
.It CheckHostIP
|
||||
.It Cipher
|
||||
|
|
65
ssh.c
65
ssh.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh.c,v 1.425 2015/09/11 06:55:46 jmc Exp $ */
|
||||
/* $OpenBSD: ssh.c,v 1.426 2015/09/24 06:15:11 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -1354,6 +1354,10 @@ main(int ac, char **av)
|
|||
options.identity_keys[i] = NULL;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < options.num_certificate_files; i++) {
|
||||
free(options.certificate_files[i]);
|
||||
options.certificate_files[i] = NULL;
|
||||
}
|
||||
|
||||
exit_status = compat20 ? ssh_session2() : ssh_session();
|
||||
packet_close();
|
||||
|
@ -1940,25 +1944,30 @@ ssh_session2(void)
|
|||
options.escape_char : SSH_ESCAPECHAR_NONE, id);
|
||||
}
|
||||
|
||||
/* Loads all IdentityFile and CertificateFile keys */
|
||||
static void
|
||||
load_public_identity_files(void)
|
||||
{
|
||||
char *filename, *cp, thishost[NI_MAXHOST];
|
||||
char *pwdir = NULL, *pwname = NULL;
|
||||
int i = 0;
|
||||
Key *public;
|
||||
struct passwd *pw;
|
||||
u_int n_ids;
|
||||
int i;
|
||||
u_int n_ids, n_certs;
|
||||
char *identity_files[SSH_MAX_IDENTITY_FILES];
|
||||
Key *identity_keys[SSH_MAX_IDENTITY_FILES];
|
||||
char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
|
||||
struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
|
||||
#ifdef ENABLE_PKCS11
|
||||
Key **keys;
|
||||
int nkeys;
|
||||
#endif /* PKCS11 */
|
||||
|
||||
n_ids = 0;
|
||||
n_ids = n_certs = 0;
|
||||
memset(identity_files, 0, sizeof(identity_files));
|
||||
memset(identity_keys, 0, sizeof(identity_keys));
|
||||
memset(certificate_files, 0, sizeof(certificate_files));
|
||||
memset(certificates, 0, sizeof(certificates));
|
||||
|
||||
#ifdef ENABLE_PKCS11
|
||||
if (options.pkcs11_provider != NULL &&
|
||||
|
@ -1990,6 +1999,7 @@ load_public_identity_files(void)
|
|||
if (n_ids >= SSH_MAX_IDENTITY_FILES ||
|
||||
strcasecmp(options.identity_files[i], "none") == 0) {
|
||||
free(options.identity_files[i]);
|
||||
options.identity_files[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
cp = tilde_expand_filename(options.identity_files[i],
|
||||
|
@ -2008,7 +2018,12 @@ load_public_identity_files(void)
|
|||
if (++n_ids >= SSH_MAX_IDENTITY_FILES)
|
||||
continue;
|
||||
|
||||
/* Try to add the certificate variant too */
|
||||
/*
|
||||
* If no certificates have been explicitly listed then try
|
||||
* to add the default certificate variant too.
|
||||
*/
|
||||
if (options.num_certificate_files != 0)
|
||||
continue;
|
||||
xasprintf(&cp, "%s-cert", filename);
|
||||
public = key_load_public(cp, NULL);
|
||||
debug("identity file %s type %d", cp,
|
||||
|
@ -2025,14 +2040,50 @@ load_public_identity_files(void)
|
|||
continue;
|
||||
}
|
||||
identity_keys[n_ids] = public;
|
||||
/* point to the original path, most likely the private key */
|
||||
identity_files[n_ids] = xstrdup(filename);
|
||||
identity_files[n_ids] = cp;
|
||||
n_ids++;
|
||||
}
|
||||
|
||||
if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES)
|
||||
fatal("%s: too many certificates", __func__);
|
||||
for (i = 0; i < options.num_certificate_files; i++) {
|
||||
cp = tilde_expand_filename(options.certificate_files[i],
|
||||
original_real_uid);
|
||||
filename = percent_expand(cp, "d", pwdir,
|
||||
"u", pwname, "l", thishost, "h", host,
|
||||
"r", options.user, (char *)NULL);
|
||||
free(cp);
|
||||
|
||||
public = key_load_public(filename, NULL);
|
||||
debug("certificate file %s type %d", filename,
|
||||
public ? public->type : -1);
|
||||
free(options.certificate_files[i]);
|
||||
options.certificate_files[i] = NULL;
|
||||
if (public == NULL) {
|
||||
free(filename);
|
||||
continue;
|
||||
}
|
||||
if (!key_is_cert(public)) {
|
||||
debug("%s: key %s type %s is not a certificate",
|
||||
__func__, filename, key_type(public));
|
||||
key_free(public);
|
||||
free(filename);
|
||||
continue;
|
||||
}
|
||||
certificate_files[n_certs] = filename;
|
||||
certificates[n_certs] = public;
|
||||
++n_certs;
|
||||
}
|
||||
|
||||
options.num_identity_files = n_ids;
|
||||
memcpy(options.identity_files, identity_files, sizeof(identity_files));
|
||||
memcpy(options.identity_keys, identity_keys, sizeof(identity_keys));
|
||||
|
||||
options.num_certificate_files = n_certs;
|
||||
memcpy(options.certificate_files,
|
||||
certificate_files, sizeof(certificate_files));
|
||||
memcpy(options.certificates, certificates, sizeof(certificates));
|
||||
|
||||
explicit_bzero(pwname, strlen(pwname));
|
||||
free(pwname);
|
||||
explicit_bzero(pwdir, strlen(pwdir));
|
||||
|
|
8
ssh.h
8
ssh.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh.h,v 1.81 2015/08/04 05:23:06 djm Exp $ */
|
||||
/* $OpenBSD: ssh.h,v 1.82 2015/09/24 06:15:11 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -18,6 +18,12 @@
|
|||
/* Default port number. */
|
||||
#define SSH_DEFAULT_PORT 22
|
||||
|
||||
/*
|
||||
* Maximum number of certificate files that can be specified
|
||||
* in configuration files or on the command line.
|
||||
*/
|
||||
#define SSH_MAX_CERTIFICATE_FILES 100
|
||||
|
||||
/*
|
||||
* Maximum number of RSA authentication identity files that can be specified
|
||||
* in configuration files or on the command line.
|
||||
|
|
54
ssh_config.5
54
ssh_config.5
|
@ -33,8 +33,8 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh_config.5,v 1.220 2015/09/22 08:33:23 sobrado Exp $
|
||||
.Dd $Mdocdate: September 22 2015 $
|
||||
.\" $OpenBSD: ssh_config.5,v 1.221 2015/09/24 06:15:11 djm Exp $
|
||||
.Dd $Mdocdate: September 24 2015 $
|
||||
.Dt SSH_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -325,6 +325,41 @@ to be canonicalized to names in the
|
|||
or
|
||||
.Dq *.c.example.com
|
||||
domains.
|
||||
.It Cm CertificateFile
|
||||
Specifies a file from which the user's certificate is read.
|
||||
A corresponding private key must be provided separately in order
|
||||
to use this certificate either
|
||||
from an
|
||||
.Cm IdentityFile
|
||||
directive or
|
||||
.Fl i
|
||||
flag to
|
||||
.Xr ssh 1 ,
|
||||
via
|
||||
.Xr ssh-agent 1 ,
|
||||
or via a
|
||||
.Cm PKCS11Provider .
|
||||
.Pp
|
||||
The file name may use the tilde
|
||||
syntax to refer to a user's home directory or one of the following
|
||||
escape characters:
|
||||
.Ql %d
|
||||
(local user's home directory),
|
||||
.Ql %u
|
||||
(local user name),
|
||||
.Ql %l
|
||||
(local host name),
|
||||
.Ql %h
|
||||
(remote host name) or
|
||||
.Ql %r
|
||||
(remote user name).
|
||||
.Pp
|
||||
It is possible to have multiple certificate files specified in
|
||||
configuration files; these certificates will be tried in sequence.
|
||||
Multiple
|
||||
.Cm CertificateFile
|
||||
directives will add to the list of certificates used for
|
||||
authentication.
|
||||
.It Cm ChallengeResponseAuthentication
|
||||
Specifies whether to use challenge-response authentication.
|
||||
The argument to this keyword must be
|
||||
|
@ -869,9 +904,13 @@ specifications).
|
|||
.It Cm IdentitiesOnly
|
||||
Specifies that
|
||||
.Xr ssh 1
|
||||
should only use the authentication identity files configured in the
|
||||
should only use the authentication identity and certificate files explicitly
|
||||
configured in the
|
||||
.Nm
|
||||
files,
|
||||
files
|
||||
or passed on the
|
||||
.Xr ssh 1
|
||||
command-line,
|
||||
even if
|
||||
.Xr ssh-agent 1
|
||||
or a
|
||||
|
@ -901,6 +940,8 @@ Additionally, any identities represented by the authentication agent
|
|||
will be used for authentication unless
|
||||
.Cm IdentitiesOnly
|
||||
is set.
|
||||
If no certificates have been explicitly specified by
|
||||
.Cm CertificateFile ,
|
||||
.Xr ssh 1
|
||||
will try to load certificate information from the filename obtained by
|
||||
appending
|
||||
|
@ -934,6 +975,11 @@ differs from that of other configuration directives).
|
|||
may be used in conjunction with
|
||||
.Cm IdentitiesOnly
|
||||
to select which identities in an agent are offered during authentication.
|
||||
.Cm IdentityFile
|
||||
may also be used in conjunction with
|
||||
.Cm CertificateFile
|
||||
in order to provide any certificate also needed for authentication with
|
||||
the identity.
|
||||
.It Cm IgnoreUnknown
|
||||
Specifies a pattern-list of unknown options to be ignored if they are
|
||||
encountered in configuration parsing.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sshconnect2.c,v 1.226 2015/07/30 00:01:34 djm Exp $ */
|
||||
/* $OpenBSD: sshconnect2.c,v 1.227 2015/09/24 06:15:11 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
|
@ -1001,18 +1001,17 @@ static int
|
|||
sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
|
||||
{
|
||||
Buffer b;
|
||||
Identity *private_id;
|
||||
u_char *blob, *signature;
|
||||
u_int bloblen;
|
||||
size_t slen;
|
||||
u_int skip = 0;
|
||||
int ret = -1;
|
||||
int have_sig = 1;
|
||||
u_int bloblen, skip = 0;
|
||||
int matched, ret = -1, have_sig = 1;
|
||||
char *fp;
|
||||
|
||||
if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
|
||||
SSH_FP_DEFAULT)) == NULL)
|
||||
return 0;
|
||||
debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp);
|
||||
debug3("%s: %s %s", __func__, key_type(id->key), fp);
|
||||
free(fp);
|
||||
|
||||
if (key_to_blob(id->key, &blob, &bloblen) == 0) {
|
||||
|
@ -1044,6 +1043,36 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
|
|||
}
|
||||
buffer_put_string(&b, blob, bloblen);
|
||||
|
||||
/*
|
||||
* If the key is an certificate, try to find a matching private key
|
||||
* and use it to complete the signature.
|
||||
* If no such private key exists, return failure and continue with
|
||||
* other methods of authentication.
|
||||
*/
|
||||
if (key_is_cert(id->key)) {
|
||||
matched = 0;
|
||||
TAILQ_FOREACH(private_id, &authctxt->keys, next) {
|
||||
if (sshkey_equal_public(id->key, private_id->key) &&
|
||||
id->key->type != private_id->key->type) {
|
||||
id = private_id;
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
debug2("%s: using private key \"%s\"%s for "
|
||||
"certificate", __func__, id->filename,
|
||||
id->agent_fd != -1 ? " from agent" : "");
|
||||
} else {
|
||||
/* XXX maybe verbose/error? */
|
||||
debug("%s: no private key for certificate "
|
||||
"\"%s\"", __func__, id->filename);
|
||||
free(blob);
|
||||
buffer_free(&b);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate signature */
|
||||
ret = identity_sign(id, &signature, &slen,
|
||||
buffer_ptr(&b), buffer_len(&b), datafellows);
|
||||
|
@ -1180,9 +1209,11 @@ load_identity_file(char *filename, int userprovided)
|
|||
|
||||
/*
|
||||
* try keys in the following order:
|
||||
* 1. agent keys that are found in the config file
|
||||
* 2. other agent keys
|
||||
* 3. keys that are only listed in the config file
|
||||
* 1. certificates listed in the config file
|
||||
* 2. other input certificates
|
||||
* 3. agent keys that are found in the config file
|
||||
* 4. other agent keys
|
||||
* 5. keys that are only listed in the config file
|
||||
*/
|
||||
static void
|
||||
pubkey_prepare(Authctxt *authctxt)
|
||||
|
@ -1236,6 +1267,18 @@ pubkey_prepare(Authctxt *authctxt)
|
|||
free(id);
|
||||
}
|
||||
}
|
||||
/* list of certificates specified by user */
|
||||
for (i = 0; i < options.num_certificate_files; i++) {
|
||||
key = options.certificates[i];
|
||||
if (!key_is_cert(key) || key->cert == NULL ||
|
||||
key->cert->type != SSH2_CERT_TYPE_USER)
|
||||
continue;
|
||||
id = xcalloc(1, sizeof(*id));
|
||||
id->key = key;
|
||||
id->filename = xstrdup(options.certificate_files[i]);
|
||||
id->userprovided = options.certificate_file_userprovided[i];
|
||||
TAILQ_INSERT_TAIL(preferred, id, next);
|
||||
}
|
||||
/* list of keys supported by the agent */
|
||||
if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
|
||||
if (r != SSH_ERR_AGENT_NOT_PRESENT)
|
||||
|
|
Loading…
Reference in New Issue