Implemented callback PIN invocation for PKCS#11 login
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_private_key.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pkcs11_private_key.h"
17
18 #include "pkcs11_library.h"
19 #include "pkcs11_manager.h"
20
21 #include <debug.h>
22 #include <threading/mutex.h>
23
24 typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t;
25
26 /**
27 * Private data of an pkcs11_private_key_t object.
28 */
29 struct private_pkcs11_private_key_t {
30
31 /**
32 * Public pkcs11_private_key_t interface.
33 */
34 pkcs11_private_key_t public;
35
36 /**
37 * PKCS#11 module
38 */
39 pkcs11_library_t *lib;
40
41 /**
42 * Token session
43 */
44 CK_SESSION_HANDLE session;
45
46 /**
47 * Mutex to lock session
48 */
49 mutex_t *mutex;
50
51 /**
52 * Key object on the token
53 */
54 CK_OBJECT_HANDLE object;
55
56 /**
57 * Associated public key
58 */
59 public_key_t *pubkey;
60
61 /**
62 * References to this key
63 */
64 refcount_t ref;
65 };
66
67 METHOD(private_key_t, get_type, key_type_t,
68 private_pkcs11_private_key_t *this)
69 {
70 return this->pubkey->get_type(this->pubkey);
71 }
72
73 METHOD(private_key_t, get_keysize, size_t,
74 private_pkcs11_private_key_t *this)
75 {
76 return this->pubkey->get_keysize(this->pubkey);
77 }
78
79 /**
80 * Get the Cryptoki mechanism for a signature scheme
81 */
82 static CK_MECHANISM_PTR scheme_to_mechanism(signature_scheme_t scheme)
83 {
84 static struct {
85 signature_scheme_t scheme;
86 CK_MECHANISM mechanism;
87 } mappings[] = {
88 {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}},
89 {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}},
90 {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}},
91 {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}},
92 {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}},
93 {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}},
94 };
95 int i;
96
97 for (i = 0; i < countof(mappings); i++)
98 {
99 if (mappings[i].scheme == scheme)
100 {
101 return &mappings[i].mechanism;
102 }
103 }
104 return NULL;
105 }
106
107 METHOD(private_key_t, sign, bool,
108 private_pkcs11_private_key_t *this, signature_scheme_t scheme,
109 chunk_t data, chunk_t *signature)
110 {
111 CK_MECHANISM_PTR mechanism;
112 CK_BYTE_PTR buf;
113 CK_ULONG len;
114 CK_RV rv;
115
116 mechanism = scheme_to_mechanism(scheme);
117 if (!mechanism)
118 {
119 DBG1(DBG_LIB, "signature scheme %N not supported",
120 signature_scheme_names, scheme);
121 return FALSE;
122 }
123 this->mutex->lock(this->mutex);
124 rv = this->lib->f->C_SignInit(this->session, mechanism, this->object);
125 if (rv != CKR_OK)
126 {
127 this->mutex->unlock(this->mutex);
128 DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
129 return FALSE;
130 }
131 buf = malloc(get_keysize(this));
132 rv = this->lib->f->C_Sign(this->session, data.ptr, data.len, buf, &len);
133 this->mutex->unlock(this->mutex);
134 if (rv != CKR_OK)
135 {
136 DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
137 free(buf);
138 return FALSE;
139 }
140 *signature = chunk_create(buf, len);
141 return TRUE;
142 }
143
144 METHOD(private_key_t, decrypt, bool,
145 private_pkcs11_private_key_t *this, chunk_t crypto, chunk_t *plain)
146 {
147 return FALSE;
148 }
149
150 METHOD(private_key_t, get_public_key, public_key_t*,
151 private_pkcs11_private_key_t *this)
152 {
153 return this->pubkey->get_ref(this->pubkey);
154 }
155
156 METHOD(private_key_t, get_fingerprint, bool,
157 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
158 chunk_t *fingerprint)
159 {
160 return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint);
161 }
162
163 METHOD(private_key_t, get_encoding, bool,
164 private_pkcs11_private_key_t *this, cred_encoding_type_t type,
165 chunk_t *encoding)
166 {
167 return FALSE;
168 }
169
170 METHOD(private_key_t, get_ref, private_key_t*,
171 private_pkcs11_private_key_t *this)
172 {
173 ref_get(&this->ref);
174 return &this->public.key;
175 }
176
177 METHOD(private_key_t, destroy, void,
178 private_pkcs11_private_key_t *this)
179 {
180 if (ref_put(&this->ref))
181 {
182 if (this->pubkey)
183 {
184 this->pubkey->destroy(this->pubkey);
185 }
186 this->mutex->destroy(this->mutex);
187 this->lib->f->C_CloseSession(this->session);
188 free(this);
189 }
190 }
191
192 /**
193 * Find the PKCS#11 library by its friendly name
194 */
195 static pkcs11_library_t* find_lib(char *module)
196 {
197 pkcs11_manager_t *manager;
198 enumerator_t *enumerator;
199 pkcs11_library_t *p11, *found = NULL;
200 CK_SLOT_ID slot;
201
202 manager = pkcs11_manager_get();
203 if (!manager)
204 {
205 return NULL;
206 }
207 enumerator = manager->create_token_enumerator(manager);
208 while (enumerator->enumerate(enumerator, &p11, &slot))
209 {
210 if (streq(module, p11->get_name(p11)))
211 {
212 found = p11;
213 break;
214 }
215 }
216 enumerator->destroy(enumerator);
217 return found;
218 }
219
220 /**
221 * Find the PKCS#11 lib having a keyid, and optionally a slot
222 */
223 static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
224 {
225 pkcs11_manager_t *manager;
226 enumerator_t *enumerator;
227 pkcs11_library_t *p11, *found = NULL;
228 CK_SLOT_ID current;
229
230 manager = pkcs11_manager_get();
231 if (!manager)
232 {
233 return NULL;
234 }
235 enumerator = manager->create_token_enumerator(manager);
236 while (enumerator->enumerate(enumerator, &p11, &current))
237 {
238 if (*slot == -1 || *slot == current)
239 {
240 /* we look for a public key, it is usually readable without login */
241 CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
242 CK_ATTRIBUTE tmpl[] = {
243 {CKA_CLASS, &class, sizeof(class)},
244 {CKA_ID, keyid.ptr, keyid.len},
245 };
246 CK_OBJECT_HANDLE object;
247 CK_SESSION_HANDLE session;
248 CK_RV rv;
249 enumerator_t *keys;
250
251 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
252 &session);
253 if (rv != CKR_OK)
254 {
255 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N",
256 ck_rv_names, rv);
257 continue;
258 }
259 keys = p11->create_object_enumerator(p11, session,
260 tmpl, countof(tmpl), NULL, 0);
261 if (keys->enumerate(keys, &object))
262 {
263 DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d",
264 p11->get_name(p11), current);
265 found = p11;
266 *slot = current;
267 }
268 keys->destroy(keys);
269 p11->f->C_CloseSession(session);
270 if (found)
271 {
272 break;
273 }
274 }
275 }
276 enumerator->destroy(enumerator);
277 return found;
278 }
279
280 /**
281 * Find the key on the token
282 */
283 static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
284 {
285 CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
286 CK_ATTRIBUTE tmpl[] = {
287 {CKA_CLASS, &class, sizeof(class)},
288 {CKA_ID, keyid.ptr, keyid.len},
289 };
290 CK_OBJECT_HANDLE object;
291 CK_KEY_TYPE type;
292 CK_ATTRIBUTE attr[] = {
293 {CKA_KEY_TYPE, &type, sizeof(type)},
294 {CKA_MODULUS, NULL, 0},
295 {CKA_PUBLIC_EXPONENT, NULL, 0},
296 };
297 enumerator_t *enumerator;
298 chunk_t modulus, pubexp;
299
300 enumerator = this->lib->create_object_enumerator(this->lib,
301 this->session, tmpl, countof(tmpl), attr, countof(attr));
302 if (enumerator->enumerate(enumerator, &object))
303 {
304 switch (type)
305 {
306 case CKK_RSA:
307 if (attr[0].ulValueLen == -1 || attr[1].ulValueLen == -1)
308 {
309 DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
310 break;
311 }
312 modulus = chunk_create(attr[1].pValue, attr[1].ulValueLen);
313 pubexp = chunk_create(attr[2].pValue, attr[2].ulValueLen);
314 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
315 KEY_RSA, BUILD_RSA_MODULUS, modulus,
316 BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
317 if (!this->pubkey)
318 {
319 DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
320 "private key failed");
321 }
322 this->object = object;
323 break;
324 default:
325 DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
326 break;
327 }
328 }
329 enumerator->destroy(enumerator);
330 return this->pubkey != NULL;
331 }
332
333 /**
334 * Try a login to the session
335 */
336 static bool login(private_pkcs11_private_key_t *this, chunk_t pin, int slot)
337 {
338 CK_RV rv;
339
340 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
341 if (rv != CKR_OK)
342 {
343 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
344 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
345 return FALSE;
346 }
347 return TRUE;
348 }
349
350 /**
351 * See header.
352 */
353 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
354 {
355 private_pkcs11_private_key_t *this;
356 chunk_t (*cb)(void *data, int try) = NULL;
357 void *cb_data = NULL;
358 char *module = NULL;
359 chunk_t keyid, pin;
360 int slot = -1, try = 0;
361 CK_RV rv;
362
363 keyid = pin = chunk_empty;
364 while (TRUE)
365 {
366 switch (va_arg(args, builder_part_t))
367 {
368 case BUILD_PKCS11_KEYID:
369 keyid = va_arg(args, chunk_t);
370 continue;
371 case BUILD_PASSPHRASE:
372 pin = va_arg(args, chunk_t);
373 continue;
374 case BUILD_PASSPHRASE_CALLBACK:
375 cb = va_arg(args, void*);
376 cb_data = va_arg(args, void*);
377 continue;
378 case BUILD_PKCS11_SLOT:
379 slot = va_arg(args, int);
380 continue;
381 case BUILD_PKCS11_MODULE:
382 module = va_arg(args, char*);
383 continue;
384 case BUILD_END:
385 break;
386 default:
387 return NULL;
388 }
389 break;
390 }
391 if (!keyid.len || (!pin.len && !cb))
392 {
393 return NULL;
394 }
395
396 INIT(this,
397 .public.key = {
398 .get_type = _get_type,
399 .sign = _sign,
400 .decrypt = _decrypt,
401 .get_keysize = _get_keysize,
402 .get_public_key = _get_public_key,
403 .equals = private_key_equals,
404 .belongs_to = private_key_belongs_to,
405 .get_fingerprint = _get_fingerprint,
406 .has_fingerprint = private_key_has_fingerprint,
407 .get_encoding = _get_encoding,
408 .get_ref = _get_ref,
409 .destroy = _destroy,
410 },
411 .ref = 1,
412 );
413
414 if (module && slot != -1)
415 {
416 this->lib = find_lib(module);
417 if (!this->lib)
418 {
419 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
420 free(this);
421 return NULL;
422 }
423 }
424 else
425 {
426 this->lib = find_lib_by_keyid(keyid, &slot);
427 if (!this->lib)
428 {
429 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
430 free(this);
431 return NULL;
432 }
433 }
434
435 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
436 NULL, NULL, &this->session);
437 if (rv != CKR_OK)
438 {
439 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
440 module, slot, ck_rv_names, rv);
441 free(this);
442 return NULL;
443 }
444
445 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
446
447 if (pin.ptr)
448 {
449 if (!login(this, pin, slot))
450 {
451 destroy(this);
452 return NULL;
453 }
454 }
455 else
456 {
457 while (TRUE)
458 {
459 pin = cb(cb_data, ++try);
460 if (!pin.len)
461 {
462 destroy(this);
463 return NULL;
464 }
465 if (login(this, pin, slot))
466 {
467 break;
468 }
469 }
470 }
471
472 if (!find_key(this, keyid))
473 {
474 destroy(this);
475 return NULL;
476 }
477
478 return &this->public;
479 }