Bugzilla – Attachment 2700 Details for
Bug 2436
Add ssh option to present certificates on command line
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
revised patch
bz2436.diff (text/plain), 15.57 KB, created by
Damien Miller
on 2015-09-11 17:33:26 AEST
(
hide
)
Description:
revised patch
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2015-09-11 17:33:26 AEST
Size:
15.57 KB
patch
obsolete
>diff --git a/readconf.c b/readconf.c >index 372a328..935f0f7 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -124,6 +124,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, >@@ -191,6 +192,7 @@ static struct { > { "identityfile", oIdentityFile }, > { "identityfile2", oIdentityFile }, /* obsolete */ > { "identitiesonly", oIdentitiesOnly }, >+ { "certificatefile", oCertificateFile }, > { "hostname", oHostName }, > { "hostkeyalias", oHostKeyAlias }, > { "proxycommand", oProxyCommand }, >@@ -354,6 +356,30 @@ clear_forwardings(Options *options) > } > > 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) > { >@@ -969,6 +995,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; >@@ -1613,6 +1657,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; >diff --git a/readconf.h b/readconf.h >index bb2d552..e5b5936 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -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 */ >diff --git a/ssh.1 b/ssh.1 >index 495b787..5889112 100644 >--- a/ssh.1 >+++ b/ssh.1 >@@ -63,6 +63,7 @@ > .Op Fl S Ar ctl_path > .Op Fl W Ar host : Ns Ar port > .Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun >+.Op Fl z Ar certificate_file > .Oo Ar user Ns @ Oc Ns Ar hostname > .Op Ar command > .Ek >@@ -304,6 +305,11 @@ 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 >+or the >+.Fl z >+flag, > .Nm > will also try to load certificate information from the filename obtained > by appending >@@ -468,6 +474,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 >@@ -772,6 +779,27 @@ Send log information using the > .Xr syslog 3 > system module. > By default this information is sent to stderr. >+.Pp >+.It Fl z Ar certificate_file >+Selects a file from which certificate information is loaded for public >+key authentication. >+For the certificate to be usable, the private key corresponding to >+.Ar certificate_file >+must also be available, whether via >+.Xr ssh_agent 1 , >+a >+.Cm PKCS11Provider , >+or through an >+.Cm IdentityFile >+specified on the command line or in configuration files. >+Certificate files may also be specified on a per-host basis in >+the configuration file using the >+.Cm CertificateFile >+option. >+It is possible to have multiple >+.Fl z >+options (and multiple certificates specified in >+configuration files). > .El > .Pp > .Nm >diff --git a/ssh.c b/ssh.c >index 6d5c50a..3b46b23 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -193,7 +193,8 @@ usage(void) > " [-O ctl_cmd] [-o option] [-p port]\n" > " [-Q cipher | cipher-auth | mac | kex | key]\n" > " [-R address] [-S ctl_path] [-W host:port]\n" >-" [-w local_tun[:remote_tun]] [user@]hostname [command]\n" >+" [-w local_tun[:remote_tun]] [-z certificate_file]\n" >+" [user@]hostname [command]\n" > ); > exit(255); > } >@@ -565,7 +566,7 @@ main(int ac, char **av) > > again: > while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" >- "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { >+ "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYyz:")) != -1) { > switch (opt) { > case '1': > options.protocol = SSH_PROTO_1; >@@ -879,6 +880,9 @@ main(int ac, char **av) > case 'F': > config = optarg; > break; >+ case 'z': >+ add_certificate_file(&options, optarg, 1); >+ break; > default: > usage(); > } >@@ -1306,6 +1310,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(); >@@ -1892,25 +1900,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 && >@@ -1942,6 +1955,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], >@@ -1960,7 +1974,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, >@@ -1977,14 +1996,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)); >diff --git a/ssh.h b/ssh.h >index 0a3c59b..c57d430 100644 >--- a/ssh.h >+++ b/ssh.h >@@ -19,6 +19,12 @@ > #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. > */ >diff --git a/ssh_config.5 b/ssh_config.5 >index a4c8521..b2e944c 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -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 >@@ -868,9 +903,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 >@@ -900,6 +939,12 @@ 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 >+or the >+.Xr ssh 1 >+.Fl z >+flag, > .Xr ssh 1 > will try to load certificate information from the filename obtained by > appending >@@ -933,6 +978,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. >diff --git a/sshconnect2.c b/sshconnect2.c >index 2b525ac..6a2b0db 100644 >--- a/sshconnect2.c >+++ b/sshconnect2.c >@@ -993,18 +993,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) { >@@ -1036,6 +1035,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); >@@ -1172,9 +1201,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) >@@ -1228,6 +1259,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)
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2436
:
2679
|
2694
| 2700