diff options
author | djm@openbsd.org <djm@openbsd.org> | 2017-03-11 23:40:26 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2017-03-12 10:50:19 +1100 |
commit | 1e24552716194db8f2f620587b876158a9ef56ad (patch) | |
tree | 595f0fb3e56020a561c18e8f09ef1001cec6439c /sshconnect2.c | |
parent | 0fb1a617a07b8df5de188dd5a0c8bf293d4bfc0e (diff) |
upstream commit
allow ssh to use certificates accompanied by a private
key file but no corresponding plain *.pub public key. bz#2617 based on patch
from Adam Eijdenberg; ok dtucker@ markus@
Upstream-ID: 295668dca2c39505281577217583ddd2bd4b00b9
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 67 |
1 files changed, 52 insertions, 15 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 2abb86679..f8a54beea 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect2.c,v 1.254 2017/02/03 02:56:00 dtucker Exp $ */ | 1 | /* $OpenBSD: sshconnect2.c,v 1.255 2017/03/11 23:40:26 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2008 Damien Miller. All rights reserved. | 4 | * Copyright (c) 2008 Damien Miller. All rights reserved. |
@@ -996,11 +996,11 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
996 | } | 996 | } |
997 | 997 | ||
998 | static const char * | 998 | static const char * |
999 | identity_sign_encode(struct identity *id) | 999 | key_sign_encode(const struct sshkey *key) |
1000 | { | 1000 | { |
1001 | struct ssh *ssh = active_state; | 1001 | struct ssh *ssh = active_state; |
1002 | 1002 | ||
1003 | if (id->key->type == KEY_RSA) { | 1003 | if (key->type == KEY_RSA) { |
1004 | switch (ssh->kex->rsa_sha2) { | 1004 | switch (ssh->kex->rsa_sha2) { |
1005 | case 256: | 1005 | case 256: |
1006 | return "rsa-sha2-256"; | 1006 | return "rsa-sha2-256"; |
@@ -1008,7 +1008,7 @@ identity_sign_encode(struct identity *id) | |||
1008 | return "rsa-sha2-512"; | 1008 | return "rsa-sha2-512"; |
1009 | } | 1009 | } |
1010 | } | 1010 | } |
1011 | return key_ssh_name(id->key); | 1011 | return key_ssh_name(key); |
1012 | } | 1012 | } |
1013 | 1013 | ||
1014 | static int | 1014 | static int |
@@ -1017,31 +1017,50 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1017 | { | 1017 | { |
1018 | Key *prv; | 1018 | Key *prv; |
1019 | int ret; | 1019 | int ret; |
1020 | const char *alg; | ||
1021 | |||
1022 | alg = identity_sign_encode(id); | ||
1023 | 1020 | ||
1024 | /* the agent supports this key */ | 1021 | /* the agent supports this key */ |
1025 | if (id->agent_fd != -1) | 1022 | if (id->key != NULL && id->agent_fd != -1) |
1026 | return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, | 1023 | return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, |
1027 | data, datalen, alg, compat); | 1024 | data, datalen, key_sign_encode(id->key), compat); |
1028 | 1025 | ||
1029 | /* | 1026 | /* |
1030 | * we have already loaded the private key or | 1027 | * we have already loaded the private key or |
1031 | * the private key is stored in external hardware | 1028 | * the private key is stored in external hardware |
1032 | */ | 1029 | */ |
1033 | if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) | 1030 | if (id->key != NULL && |
1034 | return (sshkey_sign(id->key, sigp, lenp, data, datalen, alg, | 1031 | (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) |
1035 | compat)); | 1032 | return (sshkey_sign(id->key, sigp, lenp, data, datalen, |
1033 | key_sign_encode(id->key), compat)); | ||
1034 | |||
1036 | /* load the private key from the file */ | 1035 | /* load the private key from the file */ |
1037 | if ((prv = load_identity_file(id)) == NULL) | 1036 | if ((prv = load_identity_file(id)) == NULL) |
1038 | return SSH_ERR_KEY_NOT_FOUND; | 1037 | return SSH_ERR_KEY_NOT_FOUND; |
1039 | ret = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat); | 1038 | ret = sshkey_sign(prv, sigp, lenp, data, datalen, |
1039 | key_sign_encode(prv), compat); | ||
1040 | sshkey_free(prv); | 1040 | sshkey_free(prv); |
1041 | return (ret); | 1041 | return (ret); |
1042 | } | 1042 | } |
1043 | 1043 | ||
1044 | static int | 1044 | static int |
1045 | id_filename_matches(Identity *id, Identity *private_id) | ||
1046 | { | ||
1047 | const char *suffixes[] = { ".pub", "-cert.pub", NULL }; | ||
1048 | size_t len = strlen(id->filename), plen = strlen(private_id->filename); | ||
1049 | size_t i, slen; | ||
1050 | |||
1051 | if (strcmp(id->filename, private_id->filename) == 0) | ||
1052 | return 1; | ||
1053 | for (i = 0; suffixes[i]; i++) { | ||
1054 | slen = strlen(suffixes[i]); | ||
1055 | if (len > slen && plen == len - slen && | ||
1056 | strcmp(id->filename + (len - slen), suffixes[i]) == 0 && | ||
1057 | memcmp(id->filename, private_id->filename, plen) == 0) | ||
1058 | return 1; | ||
1059 | } | ||
1060 | return 0; | ||
1061 | } | ||
1062 | |||
1063 | static int | ||
1045 | sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | 1064 | sign_and_send_pubkey(Authctxt *authctxt, Identity *id) |
1046 | { | 1065 | { |
1047 | Buffer b; | 1066 | Buffer b; |
@@ -1083,7 +1102,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
1083 | } else { | 1102 | } else { |
1084 | buffer_put_cstring(&b, authctxt->method->name); | 1103 | buffer_put_cstring(&b, authctxt->method->name); |
1085 | buffer_put_char(&b, have_sig); | 1104 | buffer_put_char(&b, have_sig); |
1086 | buffer_put_cstring(&b, identity_sign_encode(id)); | 1105 | buffer_put_cstring(&b, key_sign_encode(id->key)); |
1087 | } | 1106 | } |
1088 | buffer_put_string(&b, blob, bloblen); | 1107 | buffer_put_string(&b, blob, bloblen); |
1089 | 1108 | ||
@@ -1103,6 +1122,24 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
1103 | break; | 1122 | break; |
1104 | } | 1123 | } |
1105 | } | 1124 | } |
1125 | /* | ||
1126 | * Exact key matches are preferred, but also allow | ||
1127 | * filename matches for non-PKCS#11/agent keys that | ||
1128 | * didn't load public keys. This supports the case | ||
1129 | * of keeping just a private key file and public | ||
1130 | * certificate on disk. | ||
1131 | */ | ||
1132 | if (!matched && !id->isprivate && id->agent_fd == -1 && | ||
1133 | (id->key->flags & SSHKEY_FLAG_EXT) == 0) { | ||
1134 | TAILQ_FOREACH(private_id, &authctxt->keys, next) { | ||
1135 | if (private_id->key == NULL && | ||
1136 | id_filename_matches(id, private_id)) { | ||
1137 | id = private_id; | ||
1138 | matched = 1; | ||
1139 | break; | ||
1140 | } | ||
1141 | } | ||
1142 | } | ||
1106 | if (matched) { | 1143 | if (matched) { |
1107 | debug2("%s: using private key \"%s\"%s for " | 1144 | debug2("%s: using private key \"%s\"%s for " |
1108 | "certificate", __func__, id->filename, | 1145 | "certificate", __func__, id->filename, |
@@ -1181,7 +1218,7 @@ send_pubkey_test(Authctxt *authctxt, Identity *id) | |||
1181 | packet_put_cstring(authctxt->method->name); | 1218 | packet_put_cstring(authctxt->method->name); |
1182 | packet_put_char(have_sig); | 1219 | packet_put_char(have_sig); |
1183 | if (!(datafellows & SSH_BUG_PKAUTH)) | 1220 | if (!(datafellows & SSH_BUG_PKAUTH)) |
1184 | packet_put_cstring(identity_sign_encode(id)); | 1221 | packet_put_cstring(key_sign_encode(id->key)); |
1185 | packet_put_string(blob, bloblen); | 1222 | packet_put_string(blob, bloblen); |
1186 | free(blob); | 1223 | free(blob); |
1187 | packet_send(); | 1224 | packet_send(); |