diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-01-20 22:51:37 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-01-21 10:54:37 +1100 |
commit | 93f02107f44d63a016d8c23ebd2ca9205c495c48 (patch) | |
tree | 1d8d6ca8e146c9bd325614f33a59adf7199b40c9 | |
parent | aa22c20e0c36c2fc610cfcc793b0d14079c38814 (diff) |
upstream: add support for ECDSA keys in PKCS#11 tokens
Work by markus@ and Pedro Martelletto, feedback and ok me@
OpenBSD-Commit-ID: a37d651e221341376636056512bddfc16efb4424
-rw-r--r-- | ssh-pkcs11-client.c | 103 | ||||
-rw-r--r-- | ssh-pkcs11-helper.c | 40 | ||||
-rw-r--r-- | ssh-pkcs11.c | 1374 | ||||
-rw-r--r-- | ssh-pkcs11.h | 18 | ||||
-rw-r--r-- | sshkey.h | 3 |
5 files changed, 1302 insertions, 236 deletions
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c index d1241ce67..6e16b2f9a 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.12 2019/01/20 22:51:37 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" |
@@ -113,8 +115,7 @@ 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; /* XXX */ |
120 | u_char *blob, *signature = NULL; | 121 | u_char *blob, *signature = NULL; |
@@ -154,18 +155,89 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | |||
154 | return (ret); | 155 | return (ret); |
155 | } | 156 | } |
156 | 157 | ||
157 | /* redirect the private key encrypt operation to the ssh-pkcs11-helper */ | 158 | static ECDSA_SIG * |
159 | ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, | ||
160 | const BIGNUM *rp, EC_KEY *ec) | ||
161 | { | ||
162 | struct sshkey key; /* XXX */ | ||
163 | u_char *blob, *signature = NULL; | ||
164 | const u_char *cp; | ||
165 | size_t blen, slen = 0; | ||
166 | ECDSA_SIG *ret = NULL; | ||
167 | struct sshbuf *msg; | ||
168 | int r; | ||
169 | |||
170 | key.type = KEY_ECDSA; | ||
171 | key.ecdsa = ec; | ||
172 | key.ecdsa_nid = sshkey_ecdsa_key_to_nid(ec); | ||
173 | if (key.ecdsa_nid < 0) { | ||
174 | error("%s: couldn't get curve nid", __func__); | ||
175 | return (NULL); | ||
176 | } | ||
177 | if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { | ||
178 | error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); | ||
179 | return (NULL); | ||
180 | } | ||
181 | if ((msg = sshbuf_new()) == NULL) | ||
182 | fatal("%s: sshbuf_new failed", __func__); | ||
183 | if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || | ||
184 | (r = sshbuf_put_string(msg, blob, blen)) != 0 || | ||
185 | (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 || | ||
186 | (r = sshbuf_put_u32(msg, 0)) != 0) | ||
187 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
188 | free(blob); | ||
189 | send_msg(msg); | ||
190 | sshbuf_reset(msg); | ||
191 | |||
192 | if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { | ||
193 | if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) | ||
194 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
195 | cp = signature; | ||
196 | ret = d2i_ECDSA_SIG(NULL, &cp, slen); | ||
197 | free(signature); | ||
198 | } | ||
199 | |||
200 | sshbuf_free(msg); | ||
201 | return (ret); | ||
202 | } | ||
203 | |||
204 | static RSA_METHOD *helper_rsa; | ||
205 | static EC_KEY_METHOD *helper_ecdsa; | ||
206 | |||
207 | /* redirect private key crypto operations to the ssh-pkcs11-helper */ | ||
208 | static void | ||
209 | wrap_key(struct sshkey *k) | ||
210 | { | ||
211 | if (k->type == KEY_RSA) | ||
212 | RSA_set_method(k->rsa, helper_rsa); | ||
213 | else if (k->type == KEY_ECDSA) | ||
214 | EC_KEY_set_method(k->ecdsa, helper_ecdsa); | ||
215 | else | ||
216 | fatal("%s: unknown key type", __func__); | ||
217 | } | ||
218 | |||
158 | static int | 219 | static int |
159 | wrap_key(RSA *rsa) | 220 | pkcs11_start_helper_methods(void) |
160 | { | 221 | { |
161 | static RSA_METHOD *helper_rsa; | 222 | if (helper_ecdsa != NULL) |
223 | return (0); | ||
224 | |||
225 | int (*orig_sign)(int, const unsigned char *, int, unsigned char *, | ||
226 | unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; | ||
227 | if (helper_ecdsa != NULL) | ||
228 | return (0); | ||
229 | helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); | ||
230 | if (helper_ecdsa == NULL) | ||
231 | return (-1); | ||
232 | EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL); | ||
233 | EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign); | ||
162 | 234 | ||
163 | if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) | 235 | if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) |
164 | fatal("%s: RSA_meth_dup failed", __func__); | 236 | fatal("%s: RSA_meth_dup failed", __func__); |
165 | if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || | 237 | if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || |
166 | !RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt)) | 238 | !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt)) |
167 | fatal("%s: failed to prepare method", __func__); | 239 | fatal("%s: failed to prepare method", __func__); |
168 | RSA_set_method(rsa, helper_rsa); | 240 | |
169 | return (0); | 241 | return (0); |
170 | } | 242 | } |
171 | 243 | ||
@@ -174,6 +246,11 @@ pkcs11_start_helper(void) | |||
174 | { | 246 | { |
175 | int pair[2]; | 247 | int pair[2]; |
176 | 248 | ||
249 | if (pkcs11_start_helper_methods() == -1) { | ||
250 | error("pkcs11_start_helper_methods failed"); | ||
251 | return (-1); | ||
252 | } | ||
253 | |||
177 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { | 254 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { |
178 | error("socketpair: %s", strerror(errno)); | 255 | error("socketpair: %s", strerror(errno)); |
179 | return (-1); | 256 | return (-1); |
@@ -204,7 +281,7 @@ int | |||
204 | pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | 281 | pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) |
205 | { | 282 | { |
206 | struct sshkey *k; | 283 | struct sshkey *k; |
207 | int r; | 284 | int r, type; |
208 | u_char *blob; | 285 | u_char *blob; |
209 | size_t blen; | 286 | size_t blen; |
210 | u_int nkeys, i; | 287 | u_int nkeys, i; |
@@ -222,7 +299,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | |||
222 | send_msg(msg); | 299 | send_msg(msg); |
223 | sshbuf_reset(msg); | 300 | sshbuf_reset(msg); |
224 | 301 | ||
225 | if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) { | 302 | type = recv_msg(msg); |
303 | if (type == SSH2_AGENT_IDENTITIES_ANSWER) { | ||
226 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) | 304 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) |
227 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 305 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
228 | *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); | 306 | *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); |
@@ -234,10 +312,13 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) | |||
234 | __func__, ssh_err(r)); | 312 | __func__, ssh_err(r)); |
235 | if ((r = sshkey_from_blob(blob, blen, &k)) != 0) | 313 | if ((r = sshkey_from_blob(blob, blen, &k)) != 0) |
236 | fatal("%s: bad key: %s", __func__, ssh_err(r)); | 314 | fatal("%s: bad key: %s", __func__, ssh_err(r)); |
237 | wrap_key(k->rsa); | 315 | wrap_key(k); |
238 | (*keysp)[i] = k; | 316 | (*keysp)[i] = k; |
239 | free(blob); | 317 | free(blob); |
240 | } | 318 | } |
319 | } else if (type == SSH2_AGENT_FAILURE) { | ||
320 | if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) | ||
321 | nkeys = -1; | ||
241 | } else { | 322 | } else { |
242 | nkeys = -1; | 323 | nkeys = -1; |
243 | } | 324 | } |
diff --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c index 6301033c5..92c6728ba 100644 --- a/ssh-pkcs11-helper.c +++ b/ssh-pkcs11-helper.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-pkcs11-helper.c,v 1.14 2018/01/08 15:18:46 markus Exp $ */ | 1 | /* $OpenBSD: ssh-pkcs11-helper.c,v 1.15 2019/01/20 22:51:37 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -110,7 +110,7 @@ static void | |||
110 | process_add(void) | 110 | process_add(void) |
111 | { | 111 | { |
112 | char *name, *pin; | 112 | char *name, *pin; |
113 | struct sshkey **keys; | 113 | struct sshkey **keys = NULL; |
114 | int r, i, nkeys; | 114 | int r, i, nkeys; |
115 | u_char *blob; | 115 | u_char *blob; |
116 | size_t blen; | 116 | size_t blen; |
@@ -139,11 +139,13 @@ process_add(void) | |||
139 | free(blob); | 139 | free(blob); |
140 | add_key(keys[i], name); | 140 | add_key(keys[i], name); |
141 | } | 141 | } |
142 | free(keys); | ||
143 | } else { | 142 | } else { |
144 | if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) | 143 | if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) |
145 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 144 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
145 | if ((r = sshbuf_put_u32(msg, -nkeys)) != 0) | ||
146 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
146 | } | 147 | } |
148 | free(keys); | ||
147 | free(pin); | 149 | free(pin); |
148 | free(name); | 150 | free(name); |
149 | send_msg(msg); | 151 | send_msg(msg); |
@@ -192,15 +194,33 @@ process_sign(void) | |||
192 | else { | 194 | else { |
193 | if ((found = lookup_key(key)) != NULL) { | 195 | if ((found = lookup_key(key)) != NULL) { |
194 | #ifdef WITH_OPENSSL | 196 | #ifdef WITH_OPENSSL |
197 | u_int xslen; | ||
195 | int ret; | 198 | int ret; |
196 | 199 | ||
197 | slen = RSA_size(key->rsa); | 200 | if (key->type == KEY_RSA) { |
198 | signature = xmalloc(slen); | 201 | slen = RSA_size(key->rsa); |
199 | if ((ret = RSA_private_encrypt(dlen, data, signature, | 202 | signature = xmalloc(slen); |
200 | found->rsa, RSA_PKCS1_PADDING)) != -1) { | 203 | ret = RSA_private_encrypt(dlen, data, signature, |
201 | slen = ret; | 204 | found->rsa, RSA_PKCS1_PADDING); |
202 | ok = 0; | 205 | if (ret != -1) { |
203 | } | 206 | slen = ret; |
207 | ok = 0; | ||
208 | } | ||
209 | } else if (key->type == KEY_ECDSA) { | ||
210 | xslen = ECDSA_size(key->ecdsa); | ||
211 | signature = xmalloc(xslen); | ||
212 | /* "The parameter type is ignored." */ | ||
213 | ret = ECDSA_sign(-1, data, dlen, signature, | ||
214 | &xslen, found->ecdsa); | ||
215 | if (ret != 0) | ||
216 | ok = 0; | ||
217 | else | ||
218 | error("%s: ECDSA_sign" | ||
219 | " returns %d", __func__, ret); | ||
220 | slen = xslen; | ||
221 | } else | ||
222 | error("%s: don't know how to sign with key " | ||
223 | "type %d", __func__, (int)key->type); | ||
204 | #endif /* WITH_OPENSSL */ | 224 | #endif /* WITH_OPENSSL */ |
205 | } | 225 | } |
206 | sshkey_free(key); | 226 | sshkey_free(key); |
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c index 775de9642..01f968a9b 100644 --- a/ssh-pkcs11.c +++ b/ssh-pkcs11.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* $OpenBSD: ssh-pkcs11.c,v 1.26 2018/02/07 02:06:51 jsing Exp $ */ | 1 | /* $OpenBSD: ssh-pkcs11.c,v 1.28 2019/01/20 22:51:37 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 |
@@ -19,20 +20,24 @@ | |||
19 | 20 | ||
20 | #ifdef ENABLE_PKCS11 | 21 | #ifdef ENABLE_PKCS11 |
21 | 22 | ||
22 | #include <sys/types.h> | ||
23 | #ifdef HAVE_SYS_TIME_H | 23 | #ifdef HAVE_SYS_TIME_H |
24 | # include <sys/time.h> | 24 | # include <sys/time.h> |
25 | #endif | 25 | #endif |
26 | |||
27 | #include <sys/types.h> | ||
26 | #include <stdarg.h> | 28 | #include <stdarg.h> |
27 | #include <stdio.h> | 29 | #include <stdio.h> |
28 | 30 | ||
31 | #include <ctype.h> | ||
29 | #include <string.h> | 32 | #include <string.h> |
30 | #include <dlfcn.h> | 33 | #include <dlfcn.h> |
31 | 34 | ||
32 | #include "openbsd-compat/sys-queue.h" | 35 | #include "openbsd-compat/sys-queue.h" |
33 | #include "openbsd-compat/openssl-compat.h" | 36 | #include "openbsd-compat/openssl-compat.h" |
34 | 37 | ||
38 | #include <openssl/ecdsa.h> | ||
35 | #include <openssl/x509.h> | 39 | #include <openssl/x509.h> |
40 | #include <openssl/err.h> | ||
36 | 41 | ||
37 | #define CRYPTOKI_COMPAT | 42 | #define CRYPTOKI_COMPAT |
38 | #include "pkcs11.h" | 43 | #include "pkcs11.h" |
@@ -69,12 +74,25 @@ struct pkcs11_key { | |||
69 | CK_ULONG slotidx; | 74 | CK_ULONG slotidx; |
70 | int (*orig_finish)(RSA *rsa); | 75 | int (*orig_finish)(RSA *rsa); |
71 | RSA_METHOD *rsa_method; | 76 | RSA_METHOD *rsa_method; |
77 | EC_KEY_METHOD *ec_key_method; | ||
72 | char *keyid; | 78 | char *keyid; |
73 | int keyid_len; | 79 | int keyid_len; |
74 | }; | 80 | }; |
75 | 81 | ||
76 | int pkcs11_interactive = 0; | 82 | int pkcs11_interactive = 0; |
77 | 83 | ||
84 | #ifdef HAVE_DLOPEN | ||
85 | static void | ||
86 | ossl_error(const char *msg) | ||
87 | { | ||
88 | unsigned long e; | ||
89 | |||
90 | while ((e = ERR_get_error()) != 0) | ||
91 | error("%s: %s: %.100s", __func__, msg, | ||
92 | ERR_error_string(e, NULL)); | ||
93 | } | ||
94 | #endif | ||
95 | |||
78 | int | 96 | int |
79 | pkcs11_init(int interactive) | 97 | pkcs11_init(int interactive) |
80 | { | 98 | { |
@@ -84,9 +102,9 @@ pkcs11_init(int interactive) | |||
84 | } | 102 | } |
85 | 103 | ||
86 | /* | 104 | /* |
87 | * finalize a provider shared libarary, it's no longer usable. | 105 | * finalize a provider shared library, it's no longer usable. |
88 | * however, there might still be keys referencing this provider, | 106 | * however, there might still be keys referencing this provider, |
89 | * so the actuall freeing of memory is handled by pkcs11_provider_unref(). | 107 | * so the actual freeing of memory is handled by pkcs11_provider_unref(). |
90 | * this is called when a provider gets unregistered. | 108 | * this is called when a provider gets unregistered. |
91 | */ | 109 | */ |
92 | static void | 110 | static void |
@@ -123,6 +141,7 @@ pkcs11_provider_unref(struct pkcs11_provider *p) | |||
123 | if (--p->refcount <= 0) { | 141 | if (--p->refcount <= 0) { |
124 | if (p->valid) | 142 | if (p->valid) |
125 | error("pkcs11_provider_unref: %p still valid", p); | 143 | error("pkcs11_provider_unref: %p still valid", p); |
144 | free(p->name); | ||
126 | free(p->slotlist); | 145 | free(p->slotlist); |
127 | free(p->slotinfo); | 146 | free(p->slotinfo); |
128 | free(p); | 147 | free(p); |
@@ -218,43 +237,27 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, | |||
218 | return (ret); | 237 | return (ret); |
219 | } | 238 | } |
220 | 239 | ||
221 | /* openssl callback doing the actual signing operation */ | ||
222 | static int | 240 | static int |
223 | pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | 241 | pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type) |
224 | int padding) | ||
225 | { | 242 | { |
226 | struct pkcs11_key *k11; | ||
227 | struct pkcs11_slotinfo *si; | 243 | struct pkcs11_slotinfo *si; |
228 | CK_FUNCTION_LIST *f; | 244 | CK_FUNCTION_LIST *f; |
229 | CK_OBJECT_HANDLE obj; | 245 | CK_OBJECT_HANDLE obj; |
230 | CK_ULONG tlen = 0; | 246 | CK_RV rv; |
231 | CK_RV rv; | 247 | CK_OBJECT_CLASS private_key_class; |
232 | CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; | 248 | CK_BBOOL true_val; |
233 | CK_BBOOL true_val = CK_TRUE; | 249 | CK_MECHANISM mech; |
234 | CK_MECHANISM mech = { | 250 | CK_ATTRIBUTE key_filter[3]; |
235 | CKM_RSA_PKCS, NULL_PTR, 0 | ||
236 | }; | ||
237 | CK_ATTRIBUTE key_filter[] = { | ||
238 | {CKA_CLASS, NULL, sizeof(private_key_class) }, | ||
239 | {CKA_ID, NULL, 0}, | ||
240 | {CKA_SIGN, NULL, sizeof(true_val) } | ||
241 | }; | ||
242 | char *pin = NULL, prompt[1024]; | 251 | char *pin = NULL, prompt[1024]; |
243 | int rval = -1; | ||
244 | |||
245 | key_filter[0].pValue = &private_key_class; | ||
246 | key_filter[2].pValue = &true_val; | ||
247 | 252 | ||
248 | if ((k11 = RSA_get_app_data(rsa)) == NULL) { | ||
249 | error("RSA_get_app_data failed for rsa %p", rsa); | ||
250 | return (-1); | ||
251 | } | ||
252 | if (!k11->provider || !k11->provider->valid) { | 253 | if (!k11->provider || !k11->provider->valid) { |
253 | error("no pkcs11 (valid) provider for rsa %p", rsa); | 254 | error("no pkcs11 (valid) provider found"); |
254 | return (-1); | 255 | return (-1); |
255 | } | 256 | } |
257 | |||
256 | f = k11->provider->function_list; | 258 | f = k11->provider->function_list; |
257 | si = &k11->provider->slotinfo[k11->slotidx]; | 259 | si = &k11->provider->slotinfo[k11->slotidx]; |
260 | |||
258 | if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { | 261 | if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { |
259 | if (!pkcs11_interactive) { | 262 | if (!pkcs11_interactive) { |
260 | error("need pin entry%s", (si->token.flags & | 263 | error("need pin entry%s", (si->token.flags & |
@@ -283,23 +286,75 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | |||
283 | } | 286 | } |
284 | si->logged_in = 1; | 287 | si->logged_in = 1; |
285 | } | 288 | } |
289 | |||
290 | memset(&key_filter, 0, sizeof(key_filter)); | ||
291 | private_key_class = CKO_PRIVATE_KEY; | ||
292 | key_filter[0].type = CKA_CLASS; | ||
293 | key_filter[0].pValue = &private_key_class; | ||
294 | key_filter[0].ulValueLen = sizeof(private_key_class); | ||
295 | |||
296 | key_filter[1].type = CKA_ID; | ||
286 | key_filter[1].pValue = k11->keyid; | 297 | key_filter[1].pValue = k11->keyid; |
287 | key_filter[1].ulValueLen = k11->keyid_len; | 298 | key_filter[1].ulValueLen = k11->keyid_len; |
299 | |||
300 | true_val = CK_TRUE; | ||
301 | key_filter[2].type = CKA_SIGN; | ||
302 | key_filter[2].pValue = &true_val; | ||
303 | key_filter[2].ulValueLen = sizeof(true_val); | ||
304 | |||
288 | /* try to find object w/CKA_SIGN first, retry w/o */ | 305 | /* try to find object w/CKA_SIGN first, retry w/o */ |
289 | if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && | 306 | if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && |
290 | pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { | 307 | pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { |
291 | error("cannot find private key"); | 308 | error("cannot find private key"); |
292 | } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { | 309 | return (-1); |
310 | } | ||
311 | |||
312 | memset(&mech, 0, sizeof(mech)); | ||
313 | mech.mechanism = mech_type; | ||
314 | mech.pParameter = NULL_PTR; | ||
315 | mech.ulParameterLen = 0; | ||
316 | |||
317 | if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { | ||
293 | error("C_SignInit failed: %lu", rv); | 318 | error("C_SignInit failed: %lu", rv); |
294 | } else { | 319 | return (-1); |
295 | /* XXX handle CKR_BUFFER_TOO_SMALL */ | 320 | } |
296 | tlen = RSA_size(rsa); | 321 | |
297 | rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); | 322 | return (0); |
298 | if (rv == CKR_OK) | 323 | } |
299 | rval = tlen; | 324 | |
300 | else | 325 | /* openssl callback doing the actual signing operation */ |
301 | error("C_Sign failed: %lu", rv); | 326 | static int |
327 | pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, | ||
328 | int padding) | ||
329 | { | ||
330 | struct pkcs11_key *k11; | ||
331 | struct pkcs11_slotinfo *si; | ||
332 | CK_FUNCTION_LIST *f; | ||
333 | CK_ULONG tlen = 0; | ||
334 | CK_RV rv; | ||
335 | int rval = -1; | ||
336 | |||
337 | if ((k11 = RSA_get_app_data(rsa)) == NULL) { | ||
338 | error("RSA_get_app_data failed for rsa %p", rsa); | ||
339 | return (-1); | ||
340 | } | ||
341 | |||
342 | if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) { | ||
343 | error("pkcs11_get_key failed"); | ||
344 | return (-1); | ||
302 | } | 345 | } |
346 | |||
347 | f = k11->provider->function_list; | ||
348 | si = &k11->provider->slotinfo[k11->slotidx]; | ||
349 | tlen = RSA_size(rsa); | ||
350 | |||
351 | /* XXX handle CKR_BUFFER_TOO_SMALL */ | ||
352 | rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); | ||
353 | if (rv == CKR_OK) | ||
354 | rval = tlen; | ||
355 | else | ||
356 | error("C_Sign failed: %lu", rv); | ||
357 | |||
303 | return (rval); | 358 | return (rval); |
304 | } | 359 | } |
305 | 360 | ||
@@ -344,6 +399,115 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, | |||
344 | return (0); | 399 | return (0); |
345 | } | 400 | } |
346 | 401 | ||
402 | /* openssl callback doing the actual signing operation */ | ||
403 | static ECDSA_SIG * | ||
404 | ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, | ||
405 | const BIGNUM *rp, EC_KEY *ec) | ||
406 | { | ||
407 | struct pkcs11_key *k11; | ||
408 | struct pkcs11_slotinfo *si; | ||
409 | CK_FUNCTION_LIST *f; | ||
410 | CK_ULONG siglen = 0, bnlen; | ||
411 | CK_RV rv; | ||
412 | ECDSA_SIG *ret = NULL; | ||
413 | u_char *sig; | ||
414 | const u_char *cp; | ||
415 | |||
416 | if ((k11 = EC_KEY_get_ex_data(ec, 0)) == NULL) { | ||
417 | ossl_error("EC_KEY_get_key_method_data failed for ec"); | ||
418 | return (NULL); | ||
419 | } | ||
420 | |||
421 | if (pkcs11_get_key(k11, CKM_ECDSA) == -1) { | ||
422 | error("pkcs11_get_key failed"); | ||
423 | return (NULL); | ||
424 | } | ||
425 | |||
426 | f = k11->provider->function_list; | ||
427 | si = &k11->provider->slotinfo[k11->slotidx]; | ||
428 | |||
429 | siglen = ECDSA_size(ec); | ||
430 | sig = xmalloc(siglen); | ||
431 | |||
432 | /* XXX handle CKR_BUFFER_TOO_SMALL */ | ||
433 | rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen); | ||
434 | if (rv != CKR_OK) { | ||
435 | error("C_Sign failed: %lu", rv); | ||
436 | goto done; | ||
437 | } | ||
438 | cp = sig; | ||
439 | ret = d2i_ECDSA_SIG(NULL, &cp, siglen); | ||
440 | if (ret == NULL) { | ||
441 | /* | ||
442 | * d2i_ECDSA_SIG failed, so sig does not point to a DER-encoded | ||
443 | * sequence, but to the concatenation r|s. | ||
444 | */ | ||
445 | if (siglen < 64 || siglen > 132 || siglen % 2) { | ||
446 | ossl_error("d2i_ECDSA_SIG failed"); | ||
447 | goto done; | ||
448 | } | ||
449 | bnlen = siglen/2; | ||
450 | if ((ret = ECDSA_SIG_new()) == NULL) { | ||
451 | error("ECDSA_SIG_new failed"); | ||
452 | goto done; | ||
453 | } | ||
454 | if (BN_bin2bn(sig, bnlen, ret->r) == NULL || | ||
455 | BN_bin2bn(sig+bnlen, bnlen, ret->s) == NULL) { | ||
456 | ossl_error("d2i_ECDSA_SIG failed"); | ||
457 | ECDSA_SIG_free(ret); | ||
458 | ret = NULL; | ||
459 | goto done; | ||
460 | } | ||
461 | } | ||
462 | done: | ||
463 | free(sig); | ||
464 | |||
465 | return (ret); | ||
466 | } | ||
467 | |||
468 | static EC_KEY_METHOD *ec_key_method; | ||
469 | |||
470 | static int | ||
471 | pkcs11_ecdsa_start_wrapper(void) | ||
472 | { | ||
473 | int (*orig_sign)(int, const unsigned char *, int, unsigned char *, | ||
474 | unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; | ||
475 | |||
476 | if (ec_key_method != NULL) | ||
477 | return (0); | ||
478 | ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); | ||
479 | if (ec_key_method == NULL) | ||
480 | return (-1); | ||
481 | EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL); | ||
482 | EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign); | ||
483 | return (0); | ||
484 | } | ||
485 | |||
486 | static int | ||
487 | pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, | ||
488 | CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec) | ||
489 | { | ||
490 | struct pkcs11_key *k11; | ||
491 | |||
492 | if (pkcs11_ecdsa_start_wrapper() == -1) | ||
493 | return (-1); | ||
494 | |||
495 | k11 = xcalloc(1, sizeof(*k11)); | ||
496 | k11->provider = provider; | ||
497 | provider->refcount++; /* provider referenced by ECDSA key */ | ||
498 | k11->slotidx = slotidx; | ||
499 | /* identify key object on smartcard */ | ||
500 | k11->keyid_len = keyid_attrib->ulValueLen; | ||
501 | k11->keyid = xmalloc(k11->keyid_len); | ||
502 | memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); | ||
503 | k11->ec_key_method = ec_key_method; | ||
504 | |||
505 | EC_KEY_set_method(ec, k11->ec_key_method); | ||
506 | EC_KEY_set_ex_data(ec, 0, k11); | ||
507 | |||
508 | return (0); | ||
509 | } | ||
510 | |||
347 | /* remove trailing spaces */ | 511 | /* remove trailing spaces */ |
348 | static void | 512 | static void |
349 | rmspace(u_char *buf, size_t len) | 513 | rmspace(u_char *buf, size_t len) |
@@ -364,18 +528,19 @@ rmspace(u_char *buf, size_t len) | |||
364 | * if pin == NULL we delay login until key use | 528 | * if pin == NULL we delay login until key use |
365 | */ | 529 | */ |
366 | static int | 530 | static int |
367 | pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) | 531 | pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin, |
532 | CK_ULONG user) | ||
368 | { | 533 | { |
369 | CK_RV rv; | 534 | CK_RV rv; |
370 | CK_FUNCTION_LIST *f; | 535 | CK_FUNCTION_LIST *f; |
371 | CK_SESSION_HANDLE session; | 536 | CK_SESSION_HANDLE session; |
372 | int login_required; | 537 | int login_required, ret; |
373 | 538 | ||
374 | f = p->function_list; | 539 | f = p->function_list; |
375 | login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; | 540 | login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; |
376 | if (pin && login_required && !strlen(pin)) { | 541 | if (pin && login_required && !strlen(pin)) { |
377 | error("pin required"); | 542 | error("pin required"); |
378 | return (-1); | 543 | return (-SSH_PKCS11_ERR_PIN_REQUIRED); |
379 | } | 544 | } |
380 | if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| | 545 | if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| |
381 | CKF_SERIAL_SESSION, NULL, NULL, &session)) | 546 | CKF_SERIAL_SESSION, NULL, NULL, &session)) |
@@ -384,13 +549,16 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) | |||
384 | return (-1); | 549 | return (-1); |
385 | } | 550 | } |
386 | if (login_required && pin) { | 551 | if (login_required && pin) { |
387 | rv = f->C_Login(session, CKU_USER, | 552 | rv = f->C_Login(session, user, |
388 | (u_char *)pin, strlen(pin)); | 553 | (u_char *)pin, strlen(pin)); |
389 | if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { | 554 | if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { |
390 | error("C_Login failed: %lu", rv); | 555 | error("C_Login failed: %lu", rv); |
556 | ret = (rv == CKR_PIN_LOCKED) ? | ||
557 | -SSH_PKCS11_ERR_PIN_LOCKED : | ||
558 | -SSH_PKCS11_ERR_LOGIN_FAIL; | ||
391 | if ((rv = f->C_CloseSession(session)) != CKR_OK) | 559 | if ((rv = f->C_CloseSession(session)) != CKR_OK) |
392 | error("C_CloseSession failed: %lu", rv); | 560 | error("C_CloseSession failed: %lu", rv); |
393 | return (-1); | 561 | return (ret); |
394 | } | 562 | } |
395 | p->slotinfo[slotidx].logged_in = 1; | 563 | p->slotinfo[slotidx].logged_in = 1; |
396 | } | 564 | } |
@@ -398,48 +566,6 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) | |||
398 | return (0); | 566 | return (0); |
399 | } | 567 | } |
400 | 568 | ||
401 | /* | ||
402 | * lookup public keys for token in slot identified by slotidx, | ||
403 | * add 'wrapped' public keys to the 'keysp' array and increment nkeys. | ||
404 | * keysp points to an (possibly empty) array with *nkeys keys. | ||
405 | */ | ||
406 | static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, | ||
407 | CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) | ||
408 | __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); | ||
409 | |||
410 | static int | ||
411 | pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
412 | struct sshkey ***keysp, int *nkeys) | ||
413 | { | ||
414 | CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; | ||
415 | CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; | ||
416 | CK_ATTRIBUTE pubkey_filter[] = { | ||
417 | { CKA_CLASS, NULL, sizeof(pubkey_class) } | ||
418 | }; | ||
419 | CK_ATTRIBUTE cert_filter[] = { | ||
420 | { CKA_CLASS, NULL, sizeof(cert_class) } | ||
421 | }; | ||
422 | CK_ATTRIBUTE pubkey_attribs[] = { | ||
423 | { CKA_ID, NULL, 0 }, | ||
424 | { CKA_MODULUS, NULL, 0 }, | ||
425 | { CKA_PUBLIC_EXPONENT, NULL, 0 } | ||
426 | }; | ||
427 | CK_ATTRIBUTE cert_attribs[] = { | ||
428 | { CKA_ID, NULL, 0 }, | ||
429 | { CKA_SUBJECT, NULL, 0 }, | ||
430 | { CKA_VALUE, NULL, 0 } | ||
431 | }; | ||
432 | pubkey_filter[0].pValue = &pubkey_class; | ||
433 | cert_filter[0].pValue = &cert_class; | ||
434 | |||
435 | if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, | ||
436 | keysp, nkeys) < 0 || | ||
437 | pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, | ||
438 | keysp, nkeys) < 0) | ||
439 | return (-1); | ||
440 | return (0); | ||
441 | } | ||
442 | |||
443 | static int | 569 | static int |
444 | pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) | 570 | pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) |
445 | { | 571 | { |
@@ -451,6 +577,355 @@ pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) | |||
451 | return (0); | 577 | return (0); |
452 | } | 578 | } |
453 | 579 | ||
580 | static struct sshkey * | ||
581 | pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
582 | CK_OBJECT_HANDLE *obj) | ||
583 | { | ||
584 | CK_ATTRIBUTE key_attr[3]; | ||
585 | CK_SESSION_HANDLE session; | ||
586 | CK_FUNCTION_LIST *f = NULL; | ||
587 | CK_RV rv; | ||
588 | EC_KEY *ec = NULL; | ||
589 | EC_GROUP *group = NULL; | ||
590 | struct sshkey *key = NULL; | ||
591 | const unsigned char *attrp = NULL; | ||
592 | int i; | ||
593 | int nid; | ||
594 | |||
595 | memset(&key_attr, 0, sizeof(key_attr)); | ||
596 | key_attr[0].type = CKA_ID; | ||
597 | key_attr[1].type = CKA_EC_POINT; | ||
598 | key_attr[2].type = CKA_EC_PARAMS; | ||
599 | |||
600 | session = p->slotinfo[slotidx].session; | ||
601 | f = p->function_list; | ||
602 | |||
603 | /* figure out size of the attributes */ | ||
604 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); | ||
605 | if (rv != CKR_OK) { | ||
606 | error("C_GetAttributeValue failed: %lu", rv); | ||
607 | return (NULL); | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * Allow CKA_ID (always first attribute) to be empty, but | ||
612 | * ensure that none of the others are zero length. | ||
613 | * XXX assumes CKA_ID is always first. | ||
614 | */ | ||
615 | if (key_attr[1].ulValueLen == 0 || | ||
616 | key_attr[2].ulValueLen == 0) { | ||
617 | error("invalid attribute length"); | ||
618 | return (NULL); | ||
619 | } | ||
620 | |||
621 | /* allocate buffers for attributes */ | ||
622 | for (i = 0; i < 3; i++) | ||
623 | if (key_attr[i].ulValueLen > 0) | ||
624 | key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); | ||
625 | |||
626 | /* retrieve ID, public point and curve parameters of EC key */ | ||
627 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); | ||
628 | if (rv != CKR_OK) { | ||
629 | error("C_GetAttributeValue failed: %lu", rv); | ||
630 | goto fail; | ||
631 | } | ||
632 | |||
633 | ec = EC_KEY_new(); | ||
634 | if (ec == NULL) { | ||
635 | error("EC_KEY_new failed"); | ||
636 | goto fail; | ||
637 | } | ||
638 | |||
639 | attrp = key_attr[2].pValue; | ||
640 | group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen); | ||
641 | if (group == NULL) { | ||
642 | ossl_error("d2i_ECPKParameters failed"); | ||
643 | goto fail; | ||
644 | } | ||
645 | |||
646 | if (EC_KEY_set_group(ec, group) == 0) { | ||
647 | ossl_error("EC_KEY_set_group failed"); | ||
648 | goto fail; | ||
649 | } | ||
650 | |||
651 | if (key_attr[1].ulValueLen <= 2) { | ||
652 | error("CKA_EC_POINT too small"); | ||
653 | goto fail; | ||
654 | } | ||
655 | |||
656 | attrp = (const unsigned char *)key_attr[1].pValue; | ||
657 | if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen) == NULL) { | ||
658 | /* try to skip DER header (octet string type and length byte) */ | ||
659 | attrp = (const unsigned char *)key_attr[1].pValue + 2; | ||
660 | if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen - 2) | ||
661 | == NULL) { | ||
662 | ossl_error("o2i_ECPublicKey failed"); | ||
663 | goto fail; | ||
664 | } | ||
665 | } | ||
666 | |||
667 | nid = sshkey_ecdsa_key_to_nid(ec); | ||
668 | if (nid < 0) { | ||
669 | error("couldn't get curve nid"); | ||
670 | goto fail; | ||
671 | } | ||
672 | |||
673 | if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec)) | ||
674 | goto fail; | ||
675 | |||
676 | key = sshkey_new(KEY_UNSPEC); | ||
677 | if (key == NULL) { | ||
678 | error("sshkey_new failed"); | ||
679 | goto fail; | ||
680 | } | ||
681 | |||
682 | key->ecdsa = ec; | ||
683 | key->ecdsa_nid = nid; | ||
684 | key->type = KEY_ECDSA; | ||
685 | key->flags |= SSHKEY_FLAG_EXT; | ||
686 | ec = NULL; /* now owned by key */ | ||
687 | |||
688 | fail: | ||
689 | for (i = 0; i < 3; i++) | ||
690 | free(key_attr[i].pValue); | ||
691 | if (ec) | ||
692 | EC_KEY_free(ec); | ||
693 | if (group) | ||
694 | EC_GROUP_free(group); | ||
695 | |||
696 | return (key); | ||
697 | } | ||
698 | |||
699 | static struct sshkey * | ||
700 | pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
701 | CK_OBJECT_HANDLE *obj) | ||
702 | { | ||
703 | CK_ATTRIBUTE key_attr[3]; | ||
704 | CK_SESSION_HANDLE session; | ||
705 | CK_FUNCTION_LIST *f = NULL; | ||
706 | CK_RV rv; | ||
707 | RSA *rsa = NULL; | ||
708 | BIGNUM *rsa_n, *rsa_e; | ||
709 | struct sshkey *key = NULL; | ||
710 | int i; | ||
711 | |||
712 | memset(&key_attr, 0, sizeof(key_attr)); | ||
713 | key_attr[0].type = CKA_ID; | ||
714 | key_attr[1].type = CKA_MODULUS; | ||
715 | key_attr[2].type = CKA_PUBLIC_EXPONENT; | ||
716 | |||
717 | session = p->slotinfo[slotidx].session; | ||
718 | f = p->function_list; | ||
719 | |||
720 | /* figure out size of the attributes */ | ||
721 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); | ||
722 | if (rv != CKR_OK) { | ||
723 | error("C_GetAttributeValue failed: %lu", rv); | ||
724 | return (NULL); | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | * Allow CKA_ID (always first attribute) to be empty, but | ||
729 | * ensure that none of the others are zero length. | ||
730 | * XXX assumes CKA_ID is always first. | ||
731 | */ | ||
732 | if (key_attr[1].ulValueLen == 0 || | ||
733 | key_attr[2].ulValueLen == 0) { | ||
734 | error("invalid attribute length"); | ||
735 | return (NULL); | ||
736 | } | ||
737 | |||
738 | /* allocate buffers for attributes */ | ||
739 | for (i = 0; i < 3; i++) | ||
740 | if (key_attr[i].ulValueLen > 0) | ||
741 | key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); | ||
742 | |||
743 | /* retrieve ID, modulus and public exponent of RSA key */ | ||
744 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); | ||
745 | if (rv != CKR_OK) { | ||
746 | error("C_GetAttributeValue failed: %lu", rv); | ||
747 | goto fail; | ||
748 | } | ||
749 | |||
750 | rsa = RSA_new(); | ||
751 | if (rsa == NULL) { | ||
752 | error("RSA_new failed"); | ||
753 | goto fail; | ||
754 | } | ||
755 | |||
756 | rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL); | ||
757 | rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL); | ||
758 | if (rsa_n == NULL || rsa_e == NULL) { | ||
759 | error("BN_bin2bn failed"); | ||
760 | goto fail; | ||
761 | } | ||
762 | if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) | ||
763 | fatal("%s: set key", __func__); | ||
764 | rsa_n = rsa_e = NULL; /* transferred */ | ||
765 | |||
766 | if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa)) | ||
767 | goto fail; | ||
768 | |||
769 | key = sshkey_new(KEY_UNSPEC); | ||
770 | if (key == NULL) { | ||
771 | error("sshkey_new failed"); | ||
772 | goto fail; | ||
773 | } | ||
774 | |||
775 | key->rsa = rsa; | ||
776 | key->type = KEY_RSA; | ||
777 | key->flags |= SSHKEY_FLAG_EXT; | ||
778 | rsa = NULL; /* now owned by key */ | ||
779 | |||
780 | fail: | ||
781 | for (i = 0; i < 3; i++) | ||
782 | free(key_attr[i].pValue); | ||
783 | RSA_free(rsa); | ||
784 | |||
785 | return (key); | ||
786 | } | ||
787 | |||
788 | static struct sshkey * | ||
789 | pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
790 | CK_OBJECT_HANDLE *obj) | ||
791 | { | ||
792 | CK_ATTRIBUTE cert_attr[3]; | ||
793 | CK_SESSION_HANDLE session; | ||
794 | CK_FUNCTION_LIST *f = NULL; | ||
795 | CK_RV rv; | ||
796 | X509 *x509 = NULL; | ||
797 | EVP_PKEY *evp; | ||
798 | RSA *rsa = NULL; | ||
799 | EC_KEY *ec = NULL; | ||
800 | struct sshkey *key = NULL; | ||
801 | int i; | ||
802 | int nid; | ||
803 | const u_char *cp; | ||
804 | |||
805 | memset(&cert_attr, 0, sizeof(cert_attr)); | ||
806 | cert_attr[0].type = CKA_ID; | ||
807 | cert_attr[1].type = CKA_SUBJECT; | ||
808 | cert_attr[2].type = CKA_VALUE; | ||
809 | |||
810 | session = p->slotinfo[slotidx].session; | ||
811 | f = p->function_list; | ||
812 | |||
813 | /* figure out size of the attributes */ | ||
814 | rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); | ||
815 | if (rv != CKR_OK) { | ||
816 | error("C_GetAttributeValue failed: %lu", rv); | ||
817 | return (NULL); | ||
818 | } | ||
819 | |||
820 | /* | ||
821 | * Allow CKA_ID (always first attribute) to be empty, but | ||
822 | * ensure that none of the others are zero length. | ||
823 | * XXX assumes CKA_ID is always first. | ||
824 | */ | ||
825 | if (cert_attr[1].ulValueLen == 0 || | ||
826 | cert_attr[2].ulValueLen == 0) { | ||
827 | error("invalid attribute length"); | ||
828 | return (NULL); | ||
829 | } | ||
830 | |||
831 | /* allocate buffers for attributes */ | ||
832 | for (i = 0; i < 3; i++) | ||
833 | if (cert_attr[i].ulValueLen > 0) | ||
834 | cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen); | ||
835 | |||
836 | /* retrieve ID, subject and value of certificate */ | ||
837 | rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); | ||
838 | if (rv != CKR_OK) { | ||
839 | error("C_GetAttributeValue failed: %lu", rv); | ||
840 | goto fail; | ||
841 | } | ||
842 | |||
843 | x509 = X509_new(); | ||
844 | if (x509 == NULL) { | ||
845 | error("x509_new failed"); | ||
846 | goto fail; | ||
847 | } | ||
848 | |||
849 | cp = cert_attr[2].pValue; | ||
850 | if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) { | ||
851 | error("d2i_x509 failed"); | ||
852 | goto fail; | ||
853 | } | ||
854 | |||
855 | evp = X509_get_pubkey(x509); | ||
856 | if (evp == NULL) { | ||
857 | error("X509_get_pubkey failed"); | ||
858 | goto fail; | ||
859 | } | ||
860 | |||
861 | if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) { | ||
862 | if (EVP_PKEY_get0_RSA(evp) == NULL) { | ||
863 | error("invalid x509; no rsa key"); | ||
864 | goto fail; | ||
865 | } | ||
866 | if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) { | ||
867 | error("RSAPublicKey_dup failed"); | ||
868 | goto fail; | ||
869 | } | ||
870 | |||
871 | if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa)) | ||
872 | goto fail; | ||
873 | |||
874 | key = sshkey_new(KEY_UNSPEC); | ||
875 | if (key == NULL) { | ||
876 | error("sshkey_new failed"); | ||
877 | goto fail; | ||
878 | } | ||
879 | |||
880 | key->rsa = rsa; | ||
881 | key->type = KEY_RSA; | ||
882 | key->flags |= SSHKEY_FLAG_EXT; | ||
883 | rsa = NULL; /* now owned by key */ | ||
884 | } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) { | ||
885 | /* XXX XXX fix accessor */ | ||
886 | if (evp->pkey.ec == NULL) { | ||
887 | error("invalid x509; no ec key"); | ||
888 | goto fail; | ||
889 | } | ||
890 | if ((ec = EC_KEY_dup(evp->pkey.ec)) == NULL) { | ||
891 | error("EC_KEY_dup failed"); | ||
892 | goto fail; | ||
893 | } | ||
894 | |||
895 | nid = sshkey_ecdsa_key_to_nid(ec); | ||
896 | if (nid < 0) { | ||
897 | error("couldn't get curve nid"); | ||
898 | goto fail; | ||
899 | } | ||
900 | |||
901 | if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec)) | ||
902 | goto fail; | ||
903 | |||
904 | key = sshkey_new(KEY_UNSPEC); | ||
905 | if (key == NULL) { | ||
906 | error("sshkey_new failed"); | ||
907 | goto fail; | ||
908 | } | ||
909 | |||
910 | key->ecdsa = ec; | ||
911 | key->ecdsa_nid = nid; | ||
912 | key->type = KEY_ECDSA; | ||
913 | key->flags |= SSHKEY_FLAG_EXT; | ||
914 | ec = NULL; /* now owned by key */ | ||
915 | } else | ||
916 | error("unknown certificate key type"); | ||
917 | |||
918 | fail: | ||
919 | for (i = 0; i < 3; i++) | ||
920 | free(cert_attr[i].pValue); | ||
921 | X509_free(x509); | ||
922 | RSA_free(rsa); | ||
923 | EC_KEY_free(ec); | ||
924 | |||
925 | return (key); | ||
926 | } | ||
927 | |||
928 | #if 0 | ||
454 | static int | 929 | static int |
455 | have_rsa_key(const RSA *rsa) | 930 | have_rsa_key(const RSA *rsa) |
456 | { | 931 | { |
@@ -459,140 +934,398 @@ have_rsa_key(const RSA *rsa) | |||
459 | RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); | 934 | RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); |
460 | return rsa_n != NULL && rsa_e != NULL; | 935 | return rsa_n != NULL && rsa_e != NULL; |
461 | } | 936 | } |
937 | #endif | ||
462 | 938 | ||
939 | /* | ||
940 | * lookup certificates for token in slot identified by slotidx, | ||
941 | * add 'wrapped' public keys to the 'keysp' array and increment nkeys. | ||
942 | * keysp points to an (possibly empty) array with *nkeys keys. | ||
943 | */ | ||
463 | static int | 944 | static int |
464 | pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, | 945 | pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, |
465 | CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], | ||
466 | struct sshkey ***keysp, int *nkeys) | 946 | struct sshkey ***keysp, int *nkeys) |
467 | { | 947 | { |
468 | struct sshkey *key; | 948 | struct sshkey *key = NULL; |
469 | RSA *rsa; | 949 | CK_OBJECT_CLASS key_class; |
470 | X509 *x509; | 950 | CK_ATTRIBUTE key_attr[1]; |
471 | EVP_PKEY *evp; | 951 | CK_SESSION_HANDLE session; |
472 | int i; | 952 | CK_FUNCTION_LIST *f = NULL; |
473 | const u_char *cp; | 953 | CK_RV rv; |
474 | CK_RV rv; | 954 | CK_OBJECT_HANDLE obj; |
475 | CK_OBJECT_HANDLE obj; | 955 | CK_ULONG n = 0; |
476 | CK_ULONG nfound; | 956 | int ret = -1; |
477 | CK_SESSION_HANDLE session; | 957 | |
478 | CK_FUNCTION_LIST *f; | 958 | memset(&key_attr, 0, sizeof(key_attr)); |
959 | memset(&obj, 0, sizeof(obj)); | ||
960 | |||
961 | key_class = CKO_CERTIFICATE; | ||
962 | key_attr[0].type = CKA_CLASS; | ||
963 | key_attr[0].pValue = &key_class; | ||
964 | key_attr[0].ulValueLen = sizeof(key_class); | ||
479 | 965 | ||
480 | f = p->function_list; | ||
481 | session = p->slotinfo[slotidx].session; | 966 | session = p->slotinfo[slotidx].session; |
482 | /* setup a filter the looks for public keys */ | 967 | f = p->function_list; |
483 | if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { | 968 | |
969 | rv = f->C_FindObjectsInit(session, key_attr, 1); | ||
970 | if (rv != CKR_OK) { | ||
484 | error("C_FindObjectsInit failed: %lu", rv); | 971 | error("C_FindObjectsInit failed: %lu", rv); |
485 | return (-1); | 972 | goto fail; |
486 | } | 973 | } |
974 | |||
487 | while (1) { | 975 | while (1) { |
488 | /* XXX 3 attributes in attribs[] */ | 976 | CK_CERTIFICATE_TYPE ck_cert_type; |
489 | for (i = 0; i < 3; i++) { | 977 | |
490 | attribs[i].pValue = NULL; | 978 | rv = f->C_FindObjects(session, &obj, 1, &n); |
491 | attribs[i].ulValueLen = 0; | 979 | if (rv != CKR_OK) { |
980 | error("C_FindObjects failed: %lu", rv); | ||
981 | goto fail; | ||
492 | } | 982 | } |
493 | if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK | 983 | if (n == 0) |
494 | || nfound == 0) | ||
495 | break; | 984 | break; |
496 | /* found a key, so figure out size of the attributes */ | 985 | |
497 | if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) | 986 | memset(&ck_cert_type, 0, sizeof(ck_cert_type)); |
498 | != CKR_OK) { | 987 | memset(&key_attr, 0, sizeof(key_attr)); |
988 | key_attr[0].type = CKA_CERTIFICATE_TYPE; | ||
989 | key_attr[0].pValue = &ck_cert_type; | ||
990 | key_attr[0].ulValueLen = sizeof(ck_cert_type); | ||
991 | |||
992 | rv = f->C_GetAttributeValue(session, obj, key_attr, 1); | ||
993 | if (rv != CKR_OK) { | ||
499 | error("C_GetAttributeValue failed: %lu", rv); | 994 | error("C_GetAttributeValue failed: %lu", rv); |
500 | continue; | 995 | goto fail; |
501 | } | 996 | } |
502 | /* | 997 | |
503 | * Allow CKA_ID (always first attribute) to be empty, but | 998 | switch (ck_cert_type) { |
504 | * ensure that none of the others are zero length. | 999 | case CKC_X_509: |
505 | * XXX assumes CKA_ID is always first. | 1000 | key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj); |
506 | */ | 1001 | break; |
507 | if (attribs[1].ulValueLen == 0 || | 1002 | default: |
508 | attribs[2].ulValueLen == 0) { | 1003 | /* XXX print key type? */ |
1004 | error("skipping unsupported certificate type"); | ||
1005 | } | ||
1006 | |||
1007 | if (key == NULL) { | ||
1008 | error("failed to fetch key"); | ||
509 | continue; | 1009 | continue; |
510 | } | 1010 | } |
511 | /* allocate buffers for attributes */ | 1011 | |
512 | for (i = 0; i < 3; i++) { | 1012 | if (pkcs11_key_included(keysp, nkeys, key)) { |
513 | if (attribs[i].ulValueLen > 0) { | 1013 | sshkey_free(key); |
514 | attribs[i].pValue = xmalloc( | 1014 | } else { |
515 | attribs[i].ulValueLen); | 1015 | /* expand key array and add key */ |
516 | } | 1016 | *keysp = xrecallocarray(*keysp, *nkeys, |
1017 | *nkeys + 1, sizeof(struct sshkey *)); | ||
1018 | (*keysp)[*nkeys] = key; | ||
1019 | *nkeys = *nkeys + 1; | ||
1020 | debug("have %d keys", *nkeys); | ||
517 | } | 1021 | } |
1022 | } | ||
518 | 1023 | ||
519 | /* | 1024 | ret = 0; |
520 | * retrieve ID, modulus and public exponent of RSA key, | 1025 | fail: |
521 | * or ID, subject and value for certificates. | 1026 | rv = f->C_FindObjectsFinal(session); |
522 | */ | 1027 | if (rv != CKR_OK) { |
523 | rsa = NULL; | 1028 | error("C_FindObjectsFinal failed: %lu", rv); |
524 | if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) | 1029 | ret = -1; |
525 | != CKR_OK) { | 1030 | } |
1031 | |||
1032 | return (ret); | ||
1033 | } | ||
1034 | |||
1035 | /* | ||
1036 | * lookup public keys for token in slot identified by slotidx, | ||
1037 | * add 'wrapped' public keys to the 'keysp' array and increment nkeys. | ||
1038 | * keysp points to an (possibly empty) array with *nkeys keys. | ||
1039 | */ | ||
1040 | static int | ||
1041 | pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
1042 | struct sshkey ***keysp, int *nkeys) | ||
1043 | { | ||
1044 | struct sshkey *key = NULL; | ||
1045 | CK_OBJECT_CLASS key_class; | ||
1046 | CK_ATTRIBUTE key_attr[1]; | ||
1047 | CK_SESSION_HANDLE session; | ||
1048 | CK_FUNCTION_LIST *f = NULL; | ||
1049 | CK_RV rv; | ||
1050 | CK_OBJECT_HANDLE obj; | ||
1051 | CK_ULONG n = 0; | ||
1052 | int ret = -1; | ||
1053 | |||
1054 | memset(&key_attr, 0, sizeof(key_attr)); | ||
1055 | memset(&obj, 0, sizeof(obj)); | ||
1056 | |||
1057 | key_class = CKO_PUBLIC_KEY; | ||
1058 | key_attr[0].type = CKA_CLASS; | ||
1059 | key_attr[0].pValue = &key_class; | ||
1060 | key_attr[0].ulValueLen = sizeof(key_class); | ||
1061 | |||
1062 | session = p->slotinfo[slotidx].session; | ||
1063 | f = p->function_list; | ||
1064 | |||
1065 | rv = f->C_FindObjectsInit(session, key_attr, 1); | ||
1066 | if (rv != CKR_OK) { | ||
1067 | error("C_FindObjectsInit failed: %lu", rv); | ||
1068 | goto fail; | ||
1069 | } | ||
1070 | |||
1071 | while (1) { | ||
1072 | CK_KEY_TYPE ck_key_type; | ||
1073 | |||
1074 | rv = f->C_FindObjects(session, &obj, 1, &n); | ||
1075 | if (rv != CKR_OK) { | ||
1076 | error("C_FindObjects failed: %lu", rv); | ||
1077 | goto fail; | ||
1078 | } | ||
1079 | if (n == 0) | ||
1080 | break; | ||
1081 | |||
1082 | memset(&ck_key_type, 0, sizeof(ck_key_type)); | ||
1083 | memset(&key_attr, 0, sizeof(key_attr)); | ||
1084 | key_attr[0].type = CKA_KEY_TYPE; | ||
1085 | key_attr[0].pValue = &ck_key_type; | ||
1086 | key_attr[0].ulValueLen = sizeof(ck_key_type); | ||
1087 | |||
1088 | rv = f->C_GetAttributeValue(session, obj, key_attr, 1); | ||
1089 | if (rv != CKR_OK) { | ||
526 | error("C_GetAttributeValue failed: %lu", rv); | 1090 | error("C_GetAttributeValue failed: %lu", rv); |
527 | } else if (attribs[1].type == CKA_MODULUS ) { | 1091 | goto fail; |
528 | if ((rsa = RSA_new()) == NULL) { | 1092 | } |
529 | error("RSA_new failed"); | 1093 | |
530 | } else { | 1094 | switch (ck_key_type) { |
531 | BIGNUM *rsa_n, *rsa_e; | 1095 | case CKK_RSA: |
532 | 1096 | key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj); | |
533 | rsa_n = BN_bin2bn(attribs[1].pValue, | 1097 | break; |
534 | attribs[1].ulValueLen, NULL); | 1098 | case CKK_ECDSA: |
535 | rsa_e = BN_bin2bn(attribs[2].pValue, | 1099 | key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj); |
536 | attribs[2].ulValueLen, NULL); | 1100 | break; |
537 | if (rsa_n != NULL && rsa_e != NULL) { | 1101 | default: |
538 | if (!RSA_set0_key(rsa, | 1102 | /* XXX print key type? */ |
539 | rsa_n, rsa_e, NULL)) | 1103 | error("skipping unsupported key type"); |
540 | fatal("%s: set key", __func__); | 1104 | } |
541 | rsa_n = rsa_e = NULL; /* transferred */ | 1105 | |
542 | } | 1106 | if (key == NULL) { |
543 | BN_free(rsa_n); | 1107 | error("failed to fetch key"); |
544 | BN_free(rsa_e); | 1108 | continue; |
545 | } | 1109 | } |
1110 | |||
1111 | if (pkcs11_key_included(keysp, nkeys, key)) { | ||
1112 | sshkey_free(key); | ||
546 | } else { | 1113 | } else { |
547 | cp = attribs[2].pValue; | 1114 | /* expand key array and add key */ |
548 | if ((x509 = X509_new()) == NULL) { | 1115 | *keysp = xrecallocarray(*keysp, *nkeys, |
549 | error("X509_new failed"); | 1116 | *nkeys + 1, sizeof(struct sshkey *)); |
550 | } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) | 1117 | (*keysp)[*nkeys] = key; |
551 | == NULL) { | 1118 | *nkeys = *nkeys + 1; |
552 | error("d2i_X509 failed"); | 1119 | debug("have %d keys", *nkeys); |
553 | } else if ((evp = X509_get_pubkey(x509)) == NULL || | 1120 | } |
554 | EVP_PKEY_base_id(evp) != EVP_PKEY_RSA || | ||
555 | EVP_PKEY_get0_RSA(evp) == NULL) { | ||
556 | debug("X509_get_pubkey failed or no rsa"); | ||
557 | } else if ((rsa = RSAPublicKey_dup( | ||
558 | EVP_PKEY_get0_RSA(evp))) == NULL) { | ||
559 | error("RSAPublicKey_dup"); | ||
560 | } | ||
561 | X509_free(x509); | ||
562 | } | ||
563 | if (rsa && have_rsa_key(rsa) && | ||
564 | pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { | ||
565 | if ((key = sshkey_new(KEY_UNSPEC)) == NULL) | ||
566 | fatal("sshkey_new failed"); | ||
567 | key->rsa = rsa; | ||
568 | key->type = KEY_RSA; | ||
569 | key->flags |= SSHKEY_FLAG_EXT; | ||
570 | if (pkcs11_key_included(keysp, nkeys, key)) { | ||
571 | sshkey_free(key); | ||
572 | } else { | ||
573 | /* expand key array and add key */ | ||
574 | *keysp = xrecallocarray(*keysp, *nkeys, | ||
575 | *nkeys + 1, sizeof(struct sshkey *)); | ||
576 | (*keysp)[*nkeys] = key; | ||
577 | *nkeys = *nkeys + 1; | ||
578 | debug("have %d keys", *nkeys); | ||
579 | } | ||
580 | } else if (rsa) { | ||
581 | RSA_free(rsa); | ||
582 | } | ||
583 | for (i = 0; i < 3; i++) | ||
584 | free(attribs[i].pValue); | ||
585 | } | 1121 | } |
586 | if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) | 1122 | |
1123 | ret = 0; | ||
1124 | fail: | ||
1125 | rv = f->C_FindObjectsFinal(session); | ||
1126 | if (rv != CKR_OK) { | ||
587 | error("C_FindObjectsFinal failed: %lu", rv); | 1127 | error("C_FindObjectsFinal failed: %lu", rv); |
588 | return (0); | 1128 | ret = -1; |
1129 | } | ||
1130 | |||
1131 | return (ret); | ||
589 | } | 1132 | } |
590 | 1133 | ||
591 | /* register a new provider, fails if provider already exists */ | 1134 | #ifdef WITH_PKCS11_KEYGEN |
592 | int | 1135 | #define FILL_ATTR(attr, idx, typ, val, len) \ |
593 | pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | 1136 | { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; } |
1137 | |||
1138 | static struct sshkey * | ||
1139 | pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
1140 | char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err) | ||
1141 | { | ||
1142 | struct pkcs11_slotinfo *si; | ||
1143 | char *plabel = label ? label : ""; | ||
1144 | int npub = 0, npriv = 0; | ||
1145 | CK_RV rv; | ||
1146 | CK_FUNCTION_LIST *f; | ||
1147 | CK_SESSION_HANDLE session; | ||
1148 | CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE; | ||
1149 | CK_OBJECT_HANDLE pubKey, privKey; | ||
1150 | CK_ATTRIBUTE tpub[16], tpriv[16]; | ||
1151 | CK_MECHANISM mech = { | ||
1152 | CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 | ||
1153 | }; | ||
1154 | CK_BYTE pubExponent[] = { | ||
1155 | 0x01, 0x00, 0x01 /* RSA_F4 in bytes */ | ||
1156 | }; | ||
1157 | pubkey_filter[0].pValue = &pubkey_class; | ||
1158 | cert_filter[0].pValue = &cert_class; | ||
1159 | |||
1160 | *err = 0; | ||
1161 | |||
1162 | FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val)); | ||
1163 | FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel)); | ||
1164 | FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val)); | ||
1165 | FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val)); | ||
1166 | FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val, | ||
1167 | sizeof(false_val)); | ||
1168 | FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val)); | ||
1169 | FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val)); | ||
1170 | FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits)); | ||
1171 | FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent, | ||
1172 | sizeof(pubExponent)); | ||
1173 | FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid)); | ||
1174 | |||
1175 | FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val)); | ||
1176 | FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel)); | ||
1177 | FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val)); | ||
1178 | FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val)); | ||
1179 | FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val)); | ||
1180 | FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val)); | ||
1181 | FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val, | ||
1182 | sizeof(false_val)); | ||
1183 | FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val)); | ||
1184 | FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val)); | ||
1185 | FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid)); | ||
1186 | |||
1187 | f = p->function_list; | ||
1188 | si = &p->slotinfo[slotidx]; | ||
1189 | session = si->session; | ||
1190 | |||
1191 | if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv, | ||
1192 | &pubKey, &privKey)) != CKR_OK) { | ||
1193 | error("%s: key generation failed: error 0x%lx", __func__, rv); | ||
1194 | *err = rv; | ||
1195 | return NULL; | ||
1196 | } | ||
1197 | |||
1198 | return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey); | ||
1199 | } | ||
1200 | |||
1201 | static int | ||
1202 | pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen) | ||
1203 | { | ||
1204 | size_t i, len; | ||
1205 | char ptr[3]; | ||
1206 | |||
1207 | if (dest) | ||
1208 | *dest = NULL; | ||
1209 | if (rlen) | ||
1210 | *rlen = 0; | ||
1211 | |||
1212 | if ((len = strlen(hex)) % 2) | ||
1213 | return -1; | ||
1214 | len /= 2; | ||
1215 | |||
1216 | *dest = xmalloc(len); | ||
1217 | |||
1218 | ptr[2] = '\0'; | ||
1219 | for (i = 0; i < len; i++) { | ||
1220 | ptr[0] = hex[2 * i]; | ||
1221 | ptr[1] = hex[(2 * i) + 1]; | ||
1222 | if (!isxdigit(ptr[0]) || !isxdigit(ptr[1])) | ||
1223 | return -1; | ||
1224 | (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16); | ||
1225 | } | ||
1226 | |||
1227 | if (rlen) | ||
1228 | *rlen = len; | ||
1229 | |||
1230 | return 0; | ||
1231 | } | ||
1232 | |||
1233 | static struct ec_curve_info { | ||
1234 | const char *name; | ||
1235 | const char *oid; | ||
1236 | const char *oid_encoded; | ||
1237 | size_t size; | ||
1238 | } ec_curve_infos[] = { | ||
1239 | {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, | ||
1240 | {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, | ||
1241 | {"secp521r1", "1.3.132.0.35", "06052B81040023", 521}, | ||
1242 | {NULL, NULL, NULL, 0}, | ||
1243 | }; | ||
1244 | |||
1245 | static struct sshkey * | ||
1246 | pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, | ||
1247 | char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err) | ||
1248 | { | ||
1249 | struct pkcs11_slotinfo *si; | ||
1250 | char *plabel = label ? label : ""; | ||
1251 | int i; | ||
1252 | size_t ecparams_size; | ||
1253 | unsigned char *ecparams = NULL; | ||
1254 | int npub = 0, npriv = 0; | ||
1255 | CK_RV rv; | ||
1256 | CK_FUNCTION_LIST *f; | ||
1257 | CK_SESSION_HANDLE session; | ||
1258 | CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE; | ||
1259 | CK_OBJECT_HANDLE pubKey, privKey; | ||
1260 | CK_MECHANISM mech = { | ||
1261 | CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0 | ||
1262 | }; | ||
1263 | CK_ATTRIBUTE tpub[16], tpriv[16]; | ||
1264 | |||
1265 | *err = 0; | ||
1266 | |||
1267 | for (i = 0; ec_curve_infos[i].name; i++) { | ||
1268 | if (ec_curve_infos[i].size == bits) | ||
1269 | break; | ||
1270 | } | ||
1271 | if (!ec_curve_infos[i].name) { | ||
1272 | error("%s: invalid key size %lu", __func__, bits); | ||
1273 | return NULL; | ||
1274 | } | ||
1275 | if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams, | ||
1276 | &ecparams_size) == -1) { | ||
1277 | error("%s: invalid oid", __func__); | ||
1278 | return NULL; | ||
1279 | } | ||
1280 | |||
1281 | FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val)); | ||
1282 | FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel)); | ||
1283 | FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val)); | ||
1284 | FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val)); | ||
1285 | FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val, | ||
1286 | sizeof(false_val)); | ||
1287 | FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val)); | ||
1288 | FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val)); | ||
1289 | FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size); | ||
1290 | FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid)); | ||
1291 | |||
1292 | FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val)); | ||
1293 | FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel)); | ||
1294 | FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val)); | ||
1295 | FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val)); | ||
1296 | FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val)); | ||
1297 | FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val)); | ||
1298 | FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val, | ||
1299 | sizeof(false_val)); | ||
1300 | FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val)); | ||
1301 | FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val)); | ||
1302 | FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid)); | ||
1303 | |||
1304 | f = p->function_list; | ||
1305 | si = &p->slotinfo[slotidx]; | ||
1306 | session = si->session; | ||
1307 | |||
1308 | if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv, | ||
1309 | &pubKey, &privKey)) != CKR_OK) { | ||
1310 | error("%s: key generation failed: error 0x%lx", __func__, rv); | ||
1311 | *err = rv; | ||
1312 | return NULL; | ||
1313 | } | ||
1314 | |||
1315 | return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey); | ||
1316 | } | ||
1317 | #endif /* WITH_PKCS11_KEYGEN */ | ||
1318 | |||
1319 | /* | ||
1320 | * register a new provider, fails if provider already exists. if | ||
1321 | * keyp is provided, fetch keys. | ||
1322 | */ | ||
1323 | static int | ||
1324 | pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp, | ||
1325 | struct pkcs11_provider **providerp, CK_ULONG user) | ||
594 | { | 1326 | { |
595 | int nkeys, need_finalize = 0; | 1327 | int nkeys, need_finalize = 0; |
1328 | int ret = -1; | ||
596 | struct pkcs11_provider *p = NULL; | 1329 | struct pkcs11_provider *p = NULL; |
597 | void *handle = NULL; | 1330 | void *handle = NULL; |
598 | CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); | 1331 | CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); |
@@ -601,13 +1334,19 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | |||
601 | CK_TOKEN_INFO *token; | 1334 | CK_TOKEN_INFO *token; |
602 | CK_ULONG i; | 1335 | CK_ULONG i; |
603 | 1336 | ||
604 | *keyp = NULL; | 1337 | if (providerp == NULL) |
1338 | goto fail; | ||
1339 | *providerp = NULL; | ||
1340 | |||
1341 | if (keyp != NULL) | ||
1342 | *keyp = NULL; | ||
1343 | |||
605 | if (pkcs11_provider_lookup(provider_id) != NULL) { | 1344 | if (pkcs11_provider_lookup(provider_id) != NULL) { |
606 | debug("%s: provider already registered: %s", | 1345 | debug("%s: provider already registered: %s", |
607 | __func__, provider_id); | 1346 | __func__, provider_id); |
608 | goto fail; | 1347 | goto fail; |
609 | } | 1348 | } |
610 | /* open shared pkcs11-libarary */ | 1349 | /* open shared pkcs11-library */ |
611 | if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { | 1350 | if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { |
612 | error("dlopen %s failed: %s", provider_id, dlerror()); | 1351 | error("dlopen %s failed: %s", provider_id, dlerror()); |
613 | goto fail; | 1352 | goto fail; |
@@ -653,8 +1392,9 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | |||
653 | goto fail; | 1392 | goto fail; |
654 | } | 1393 | } |
655 | if (p->nslots == 0) { | 1394 | if (p->nslots == 0) { |
656 | debug("%s: provider %s returned no slots", __func__, | 1395 | error("%s: provider %s returned no slots", __func__, |
657 | provider_id); | 1396 | provider_id); |
1397 | ret = -SSH_PKCS11_ERR_NO_SLOTS; | ||
658 | goto fail; | 1398 | goto fail; |
659 | } | 1399 | } |
660 | p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); | 1400 | p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); |
@@ -690,43 +1430,251 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | |||
690 | provider_id, (unsigned long)i, | 1430 | provider_id, (unsigned long)i, |
691 | token->label, token->manufacturerID, token->model, | 1431 | token->label, token->manufacturerID, token->model, |
692 | token->serialNumber, token->flags); | 1432 | token->serialNumber, token->flags); |
693 | /* open session, login with pin and retrieve public keys */ | 1433 | /* |
694 | if (pkcs11_open_session(p, i, pin) == 0) | 1434 | * open session, login with pin and retrieve public |
1435 | * keys (if keyp is provided) | ||
1436 | */ | ||
1437 | if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) { | ||
1438 | if (keyp == NULL) | ||
1439 | continue; | ||
695 | pkcs11_fetch_keys(p, i, keyp, &nkeys); | 1440 | pkcs11_fetch_keys(p, i, keyp, &nkeys); |
1441 | pkcs11_fetch_certs(p, i, keyp, &nkeys); | ||
1442 | } | ||
696 | } | 1443 | } |
697 | if (nkeys > 0) { | 1444 | |
698 | TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); | 1445 | /* now owned by caller */ |
699 | p->refcount++; /* add to provider list */ | 1446 | *providerp = p; |
700 | return (nkeys); | 1447 | |
701 | } | 1448 | TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); |
702 | debug("%s: provider %s returned no keys", __func__, provider_id); | 1449 | p->refcount++; /* add to provider list */ |
703 | /* don't add the provider, since it does not have any keys */ | 1450 | |
1451 | return (nkeys); | ||
704 | fail: | 1452 | fail: |
705 | if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) | 1453 | if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) |
706 | error("C_Finalize for provider %s failed: %lu", | 1454 | error("C_Finalize for provider %s failed: %lu", |
707 | provider_id, rv); | 1455 | provider_id, rv); |
708 | if (p) { | 1456 | if (p) { |
1457 | free(p->name); | ||
709 | free(p->slotlist); | 1458 | free(p->slotlist); |
710 | free(p->slotinfo); | 1459 | free(p->slotinfo); |
711 | free(p); | 1460 | free(p); |
712 | } | 1461 | } |
713 | if (handle) | 1462 | if (handle) |
714 | dlclose(handle); | 1463 | dlclose(handle); |
715 | return (-1); | 1464 | return (ret); |
716 | } | 1465 | } |
717 | 1466 | ||
718 | #else | 1467 | /* |
1468 | * register a new provider and get number of keys hold by the token, | ||
1469 | * fails if provider already exists | ||
1470 | */ | ||
1471 | int | ||
1472 | pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | ||
1473 | { | ||
1474 | struct pkcs11_provider *p = NULL; | ||
1475 | int nkeys; | ||
1476 | |||
1477 | nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER); | ||
719 | 1478 | ||
1479 | /* no keys found or some other error, de-register provider */ | ||
1480 | if (nkeys <= 0 && p != NULL) { | ||
1481 | TAILQ_REMOVE(&pkcs11_providers, p, next); | ||
1482 | pkcs11_provider_finalize(p); | ||
1483 | pkcs11_provider_unref(p); | ||
1484 | } | ||
1485 | if (nkeys == 0) | ||
1486 | debug("%s: provider %s returned no keys", __func__, | ||
1487 | provider_id); | ||
1488 | |||
1489 | return (nkeys); | ||
1490 | } | ||
1491 | |||
1492 | #ifdef WITH_PKCS11_KEYGEN | ||
1493 | struct sshkey * | ||
1494 | pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label, | ||
1495 | unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err) | ||
1496 | { | ||
1497 | struct pkcs11_provider *p = NULL; | ||
1498 | struct pkcs11_slotinfo *si; | ||
1499 | CK_FUNCTION_LIST *f; | ||
1500 | CK_SESSION_HANDLE session; | ||
1501 | struct sshkey *k = NULL; | ||
1502 | int ret = -1, reset_pin = 0, reset_provider = 0; | ||
1503 | CK_RV rv; | ||
1504 | |||
1505 | *err = 0; | ||
1506 | |||
1507 | if ((p = pkcs11_provider_lookup(provider_id)) != NULL) | ||
1508 | debug("%s: provider \"%s\" available", __func__, provider_id); | ||
1509 | else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p, | ||
1510 | CKU_SO)) < 0) { | ||
1511 | debug("%s: could not register provider %s", __func__, | ||
1512 | provider_id); | ||
1513 | goto out; | ||
1514 | } else | ||
1515 | reset_provider = 1; | ||
1516 | |||
1517 | f = p->function_list; | ||
1518 | si = &p->slotinfo[slotidx]; | ||
1519 | session = si->session; | ||
1520 | |||
1521 | if ((rv = f->C_SetOperationState(session , pin, strlen(pin), | ||
1522 | CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) { | ||
1523 | debug("%s: could not supply SO pin: %lu", __func__, rv); | ||
1524 | reset_pin = 0; | ||
1525 | } else | ||
1526 | reset_pin = 1; | ||
1527 | |||
1528 | switch (type) { | ||
1529 | case KEY_RSA: | ||
1530 | if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label, | ||
1531 | bits, keyid, err)) == NULL) { | ||
1532 | debug("%s: failed to generate RSA key", __func__); | ||
1533 | goto out; | ||
1534 | } | ||
1535 | break; | ||
1536 | case KEY_ECDSA: | ||
1537 | if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label, | ||
1538 | bits, keyid, err)) == NULL) { | ||
1539 | debug("%s: failed to generate ECDSA key", __func__); | ||
1540 | goto out; | ||
1541 | } | ||
1542 | break; | ||
1543 | default: | ||
1544 | *err = SSH_PKCS11_ERR_GENERIC; | ||
1545 | debug("%s: unknown type %d", __func__, type); | ||
1546 | goto out; | ||
1547 | } | ||
1548 | |||
1549 | out: | ||
1550 | if (reset_pin) | ||
1551 | f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE, | ||
1552 | CK_INVALID_HANDLE); | ||
1553 | |||
1554 | if (reset_provider) | ||
1555 | pkcs11_del_provider(provider_id); | ||
1556 | |||
1557 | return (k); | ||
1558 | } | ||
1559 | |||
1560 | struct sshkey * | ||
1561 | pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx, | ||
1562 | unsigned char keyid, u_int32_t *err) | ||
1563 | { | ||
1564 | struct pkcs11_provider *p = NULL; | ||
1565 | struct pkcs11_slotinfo *si; | ||
1566 | struct sshkey *k = NULL; | ||
1567 | int reset_pin = 0, reset_provider = 0; | ||
1568 | CK_ULONG nattrs; | ||
1569 | CK_FUNCTION_LIST *f; | ||
1570 | CK_SESSION_HANDLE session; | ||
1571 | CK_ATTRIBUTE attrs[16]; | ||
1572 | CK_OBJECT_CLASS key_class; | ||
1573 | CK_KEY_TYPE key_type; | ||
1574 | CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; | ||
1575 | CK_RV rv; | ||
1576 | |||
1577 | *err = 0; | ||
1578 | |||
1579 | if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { | ||
1580 | debug("%s: using provider \"%s\"", __func__, provider_id); | ||
1581 | } else if (pkcs11_register_provider(provider_id, pin, NULL, &p, | ||
1582 | CKU_SO) < 0) { | ||
1583 | debug("%s: could not register provider %s", __func__, | ||
1584 | provider_id); | ||
1585 | goto out; | ||
1586 | } else | ||
1587 | reset_provider = 1; | ||
1588 | |||
1589 | f = p->function_list; | ||
1590 | si = &p->slotinfo[slotidx]; | ||
1591 | session = si->session; | ||
1592 | |||
1593 | if ((rv = f->C_SetOperationState(session , pin, strlen(pin), | ||
1594 | CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) { | ||
1595 | debug("%s: could not supply SO pin: %lu", __func__, rv); | ||
1596 | reset_pin = 0; | ||
1597 | } else | ||
1598 | reset_pin = 1; | ||
1599 | |||
1600 | /* private key */ | ||
1601 | nattrs = 0; | ||
1602 | key_class = CKO_PRIVATE_KEY; | ||
1603 | FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class)); | ||
1604 | FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid)); | ||
1605 | |||
1606 | if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 && | ||
1607 | obj != CK_INVALID_HANDLE) { | ||
1608 | if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) { | ||
1609 | debug("%s: could not destroy private key 0x%hhx", | ||
1610 | __func__, keyid); | ||
1611 | *err = rv; | ||
1612 | goto out; | ||
1613 | } | ||
1614 | } | ||
1615 | |||
1616 | /* public key */ | ||
1617 | nattrs = 0; | ||
1618 | key_class = CKO_PUBLIC_KEY; | ||
1619 | FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class)); | ||
1620 | FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid)); | ||
1621 | |||
1622 | if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 && | ||
1623 | obj != CK_INVALID_HANDLE) { | ||
1624 | |||
1625 | /* get key type */ | ||
1626 | nattrs = 0; | ||
1627 | FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type, | ||
1628 | sizeof(key_type)); | ||
1629 | rv = f->C_GetAttributeValue(session, obj, attrs, nattrs); | ||
1630 | if (rv != CKR_OK) { | ||
1631 | debug("%s: could not get key type of public key 0x%hhx", | ||
1632 | __func__, keyid); | ||
1633 | *err = rv; | ||
1634 | key_type = -1; | ||
1635 | } | ||
1636 | if (key_type == CKK_RSA) | ||
1637 | k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj); | ||
1638 | else if (key_type == CKK_ECDSA) | ||
1639 | k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj); | ||
1640 | |||
1641 | if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) { | ||
1642 | debug("%s: could not destroy public key 0x%hhx", | ||
1643 | __func__, keyid); | ||
1644 | *err = rv; | ||
1645 | goto out; | ||
1646 | } | ||
1647 | } | ||
1648 | |||
1649 | out: | ||
1650 | if (reset_pin) | ||
1651 | f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE, | ||
1652 | CK_INVALID_HANDLE); | ||
1653 | |||
1654 | if (reset_provider) | ||
1655 | pkcs11_del_provider(provider_id); | ||
1656 | |||
1657 | return (k); | ||
1658 | } | ||
1659 | #endif /* WITH_PKCS11_KEYGEN */ | ||
1660 | #else /* HAVE_DLOPEN */ | ||
720 | int | 1661 | int |
721 | pkcs11_init(int interactive) | 1662 | pkcs11_init(int interactive) |
722 | { | 1663 | { |
723 | return (0); | 1664 | error("%s: dlopen() not supported", __func__); |
1665 | return (-1); | ||
1666 | } | ||
1667 | |||
1668 | int | ||
1669 | pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) | ||
1670 | { | ||
1671 | error("%s: dlopen() not supported", __func__); | ||
1672 | return (-1); | ||
724 | } | 1673 | } |
725 | 1674 | ||
726 | void | 1675 | void |
727 | pkcs11_terminate(void) | 1676 | pkcs11_terminate(void) |
728 | { | 1677 | { |
729 | return; | 1678 | error("%s: dlopen() not supported", __func__); |
730 | } | 1679 | } |
731 | 1680 | #endif /* HAVE_DLOPEN */ | |
732 | #endif /* ENABLE_PKCS11 */ | ||
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h index 0ced74f29..b9038450d 100644 --- a/ssh-pkcs11.h +++ b/ssh-pkcs11.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-pkcs11.h,v 1.4 2015/01/15 09:40:00 djm Exp $ */ | 1 | /* $OpenBSD: ssh-pkcs11.h,v 1.5 2019/01/20 22:51:37 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2010 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -14,10 +14,26 @@ | |||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | */ | 16 | */ |
17 | |||
18 | /* Errors for pkcs11_add_provider() */ | ||
19 | #define SSH_PKCS11_ERR_GENERIC 1 | ||
20 | #define SSH_PKCS11_ERR_LOGIN_FAIL 2 | ||
21 | #define SSH_PKCS11_ERR_NO_SLOTS 3 | ||
22 | #define SSH_PKCS11_ERR_PIN_REQUIRED 4 | ||
23 | #define SSH_PKCS11_ERR_PIN_LOCKED 5 | ||
24 | |||
17 | int pkcs11_init(int); | 25 | int pkcs11_init(int); |
18 | void pkcs11_terminate(void); | 26 | void pkcs11_terminate(void); |
19 | int pkcs11_add_provider(char *, char *, struct sshkey ***); | 27 | int pkcs11_add_provider(char *, char *, struct sshkey ***); |
20 | int pkcs11_del_provider(char *); | 28 | int pkcs11_del_provider(char *); |
29 | #ifdef WITH_PKCS11_KEYGEN | ||
30 | struct sshkey * | ||
31 | pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int, | ||
32 | unsigned int, unsigned char, u_int32_t *); | ||
33 | struct sshkey * | ||
34 | pkcs11_destroy_keypair(char *, char *, unsigned long, unsigned char, | ||
35 | u_int32_t *); | ||
36 | #endif | ||
21 | 37 | ||
22 | #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) | 38 | #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) |
23 | #undef ENABLE_PKCS11 | 39 | #undef ENABLE_PKCS11 |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshkey.h,v 1.30 2018/09/14 04:17:44 djm Exp $ */ | 1 | /* $OpenBSD: sshkey.h,v 1.31 2019/01/20 22:51:37 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. |
@@ -33,6 +33,7 @@ | |||
33 | #include <openssl/dsa.h> | 33 | #include <openssl/dsa.h> |
34 | # ifdef OPENSSL_HAS_ECC | 34 | # ifdef OPENSSL_HAS_ECC |
35 | # include <openssl/ec.h> | 35 | # include <openssl/ec.h> |
36 | # include <openssl/ecdsa.h> | ||
36 | # else /* OPENSSL_HAS_ECC */ | 37 | # else /* OPENSSL_HAS_ECC */ |
37 | # define EC_KEY void | 38 | # define EC_KEY void |
38 | # define EC_GROUP void | 39 | # define EC_GROUP void |