Do not try to log in if we already have a user session
[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 * Find a PIN and try to log in
335 */
336 static bool login(private_pkcs11_private_key_t *this, chunk_t keyid, int slot)
337 {
338 enumerator_t *enumerator;
339 identification_t *id;
340 shared_key_t *shared;
341 chunk_t pin;
342 CK_RV rv;
343 CK_SESSION_INFO info;
344 bool found = FALSE, success = FALSE;
345
346 rv = this->lib->f->C_GetSessionInfo(this->session, &info);
347 if (rv != CKR_OK)
348 {
349 DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv);
350 return FALSE;
351 }
352 if (info.state != CKS_RO_PUBLIC_SESSION &&
353 info.state != CKS_RW_PUBLIC_SESSION)
354 { /* already logged in with another session, skip */
355 return TRUE;
356 }
357
358 id = identification_create_from_encoding(ID_KEY_ID, keyid);
359 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
360 SHARED_PIN, id, NULL);
361 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
362 {
363 found = TRUE;
364 pin = shared->get_key(shared);
365 rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
366 if (rv == CKR_OK)
367 {
368 success = TRUE;
369 break;
370 }
371 DBG1(DBG_CFG, "login to '%s':%d failed: %N",
372 this->lib->get_name(this->lib), slot, ck_rv_names, rv);
373 }
374 enumerator->destroy(enumerator);
375 id->destroy(id);
376
377 if (!found)
378 {
379 DBG1(DBG_CFG, "no PIN found for PKCS#11 key %#B", &keyid);
380 return FALSE;
381 }
382 return success;
383 }
384
385 /**
386 * See header.
387 */
388 pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
389 {
390 private_pkcs11_private_key_t *this;
391 char *module = NULL;
392 chunk_t keyid = chunk_empty;
393 int slot = -1;
394 CK_RV rv;
395
396 while (TRUE)
397 {
398 switch (va_arg(args, builder_part_t))
399 {
400 case BUILD_PKCS11_KEYID:
401 keyid = va_arg(args, chunk_t);
402 continue;
403 case BUILD_PKCS11_SLOT:
404 slot = va_arg(args, int);
405 continue;
406 case BUILD_PKCS11_MODULE:
407 module = va_arg(args, char*);
408 continue;
409 case BUILD_END:
410 break;
411 default:
412 return NULL;
413 }
414 break;
415 }
416 if (!keyid.len)
417 {
418 return NULL;
419 }
420
421 INIT(this,
422 .public.key = {
423 .get_type = _get_type,
424 .sign = _sign,
425 .decrypt = _decrypt,
426 .get_keysize = _get_keysize,
427 .get_public_key = _get_public_key,
428 .equals = private_key_equals,
429 .belongs_to = private_key_belongs_to,
430 .get_fingerprint = _get_fingerprint,
431 .has_fingerprint = private_key_has_fingerprint,
432 .get_encoding = _get_encoding,
433 .get_ref = _get_ref,
434 .destroy = _destroy,
435 },
436 .ref = 1,
437 );
438
439 if (module && slot != -1)
440 {
441 this->lib = find_lib(module);
442 if (!this->lib)
443 {
444 DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module);
445 free(this);
446 return NULL;
447 }
448 }
449 else
450 {
451 this->lib = find_lib_by_keyid(keyid, &slot);
452 if (!this->lib)
453 {
454 DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid);
455 free(this);
456 return NULL;
457 }
458 }
459
460 rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
461 NULL, NULL, &this->session);
462 if (rv != CKR_OK)
463 {
464 DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N",
465 module, slot, ck_rv_names, rv);
466 free(this);
467 return NULL;
468 }
469
470 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
471
472 if (!login(this, keyid, slot))
473 {
474 destroy(this);
475 return NULL;
476 }
477
478 if (!find_key(this, keyid))
479 {
480 destroy(this);
481 return NULL;
482 }
483
484 return &this->public;
485 }