diff options
Diffstat (limited to 'ssh-pkcs11-client.c')
-rw-r--r-- | ssh-pkcs11-client.c | 167 |
1 files changed, 141 insertions, 26 deletions
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c index d1241ce67..e7860de89 100644 --- a/ssh-pkcs11-client.c +++ b/ssh-pkcs11-client.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* $OpenBSD: ssh-pkcs11-client.c,v 1.10 2018/07/09 21:59:10 markus Exp $ */ | 1 | /* $OpenBSD: ssh-pkcs11-client.c,v 1.15 2019/01/21 12:53:35 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2014 Pedro Martelletto. All rights reserved. | ||
4 | * | 5 | * |
5 | * Permission to use, copy, modify, and distribute this software for any | 6 | * Permission to use, copy, modify, and distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above | 7 | * purpose with or without fee is hereby granted, provided that the above |
@@ -30,6 +31,7 @@ | |||
30 | #include <unistd.h> | 31 | #include <unistd.h> |
31 | #include <errno.h> | 32 | #include <errno.h> |
32 | 33 | ||
34 | #include <openssl/ecdsa.h> | ||
33 | #include <openssl/rsa.h> | 35 | #include <openssl/rsa.h> |
34 | 36 | ||
35 | #include "openbsd-compat/openssl-compat.h" | 37 | #include "openbsd-compat/openssl-compat.h" |
@@ -47,8 +49,8 @@ | |||
47 | 49 | ||
48 | /* borrows code from sftp-server and ssh-agent */ | 50 | /* borrows code from sftp-server and ssh-agent */ |
49 | 51 | ||
50 | int fd = -1; | 52 | static int fd = -1; |
51 | pid_t pid = -1; | 53 | static pid_t pid = -1; |
52 | 54 | ||
53 | static void | 55 | static void |
54 | send_msg(struct sshbuf *m) | 56 | send_msg(struct sshbuf *m) |
@@ -113,22 +115,27 @@ pkcs11_terminate(void) | |||
113 | } | 115 | } |
114 | 116 | ||
115 | static int | 117 | static int |
116 | pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | 118 | rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding) |
117 | int padding) | ||
118 | { | 119 | { |
119 | struct sshkey key; /* XXX */ | 120 | struct sshkey *key = NULL; |
120 | u_char *blob, *signature = NULL; | 121 | struct sshbuf *msg = NULL; |
122 | u_char *blob = NULL, *signature = NULL; | ||
121 | size_t blen, slen = 0; | 123 | size_t blen, slen = 0; |
122 | int r, ret = -1; | 124 | int r, ret = -1; |
123 | struct sshbuf *msg; | ||
124 | 125 | ||
125 | if (padding != RSA_PKCS1_PADDING) | 126 | if (padding != RSA_PKCS1_PADDING) |
126 | return (-1); | 127 | goto fail; |
127 | key.type = KEY_RSA; | 128 | key = sshkey_new(KEY_UNSPEC); |
128 | key.rsa = rsa; | 129 | if (key == NULL) { |
129 | if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { | 130 | error("%s: sshkey_new failed", __func__); |
131 | goto fail; | ||
132 | } | ||
133 | key->type = KEY_RSA; | ||
134 | RSA_up_ref(rsa); | ||
135 | key->rsa = rsa; | ||
136 | if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { | ||
130 | error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); | 137 | error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); |
131 | return -1; | 138 | goto fail; |
132 | } | 139 | } |
133 | if ((msg = sshbuf_new()) == NULL) | 140 | if ((msg = sshbuf_new()) == NULL) |
134 | fatal("%s: sshbuf_new failed", __func__); | 141 | fatal("%s: sshbuf_new failed", __func__); |
@@ -137,7 +144,6 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | |||
137 | (r = sshbuf_put_string(msg, from, flen)) != 0 || | 144 | (r = sshbuf_put_string(msg, from, flen)) != 0 || |
138 | (r = sshbuf_put_u32(msg, 0)) != 0) | 145 | (r = sshbuf_put_u32(msg, 0)) != 0) |
139 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 146 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
140 | free(blob); | ||
141 | send_msg(msg); | 147 | send_msg(msg); |
142 | sshbuf_reset(msg); | 148 | sshbuf_reset(msg); |
143 | 149 | ||
@@ -150,22 +156,115 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | |||
150 | } | 156 | } |
151 | free(signature); | 157 | free(signature); |
152 | } | 158 | } |
159 | fail: | ||
160 | free(blob); | ||
161 | sshkey_free(key); | ||
162 | sshbuf_free(msg); | ||
163 | return (ret); | ||
164 | } | ||
165 | |||
166 | #ifdef HAVE_EC_KEY_METHOD_NEW | ||
167 | static ECDSA_SIG * | ||
168 | ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, | ||
169 | const BIGNUM *rp, EC_KEY *ec) | ||
170 | { | ||
171 | struct sshkey *key = NULL; | ||
172 | struct sshbuf *msg = NULL; | ||
173 | ECDSA_SIG *ret = NULL; | ||
174 | const u_char *cp; | ||
175 | u_char *blob = NULL, *signature = NULL; | ||
176 | size_t blen, slen = 0; | ||
177 | int r, nid; | ||
178 | |||
179 | nid = sshkey_ecdsa_key_to_nid(ec); | ||
180 | if (nid < 0) { | ||
181 | error("%s: couldn't get curve nid", __func__); | ||
182 | goto fail; | ||
183 | } | ||
184 | |||
185 | key = sshkey_new(KEY_UNSPEC); | ||
186 | if (key == NULL) { | ||
187 | error("%s: sshkey_new failed", __func__); | ||
188 | goto fail; | ||
189 | } | ||
190 | key->ecdsa = ec; | ||
191 | key->ecdsa_nid = nid; | ||
192 | key->type = KEY_ECDSA; | ||
193 | EC_KEY_up_ref(ec); | ||
194 | |||
195 | if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { | ||
196 | error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); | ||
197 | goto fail; | ||
198 | } | ||
199 | if ((msg = sshbuf_new()) == NULL) | ||
200 | fatal("%s: sshbuf_new failed", __func__); | ||
201 | if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || | ||
202 | (r = sshbuf_put_string(msg, blob, blen)) != 0 || | ||
203 | (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 || | ||
204 | (r = sshbuf_put_u32(msg, 0)) != 0) | ||
205 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
206 | send_msg(msg); | ||
207 | sshbuf_reset(msg); | ||
208 | |||
209 | if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { | ||
210 | if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) | ||
211 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
212 | cp = signature; | ||
213 | ret = d2i_ECDSA_SIG(NULL, &cp, slen); | ||
214 | free(signature); | ||
215 | } | ||
216 | |||
217 | fail: | ||
218 | free(blob); | ||
219 | sshkey_free(key); | ||
153 | sshbuf_free(msg); | 220 | sshbuf_free(msg); |
154 | return (ret); | 221 | return (ret); |
155 | } | 222 | } |
223 | #endif /* HAVE_EC_KEY_METHOD_NEW */ | ||
224 | |||
225 | static RSA_METHOD *helper_rsa; | ||
226 | #ifdef HAVE_EC_KEY_METHOD_NEW | ||
227 | static EC_KEY_METHOD *helper_ecdsa; | ||
228 | #endif /* HAVE_EC_KEY_METHOD_NEW */ | ||
229 | |||
230 | /* redirect private key crypto operations to the ssh-pkcs11-helper */ | ||
231 | static void | ||
232 | wrap_key(struct sshkey *k) | ||
233 | { | ||
234 | if (k->type == KEY_RSA) | ||
235 | RSA_set_method(k->rsa, helper_rsa); | ||
236 | #ifdef HAVE_EC_KEY_METHOD_NEW | ||
237 | else if (k->type == KEY_ECDSA) | ||
238 | EC_KEY_set_method(k->ecdsa, helper_ecdsa); | ||
239 | #endif /* HAVE_EC_KEY_METHOD_NEW */ | ||
240 | else | ||
241 | fatal("%s: unknown key type", __func__); | ||
242 | } | ||
156 | 243 | ||
157 | /* redirect the private key encrypt operation to the ssh-pkcs11-helper */ | ||
158 | static int | 244 | static int |
159 | wrap_key(RSA *rsa) | 245 | pkcs11_start_helper_methods(void) |
160 | { | 246 | { |
161 | static RSA_METHOD *helper_rsa; | 247 | if (helper_rsa != NULL) |
248 | return (0); | ||
249 | |||
250 | #ifdef HAVE_EC_KEY_METHOD_NEW | ||
251 | int (*orig_sign)(int, const unsigned char *, int, unsigned char *, | ||
252 | unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; | ||
253 | if (helper_ecdsa != NULL) | ||
254 | return (0); | ||
255 | helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); | ||
256 | if (helper_ecdsa == NULL) | ||
257 | return (-1); | ||
258 | EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL); | ||
259 | EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign); | ||
260 | #endif /* HAVE_EC_KEY_METHOD_NEW */ | ||
162 | 261 | ||
163 | if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) | 262 | if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) |
164 | fatal("%s: RSA_meth_dup failed", __func__); | 263 | fatal("%s: RSA_meth_dup failed", __func__); |
165 | if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || | 264 | if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || |
166 | !RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt)) | 265 | !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt)) |
167 | fatal("%s: failed to prepare method", __func__); | 266 | fatal("%s: failed to prepare method", __func__); |
168 | RSA_set_method(rsa, helper_rsa); | 267 | |
169 | return (0); | 268 | return (0); |
170 | } | 269 | } |
171 | 270 | ||
@@ -173,6 +272,15 @@ static int | |||
173 | pkcs11_start_helper(void) | 272 | pkcs11_start_helper(void) |
174 | { | 273 | { |
175 | int pair[2]; | 274 | int pair[2]; |
275 | char *helper, *verbosity = NULL; | ||
276 | |||
277 | if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) | ||
278 | verbosity = "-vvv"; | ||
279 | |||
280 | if (pkcs11_start_helper_methods() == -1) { | ||
281 | error("pkcs11_start_helper_methods failed"); | ||
282 | return (-1); | ||
283 | } | ||
176 | 284 | ||
177 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { | 285 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { |
178 | error("socketpair: %s", strerror(errno)); | 286 | error("socketpair: %s", strerror(errno)); |
@@ -189,10 +297,13 @@ pkcs11_start_helper(void) | |||
189 | } | 297 | } |
190 | close(pair[0]); | 298 | close(pair[0]); |
191 | close(pair[1]); | 299 | close(pair[1]); |
192 | execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER, | 300 | helper = getenv("SSH_PKCS11_HELPER"); |
193 | (char *)NULL); | 301 | if (helper == NULL || strlen(helper) == 0) |
194 | fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER, | 302 | helper = _PATH_SSH_PKCS11_HELPER; |
195 | strerror(errno)); | 303 | debug("%s: starting %s %s", __func__, helper, |
304 | verbosity == NULL ? "" : verbosity); | ||
305 | execlp(helper, helper, verbosity, (char *)NULL); | ||
306 | fprintf(stderr, "exec: %s: %s\n", helper, strerror(errno)); | ||
196 | _exit(1); | 307 | _exit(1); |
197 | } | 308 | } |
198 | close(pair[1]); | 309 | close(pair[1]); |
@@ -204,7 +315,7 @@ int | |||
204 | pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | 315 | pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) |
205 | { | 316 | { |
206 | struct sshkey *k; | 317 | struct sshkey *k; |
207 | int r; | 318 | int r, type; |
208 | u_char *blob; | 319 | u_char *blob; |
209 | size_t blen; | 320 | size_t blen; |
210 | u_int nkeys, i; | 321 | u_int nkeys, i; |
@@ -222,7 +333,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | |||
222 | send_msg(msg); | 333 | send_msg(msg); |
223 | sshbuf_reset(msg); | 334 | sshbuf_reset(msg); |
224 | 335 | ||
225 | if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) { | 336 | type = recv_msg(msg); |
337 | if (type == SSH2_AGENT_IDENTITIES_ANSWER) { | ||
226 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) | 338 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) |
227 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 339 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
228 | *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); | 340 | *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); |
@@ -234,10 +346,13 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | |||
234 | __func__, ssh_err(r)); | 346 | __func__, ssh_err(r)); |
235 | if ((r = sshkey_from_blob(blob, blen, &k)) != 0) | 347 | if ((r = sshkey_from_blob(blob, blen, &k)) != 0) |
236 | fatal("%s: bad key: %s", __func__, ssh_err(r)); | 348 | fatal("%s: bad key: %s", __func__, ssh_err(r)); |
237 | wrap_key(k->rsa); | 349 | wrap_key(k); |
238 | (*keysp)[i] = k; | 350 | (*keysp)[i] = k; |
239 | free(blob); | 351 | free(blob); |
240 | } | 352 | } |
353 | } else if (type == SSH2_AGENT_FAILURE) { | ||
354 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) | ||
355 | nkeys = -1; | ||
241 | } else { | 356 | } else { |
242 | nkeys = -1; | 357 | nkeys = -1; |
243 | } | 358 | } |