1 /*
2 * pkcs11-session.c: PKCS#11 functions for session management
3 *
4 * Copyright (C) 2001 Timo Teräs <timo.teras@iki.fi>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "config.h"
22
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "sc-pkcs11.h"
29
get_session(CK_SESSION_HANDLE hSession,struct sc_pkcs11_session ** session)30 CK_RV get_session(CK_SESSION_HANDLE hSession, struct sc_pkcs11_session **session)
31 {
32 *session = list_seek(&sessions, &hSession);
33 if (!*session)
34 return CKR_SESSION_HANDLE_INVALID;
35 return CKR_OK;
36 }
37
C_OpenSession(CK_SLOT_ID slotID,CK_FLAGS flags,CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession)38 CK_RV C_OpenSession(CK_SLOT_ID slotID, /* the slot's ID */
39 CK_FLAGS flags, /* defined in CK_SESSION_INFO */
40 CK_VOID_PTR pApplication, /* pointer passed to callback */
41 CK_NOTIFY Notify, /* notification callback function */
42 CK_SESSION_HANDLE_PTR phSession)
43 { /* receives new session handle */
44 CK_RV rv;
45 struct sc_pkcs11_slot *slot;
46 struct sc_pkcs11_session *session;
47
48 if (!(flags & CKF_SERIAL_SESSION))
49 return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
50
51 if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION))
52 return CKR_ARGUMENTS_BAD;
53
54 rv = sc_pkcs11_lock();
55 if (rv != CKR_OK)
56 return rv;
57
58 sc_log(context, "C_OpenSession(0x%lx)", slotID);
59
60 rv = slot_get_token(slotID, &slot);
61 if (rv != CKR_OK)
62 goto out;
63
64 /* Check that no conflicting sessions exist */
65 if (!(flags & CKF_RW_SESSION) && (slot->login_user == CKU_SO)) {
66 rv = CKR_SESSION_READ_WRITE_SO_EXISTS;
67 goto out;
68 }
69
70 session = (struct sc_pkcs11_session *)calloc(1, sizeof(struct sc_pkcs11_session));
71 if (session == NULL) {
72 rv = CKR_HOST_MEMORY;
73 goto out;
74 }
75
76 /* make session handle from pointer and check its uniqueness */
77 session->handle = (CK_SESSION_HANDLE)(uintptr_t)session;
78 if (list_seek(&sessions, &session->handle) != NULL) {
79 sc_log(context, "C_OpenSession handle 0x%lx already exists", session->handle);
80
81 free(session);
82
83 rv = CKR_HOST_MEMORY;
84 goto out;
85 }
86
87 session->slot = slot;
88 session->notify_callback = Notify;
89 session->notify_data = pApplication;
90 session->flags = flags;
91 slot->nsessions++;
92 list_append(&sessions, session);
93 *phSession = session->handle;
94 sc_log(context, "C_OpenSession handle: 0x%lx", session->handle);
95
96 out:
97 sc_log(context, "C_OpenSession() = %s", lookup_enum(RV_T, rv));
98 sc_pkcs11_unlock();
99 return rv;
100 }
101
102 /* Internal version of C_CloseSession that gets called with
103 * the global lock held */
sc_pkcs11_close_session(CK_SESSION_HANDLE hSession)104 static CK_RV sc_pkcs11_close_session(CK_SESSION_HANDLE hSession)
105 {
106 struct sc_pkcs11_slot *slot;
107 struct sc_pkcs11_session *session;
108
109 sc_log(context, "real C_CloseSession(0x%lx)", hSession);
110
111 session = list_seek(&sessions, &hSession);
112 if (!session)
113 return CKR_SESSION_HANDLE_INVALID;
114
115 /* If we're the last session using this slot, make sure
116 * we log out */
117 slot = session->slot;
118 slot->nsessions--;
119 if (slot->nsessions == 0 && slot->login_user >= 0) {
120 slot->login_user = -1;
121 if (sc_pkcs11_conf.atomic)
122 pop_all_login_states(slot);
123 else {
124 if (slot->p11card == NULL)
125 return CKR_TOKEN_NOT_RECOGNIZED;
126 slot->p11card->framework->logout(slot);
127 }
128 }
129
130 if (list_delete(&sessions, session) != 0)
131 sc_log(context, "Could not delete session from list!");
132 free(session);
133 return CKR_OK;
134 }
135
136 /* Internal version of C_CloseAllSessions that gets called with
137 * the global lock held */
sc_pkcs11_close_all_sessions(CK_SLOT_ID slotID)138 CK_RV sc_pkcs11_close_all_sessions(CK_SLOT_ID slotID)
139 {
140 CK_RV rv = CKR_OK, error;
141 struct sc_pkcs11_session *session;
142 unsigned int i;
143 sc_log(context, "real C_CloseAllSessions(0x%lx) %d", slotID, list_size(&sessions));
144 for (i = 0; i < list_size(&sessions); i++) {
145 session = list_get_at(&sessions, i);
146 if (session->slot->id == slotID)
147 if ((error = sc_pkcs11_close_session(session->handle)) != CKR_OK)
148 rv = error;
149 }
150 return rv;
151 }
152
C_CloseSession(CK_SESSION_HANDLE hSession)153 CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
154 { /* the session's handle */
155 CK_RV rv;
156
157 rv = sc_pkcs11_lock();
158 if (rv != CKR_OK)
159 return rv;
160
161 sc_log(context, "C_CloseSession(0x%lx)", hSession);
162
163 rv = sc_pkcs11_close_session(hSession);
164
165 sc_pkcs11_unlock();
166 return rv;
167 }
168
C_CloseAllSessions(CK_SLOT_ID slotID)169 CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
170 { /* the token's slot */
171 CK_RV rv;
172 struct sc_pkcs11_slot *slot;
173
174 rv = sc_pkcs11_lock();
175 if (rv != CKR_OK)
176 return rv;
177
178 sc_log(context, "C_CloseAllSessions(0x%lx)", slotID);
179
180 rv = slot_get_token(slotID, &slot);
181 if (rv != CKR_OK)
182 goto out;
183
184 rv = sc_pkcs11_close_all_sessions(slotID);
185
186 out:
187 sc_pkcs11_unlock();
188 return rv;
189 }
190
191 /* PKCS #11 3.0 only */
C_SessionCancel(CK_SESSION_HANDLE hSession,CK_FLAGS flags)192 CK_RV C_SessionCancel(CK_SESSION_HANDLE hSession, /* the session's handle */
193 CK_FLAGS flags) /* flags control which sessions are cancelled */
194 {
195 struct sc_pkcs11_session *session;
196 CK_RV rv;
197
198 rv = sc_pkcs11_lock();
199 if (rv != CKR_OK)
200 return rv;
201
202 rv = get_session(hSession, &session);
203 if (rv != CKR_OK)
204 goto out;
205
206 /* Ignore return value of the cancel operation as it is valid to
207 * cancel not started operation and it can not fail for other reasons */
208 if (flags & CKF_ENCRYPT) {
209 /* unused */
210 }
211 if (flags & CKF_DECRYPT) {
212 session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT);
213 }
214 if (flags & CKF_DIGEST) {
215 session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST);
216 }
217 if (flags & CKF_SIGN) {
218 session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
219 }
220 if (flags & CKF_SIGN_RECOVER) {
221 /* unused */
222 }
223 if (flags & CKF_VERIFY) {
224 session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY);
225 }
226 if (flags & CKF_VERIFY_RECOVER) {
227 /* unused */
228 }
229 if (flags & CKF_GENERATE || flags & CKF_GENERATE_KEY_PAIR) {
230 /* unused */
231 }
232 if (flags & CKF_WRAP) {
233 session_stop_operation(session, SC_PKCS11_OPERATION_WRAP);
234 }
235 if (flags & CKF_UNWRAP) {
236 session_stop_operation(session, SC_PKCS11_OPERATION_UNWRAP);
237 }
238 if (flags & CKF_DERIVE) {
239 session_stop_operation(session, SC_PKCS11_OPERATION_DERIVE);
240 }
241
242 out:
243 sc_pkcs11_unlock();
244 return rv;
245 }
246
C_GetSessionInfo(CK_SESSION_HANDLE hSession,CK_SESSION_INFO_PTR pInfo)247 CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, /* the session's handle */
248 CK_SESSION_INFO_PTR pInfo)
249 { /* receives session information */
250 CK_RV rv;
251 struct sc_pkcs11_session *session;
252 struct sc_pkcs11_slot *slot;
253 int logged_out;
254
255 if (pInfo == NULL_PTR)
256 return CKR_ARGUMENTS_BAD;
257
258 rv = sc_pkcs11_lock();
259 if (rv != CKR_OK)
260 return rv;
261
262 sc_log(context, "C_GetSessionInfo(hSession:0x%lx)", hSession);
263
264 session = list_seek(&sessions, &hSession);
265 if (!session) {
266 rv = CKR_SESSION_HANDLE_INVALID;
267 goto out;
268 }
269
270 sc_log(context, "C_GetSessionInfo(slot:0x%lx)", session->slot->id);
271 pInfo->slotID = session->slot->id;
272 pInfo->flags = session->flags;
273 pInfo->ulDeviceError = 0;
274
275 slot = session->slot;
276 logged_out = (slot_get_logged_in_state(slot) == SC_PIN_STATE_LOGGED_OUT);
277 if (logged_out && slot->login_user >= 0) {
278 slot->login_user = -1;
279 sc_pkcs11_close_all_sessions(session->slot->id);
280 rv = CKR_SESSION_HANDLE_INVALID;
281 goto out;
282 }
283 if (slot->login_user == CKU_SO && !logged_out) {
284 pInfo->state = CKS_RW_SO_FUNCTIONS;
285 } else if ((slot->login_user == CKU_USER && !logged_out) || (!(slot->token_info.flags & CKF_LOGIN_REQUIRED))) {
286 pInfo->state = (session->flags & CKF_RW_SESSION)
287 ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS;
288 } else {
289 pInfo->state = (session->flags & CKF_RW_SESSION)
290 ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION;
291 }
292
293 out:
294 sc_log(context, "C_GetSessionInfo(0x%lx) = %s", hSession, lookup_enum(RV_T, rv));
295 sc_pkcs11_unlock();
296 return rv;
297 }
298
C_GetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG_PTR pulOperationStateLen)299 CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */
300 CK_BYTE_PTR pOperationState, /* location receiving state */
301 CK_ULONG_PTR pulOperationStateLen)
302 { /* location receiving state length */
303 return CKR_FUNCTION_NOT_SUPPORTED;
304 }
305
C_SetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG ulOperationStateLen,CK_OBJECT_HANDLE hEncryptionKey,CK_OBJECT_HANDLE hAuthenticationKey)306 CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */
307 CK_BYTE_PTR pOperationState, /* the location holding the state */
308 CK_ULONG ulOperationStateLen, /* location holding state length */
309 CK_OBJECT_HANDLE hEncryptionKey, /* handle of en/decryption key */
310 CK_OBJECT_HANDLE hAuthenticationKey)
311 { /* handle of sign/verify key */
312 return CKR_FUNCTION_NOT_SUPPORTED;
313 }
314
C_Login(CK_SESSION_HANDLE hSession,CK_USER_TYPE userType,CK_CHAR_PTR pPin,CK_ULONG ulPinLen)315 CK_RV C_Login(CK_SESSION_HANDLE hSession, /* the session's handle */
316 CK_USER_TYPE userType, /* the user type */
317 CK_CHAR_PTR pPin, /* the user's PIN */
318 CK_ULONG ulPinLen)
319 { /* the length of the PIN */
320 CK_RV rv;
321 struct sc_pkcs11_session *session;
322 struct sc_pkcs11_slot *slot;
323
324 if (pPin == NULL_PTR && ulPinLen > 0)
325 return CKR_ARGUMENTS_BAD;
326
327 rv = sc_pkcs11_lock();
328 if (rv != CKR_OK)
329 return rv;
330
331 if (userType != CKU_USER && userType != CKU_SO && userType != CKU_CONTEXT_SPECIFIC) {
332 rv = CKR_USER_TYPE_INVALID;
333 goto out;
334 }
335 session = list_seek(&sessions, &hSession);
336 if (!session) {
337 rv = CKR_SESSION_HANDLE_INVALID;
338 goto out;
339 }
340
341 sc_log(context, "C_Login(0x%lx, %lu)", hSession, userType);
342
343 slot = session->slot;
344
345 if (!(slot->token_info.flags & CKF_USER_PIN_INITIALIZED) && userType == CKU_USER) {
346 rv = CKR_USER_PIN_NOT_INITIALIZED;
347 goto out;
348 }
349
350 /* TODO: check if context specific is valid */
351 if (userType == CKU_CONTEXT_SPECIFIC) {
352 if (slot->login_user == -1) {
353 rv = CKR_OPERATION_NOT_INITIALIZED;
354 }
355 else {
356 rv = restore_login_state(slot);
357 if (rv == CKR_OK && slot->p11card && slot->p11card->framework)
358 rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen);
359 rv = reset_login_state(slot, rv);
360 }
361 }
362 else {
363 sc_log(context, "C_Login() slot->login_user %i", slot->login_user);
364 if (slot->login_user >= 0) {
365 if ((CK_USER_TYPE) slot->login_user == userType)
366 rv = CKR_USER_ALREADY_LOGGED_IN;
367 else
368 rv = CKR_USER_ANOTHER_ALREADY_LOGGED_IN;
369 goto out;
370 }
371
372 rv = restore_login_state(slot);
373 if (rv == CKR_OK) {
374 sc_log(context, "C_Login() userType %li", userType);
375 if (slot->p11card == NULL)
376 return CKR_TOKEN_NOT_RECOGNIZED;
377 rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen);
378 sc_log(context, "fLogin() rv %li", rv);
379 }
380 if (rv == CKR_OK)
381 rv = push_login_state(slot, userType, pPin, ulPinLen);
382 if (rv == CKR_OK) {
383 slot->login_user = (int) userType;
384 }
385 rv = reset_login_state(slot, rv);
386 }
387
388 out:
389 sc_pkcs11_unlock();
390 return rv;
391 }
392
393 /* PKCS #11 3.0 only */
C_LoginUser(CK_SESSION_HANDLE hSession,CK_USER_TYPE userType,CK_CHAR_PTR pPin,CK_ULONG ulPinLen,CK_UTF8CHAR_PTR pUsername,CK_ULONG ulUsernameLen)394 CK_RV C_LoginUser(CK_SESSION_HANDLE hSession, /* the session's handle */
395 CK_USER_TYPE userType, /* the user type */
396 CK_CHAR_PTR pPin, /* the user's PIN */
397 CK_ULONG ulPinLen, /* the length of the PIN */
398 CK_UTF8CHAR_PTR pUsername, /* the user's name */
399 CK_ULONG ulUsernameLen) /*the length of the user's name */
400 {
401 return CKR_FUNCTION_NOT_SUPPORTED;
402 }
403
C_Logout(CK_SESSION_HANDLE hSession)404 CK_RV C_Logout(CK_SESSION_HANDLE hSession)
405 { /* the session's handle */
406 CK_RV rv;
407 struct sc_pkcs11_session *session;
408 struct sc_pkcs11_slot *slot;
409
410 rv = sc_pkcs11_lock();
411 if (rv != CKR_OK)
412 return rv;
413
414 session = list_seek(&sessions, &hSession);
415 if (!session) {
416 rv = CKR_SESSION_HANDLE_INVALID;
417 goto out;
418 }
419
420 sc_log(context, "C_Logout(hSession:0x%lx)", hSession);
421
422 slot = session->slot;
423
424 if (slot->login_user >= 0) {
425 slot->login_user = -1;
426 if (sc_pkcs11_conf.atomic)
427 pop_all_login_states(slot);
428 else {
429 if (!slot->p11card)
430 return CKR_TOKEN_NOT_RECOGNIZED;
431 rv = slot->p11card->framework->logout(slot);
432 }
433 } else
434 rv = CKR_USER_NOT_LOGGED_IN;
435
436 out:
437 sc_pkcs11_unlock();
438 return rv;
439 }
440
C_InitPIN(CK_SESSION_HANDLE hSession,CK_CHAR_PTR pPin,CK_ULONG ulPinLen)441 CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
442 {
443 CK_RV rv;
444 struct sc_pkcs11_session *session;
445 struct sc_pkcs11_slot *slot;
446
447 sc_log(context, "C_InitPIN() called, pin '%s'", pPin ? (char *) pPin : "<null>");
448 if (pPin == NULL_PTR && ulPinLen > 0)
449 return CKR_ARGUMENTS_BAD;
450
451 rv = sc_pkcs11_lock();
452 if (rv != CKR_OK)
453 return rv;
454
455 session = list_seek(&sessions, &hSession);
456 if (!session) {
457 rv = CKR_SESSION_HANDLE_INVALID;
458 goto out;
459 }
460
461 if (!(session->flags & CKF_RW_SESSION)) {
462 rv = CKR_SESSION_READ_ONLY;
463 goto out;
464 }
465
466 slot = session->slot;
467 if (slot->login_user != CKU_SO) {
468 rv = CKR_USER_NOT_LOGGED_IN;
469 } else if (slot->p11card == NULL || slot->p11card->framework->init_pin == NULL) {
470 rv = CKR_FUNCTION_NOT_SUPPORTED;
471 } else {
472 rv = restore_login_state(slot);
473 if (rv == CKR_OK) {
474 rv = slot->p11card->framework->init_pin(slot, pPin, ulPinLen);
475 sc_log(context, "C_InitPIN() init-pin result %li", rv);
476 }
477 rv = reset_login_state(slot, rv);
478 }
479
480 out:
481 sc_pkcs11_unlock();
482 return rv;
483 }
484
C_SetPIN(CK_SESSION_HANDLE hSession,CK_CHAR_PTR pOldPin,CK_ULONG ulOldLen,CK_CHAR_PTR pNewPin,CK_ULONG ulNewLen)485 CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
486 CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen)
487 {
488 CK_RV rv;
489 struct sc_pkcs11_session *session;
490 struct sc_pkcs11_slot *slot;
491
492 if ((pOldPin == NULL_PTR && ulOldLen > 0) || (pNewPin == NULL_PTR && ulNewLen > 0))
493 return CKR_ARGUMENTS_BAD;
494
495 rv = sc_pkcs11_lock();
496 if (rv != CKR_OK)
497 return rv;
498
499 session = list_seek(&sessions, &hSession);
500 if (!session) {
501 rv = CKR_SESSION_HANDLE_INVALID;
502 goto out;
503 }
504
505 slot = session->slot;
506 sc_log(context, "Changing PIN (session 0x%lx; login user %d)", hSession, slot->login_user);
507
508 if (!(session->flags & CKF_RW_SESSION)) {
509 rv = CKR_SESSION_READ_ONLY;
510 goto out;
511 }
512
513 rv = restore_login_state(slot);
514 if (rv == CKR_OK) {
515 if (slot->p11card == NULL)
516 return CKR_TOKEN_NOT_RECOGNIZED;
517 rv = slot->p11card->framework->change_pin(slot, pOldPin, ulOldLen, pNewPin, ulNewLen);
518 }
519 rv = reset_login_state(slot, rv);
520
521 out:
522 sc_pkcs11_unlock();
523 return rv;
524 }
525