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