-/* $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
oPasswordAuthentication, oRSAAuthentication,
oChallengeResponseAuthentication, oXAuthLocation,
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
+ oCertificateFile,
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
{ "identityfile", oIdentityFile },
{ "identityfile2", oIdentityFile }, /* obsolete */
{ "identitiesonly", oIdentitiesOnly },
+ { "certificatefile", oCertificateFile },
{ "hostname", oHostName },
{ "hostkeyalias", oHostKeyAlias },
{ "proxycommand", oProxyCommand },
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)
}
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;
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;
-/* $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>
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;
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 */
.\" (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
.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
.It CanonicalizeHostname
.It CanonicalizeMaxDots
.It CanonicalizePermittedCNAMEs
+.It CertificateFile
.It ChallengeResponseAuthentication
.It CheckHostIP
.It Cipher
-/* $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
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();
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 &&
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],
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,
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));
-/* $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>
/* 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.
.\" (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
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
.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
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
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.
-/* $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.
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) {
}
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);
/*
* 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)
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)