1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <pthread.h>
27 #include <errno.h>
28 #include <security/cryptoki.h>
29 #include <sys/crypto/ioctl.h>
30 #include "kernelGlobal.h"
31 #include "kernelSession.h"
32 #include "kernelSlot.h"
33 #include "kernelEmulate.h"
34
35 CK_RV
C_OpenSession(CK_SLOT_ID slotID,CK_FLAGS flags,CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession)36 C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
37 CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
38 {
39 CK_RV rv = CKR_OK;
40 kernel_slot_t *pslot;
41
42 if (!kernel_initialized)
43 return (CKR_CRYPTOKI_NOT_INITIALIZED);
44
45 /*
46 * For legacy reasons, the CKF_SERIAL_SESSION bit must always
47 * be set.
48 */
49 if (!(flags & CKF_SERIAL_SESSION))
50 return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
51
52 if (phSession == NULL)
53 return (CKR_ARGUMENTS_BAD);
54
55 if (slotID >= slot_count) {
56 return (CKR_SLOT_ID_INVALID);
57 }
58
59 /*
60 * Acquire the slot lock to protect sl_state and sl_sess_list.
61 * These two fields need to be protected atomically, even though
62 * "sl_sess_list" is updated in kernel_add_session().
63 */
64 pslot = slot_table[slotID];
65 (void) pthread_mutex_lock(&pslot->sl_mutex);
66
67 /* If SO is logged in the slot, only the RW session is allowed. */
68 if ((pslot->sl_state == CKU_SO) && !(flags & CKF_RW_SESSION)) {
69 (void) pthread_mutex_unlock(&pslot->sl_mutex);
70 return (CKR_SESSION_READ_WRITE_SO_EXISTS);
71 }
72
73 /* Create a new session */
74 rv = kernel_add_session(slotID, flags, pApplication, Notify,
75 phSession);
76 (void) pthread_mutex_unlock(&pslot->sl_mutex);
77 return (rv);
78 }
79
80 CK_RV
C_CloseSession(CK_SESSION_HANDLE hSession)81 C_CloseSession(CK_SESSION_HANDLE hSession)
82 {
83 CK_RV rv;
84
85 kernel_session_t *session_p;
86 boolean_t ses_lock_held = B_FALSE;
87
88 if (!kernel_initialized)
89 return (CKR_CRYPTOKI_NOT_INITIALIZED);
90
91 /*
92 * Obtain the session pointer. Also, increment the session
93 * reference count.
94 */
95 rv = handle2session(hSession, &session_p);
96 if (rv != CKR_OK)
97 return (rv);
98
99 (void) pthread_mutex_lock(&session_p->session_mutex);
100 ses_lock_held = B_TRUE;
101
102 /*
103 * Set SESSION_IS_CLOSING flag so any access to this
104 * session will be rejected.
105 */
106 if (session_p->ses_close_sync & SESSION_IS_CLOSING) {
107 REFRELE(session_p, ses_lock_held);
108 return (CKR_SESSION_CLOSED);
109 }
110 session_p->ses_close_sync |= SESSION_IS_CLOSING;
111
112 /*
113 * Decrement the session reference count.
114 * We hold the session lock, and REFRELE()
115 * will release the session lock for us.
116 */
117 REFRELE(session_p, ses_lock_held);
118
119 /*
120 * Delete a session by calling kernel_delete_session() with
121 * a session pointer and two boolean arguments. The 3rd argument
122 * boolean value FALSE indicates that the caller does not
123 * hold the slot lock. The 4th argument boolean value B_FALSE
124 * indicates that we want to delete all the objects completely.
125 *
126 * kernel_delete_session() will reset SESSION_IS_CLOSING
127 * flag after it is done.
128 */
129 kernel_delete_session(session_p->ses_slotid, session_p, B_FALSE,
130 B_FALSE);
131 return (rv);
132 }
133
134
135 CK_RV
C_CloseAllSessions(CK_SLOT_ID slotID)136 C_CloseAllSessions(CK_SLOT_ID slotID)
137 {
138 if (!kernel_initialized)
139 return (CKR_CRYPTOKI_NOT_INITIALIZED);
140
141 /* Delete all the sessions and release the allocated resources */
142 kernel_delete_all_sessions(slotID, B_FALSE);
143
144 return (CKR_OK);
145 }
146
147 /*
148 * Utility routine to get CK_STATE value for a session.
149 * The caller should not be holding the session lock.
150 */
151 static CK_STATE
get_ses_state(kernel_session_t * session_p)152 get_ses_state(kernel_session_t *session_p)
153 {
154 CK_STATE state;
155 kernel_slot_t *pslot;
156
157 pslot = slot_table[session_p->ses_slotid];
158 (void) pthread_mutex_lock(&pslot->sl_mutex);
159
160 if (pslot->sl_state == CKU_PUBLIC) {
161 state = (session_p->ses_RO) ?
162 CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
163 } else if (pslot->sl_state == CKU_USER) {
164 state = (session_p->ses_RO) ?
165 CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
166 } else if (pslot->sl_state == CKU_SO) {
167 state = CKS_RW_SO_FUNCTIONS;
168 }
169
170 (void) pthread_mutex_unlock(&pslot->sl_mutex);
171
172 return (state);
173 }
174
175
176 CK_RV
C_GetSessionInfo(CK_SESSION_HANDLE hSession,CK_SESSION_INFO_PTR pInfo)177 C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
178 {
179 kernel_session_t *session_p;
180 CK_RV rv;
181 boolean_t ses_lock_held = B_FALSE;
182
183 if (!kernel_initialized)
184 return (CKR_CRYPTOKI_NOT_INITIALIZED);
185
186 if (pInfo == NULL)
187 return (CKR_ARGUMENTS_BAD);
188
189 /*
190 * Obtain the session pointer. Also, increment the session
191 * reference count.
192 */
193 rv = handle2session(hSession, &session_p);
194 if (rv != CKR_OK)
195 return (rv);
196
197 /* Provide information for the specified session */
198 pInfo->slotID = session_p->ses_slotid;
199 pInfo->flags = session_p->flags;
200 pInfo->ulDeviceError = 0;
201 pInfo->state = get_ses_state(session_p);
202
203 /*
204 * Decrement the session reference count.
205 */
206 REFRELE(session_p, ses_lock_held);
207
208 return (CKR_OK);
209 }
210
211 /*
212 * Save the state in pOperationState. The data format is:
213 * 1. Total length (including this field)
214 * 2. session state
215 * 3. crypto_active_op_t structure
216 * 4. digest_buf_t's data buffer contents
217 */
218 static CK_RV
kernel_get_operationstate(kernel_session_t * session_p,CK_STATE ses_state,CK_BYTE_PTR pOperationState,CK_ULONG_PTR pulOperationStateLen)219 kernel_get_operationstate(kernel_session_t *session_p, CK_STATE ses_state,
220 CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen)
221 {
222 int op_data_len = 0;
223 CK_BYTE_PTR dst;
224 digest_buf_t *bufp;
225
226 if (!(session_p->digest.flags & CRYPTO_EMULATE)) {
227 /*
228 * Return CKR_OPERATION_NOT_INITIALIZED if the slot
229 * is capable of C_GetOperationState(). Return
230 * CKR_FUNCTION_NOT_SUPPORTED otherwise.
231 *
232 * We return these codes because some clients
233 * check the return code to determine if C_GetOperationState()
234 * is supported.
235 */
236 if (slot_table[session_p->ses_slotid]->sl_flags &
237 CRYPTO_LIMITED_HASH_SUPPORT)
238 return (CKR_OPERATION_NOT_INITIALIZED);
239 else
240 return (CKR_FUNCTION_NOT_SUPPORTED);
241 }
242
243 /*
244 * XXX Need to support this case in future.
245 * This is the case where we exceeded SLOT_HASH_MAX_INDATA_LEN and
246 * hence started using libmd. SLOT_HASH_MAX_INDATA_LEN is at least
247 * 64K for current crypto framework providers and web servers
248 * do not need to clone digests that big for SSL operations.
249 */
250 if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) {
251 return (CKR_STATE_UNSAVEABLE);
252 }
253
254 /* Check to see if this is an unsupported operation. */
255 if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE ||
256 session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE ||
257 session_p->sign.flags & CRYPTO_OPERATION_ACTIVE ||
258 session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) {
259 return (CKR_STATE_UNSAVEABLE);
260 }
261
262 /* Check to see if digest operation is active. */
263 if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) {
264 return (CKR_OPERATION_NOT_INITIALIZED);
265 }
266
267 bufp = session_p->digest.context;
268
269 op_data_len = sizeof (int);
270 op_data_len += sizeof (CK_STATE);
271 op_data_len += sizeof (crypto_active_op_t);
272 op_data_len += bufp->indata_len;
273
274 if (pOperationState == NULL_PTR) {
275 *pulOperationStateLen = op_data_len;
276 return (CKR_OK);
277 } else {
278 if (*pulOperationStateLen < op_data_len) {
279 *pulOperationStateLen = op_data_len;
280 return (CKR_BUFFER_TOO_SMALL);
281 }
282 }
283
284 dst = pOperationState;
285
286 /* Save total length */
287 bcopy(&op_data_len, dst, sizeof (int));
288 dst += sizeof (int);
289
290 /* Save session state */
291 bcopy(&ses_state, dst, sizeof (CK_STATE));
292 dst += sizeof (CK_STATE);
293
294 /* Save crypto_active_op_t */
295 bcopy(&session_p->digest, dst, sizeof (crypto_active_op_t));
296 dst += sizeof (crypto_active_op_t);
297
298 /* Save the data buffer */
299 bcopy(bufp->buf, dst, bufp->indata_len);
300
301 *pulOperationStateLen = op_data_len;
302 return (CKR_OK);
303 }
304
305 CK_RV
C_GetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG_PTR pulOperationStateLen)306 C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
307 CK_ULONG_PTR pulOperationStateLen)
308 {
309 CK_RV rv;
310 CK_STATE ses_state;
311 kernel_session_t *session_p;
312 boolean_t ses_lock_held = B_TRUE;
313
314 if (!kernel_initialized)
315 return (CKR_CRYPTOKI_NOT_INITIALIZED);
316
317 if (pulOperationStateLen == NULL_PTR)
318 return (CKR_ARGUMENTS_BAD);
319
320 /*
321 * Obtain the session pointer. Also, increment the session
322 * reference count.
323 */
324 rv = handle2session(hSession, &session_p);
325 if (rv != CKR_OK)
326 return (rv);
327
328 ses_state = get_ses_state(session_p);
329
330 (void) pthread_mutex_lock(&session_p->session_mutex);
331 rv = kernel_get_operationstate(session_p, ses_state,
332 pOperationState, pulOperationStateLen);
333
334 REFRELE(session_p, ses_lock_held);
335 return (rv);
336 }
337
338 /*
339 * Restore the state from pOperationState. The data format is:
340 * 1. Total length (including this field)
341 * 2. session state
342 * 3. crypto_active_op_t structure
343 * 4. digest_buf_t's data buffer contents
344 */
345 static CK_RV
kernel_set_operationstate(kernel_session_t * session_p,CK_STATE ses_state,CK_BYTE_PTR pOperationState,CK_ULONG ulOperationStateLen,CK_OBJECT_HANDLE hEncryptionKey,CK_OBJECT_HANDLE hAuthenticationKey)346 kernel_set_operationstate(kernel_session_t *session_p, CK_STATE ses_state,
347 CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen,
348 CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey)
349 {
350 CK_RV rv;
351 CK_BYTE_PTR src;
352 CK_STATE src_ses_state;
353 int expected_len, indata_len;
354 digest_buf_t *bufp;
355 crypto_active_op_t tmp_op;
356
357 if ((hAuthenticationKey != 0) || (hEncryptionKey != 0))
358 return (CKR_KEY_NOT_NEEDED);
359
360 src = pOperationState;
361
362 /* Get total length field */
363 bcopy(src, &expected_len, sizeof (int));
364 if (ulOperationStateLen < expected_len)
365 return (CKR_SAVED_STATE_INVALID);
366
367 /* compute the data buffer length */
368 indata_len = expected_len - sizeof (int) -
369 sizeof (CK_STATE) - sizeof (crypto_active_op_t);
370 if (indata_len > SLOT_HASH_MAX_INDATA_LEN(session_p))
371 return (CKR_SAVED_STATE_INVALID);
372 src += sizeof (int);
373
374 /* Get session state */
375 bcopy(src, &src_ses_state, sizeof (CK_STATE));
376 if (ses_state != src_ses_state)
377 return (CKR_SAVED_STATE_INVALID);
378 src += sizeof (CK_STATE);
379
380 /*
381 * Restore crypto_active_op_t. We need to use a temporary
382 * buffer to avoid modifying the source session's buffer.
383 */
384 bcopy(src, &tmp_op, sizeof (crypto_active_op_t));
385 if (tmp_op.flags & CRYPTO_EMULATE_USING_SW)
386 return (CKR_SAVED_STATE_INVALID);
387 session_p->digest.mech = tmp_op.mech;
388 session_p->digest.flags = tmp_op.flags;
389 src += sizeof (crypto_active_op_t);
390
391 /* This routine reuses the session's existing buffer if possible */
392 rv = emulate_buf_init(session_p, indata_len, OP_DIGEST);
393 if (rv != CKR_OK)
394 return (rv);
395 bufp = session_p->digest.context;
396 bufp->indata_len = indata_len;
397
398 /* Restore the data buffer */
399 bcopy(src, bufp->buf, bufp->indata_len);
400
401 return (CKR_OK);
402 }
403
404
405 CK_RV
C_SetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG ulOperationStateLen,CK_OBJECT_HANDLE hEncryptionKey,CK_OBJECT_HANDLE hAuthenticationKey)406 C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
407 CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
408 CK_OBJECT_HANDLE hAuthenticationKey)
409 {
410 CK_RV rv;
411 CK_STATE ses_state;
412 kernel_session_t *session_p;
413 boolean_t ses_lock_held = B_TRUE;
414
415 if (!kernel_initialized)
416 return (CKR_CRYPTOKI_NOT_INITIALIZED);
417
418 if ((pOperationState == NULL_PTR) ||
419 (ulOperationStateLen == 0))
420 return (CKR_ARGUMENTS_BAD);
421
422 rv = handle2session(hSession, &session_p);
423 if (rv != CKR_OK)
424 return (rv);
425
426 ses_state = get_ses_state(session_p);
427
428 (void) pthread_mutex_lock(&session_p->session_mutex);
429
430 rv = kernel_set_operationstate(session_p, ses_state,
431 pOperationState, ulOperationStateLen,
432 hEncryptionKey, hAuthenticationKey);
433
434 REFRELE(session_p, ses_lock_held);
435 return (rv);
436 }
437
438
439 CK_RV
C_Login(CK_SESSION_HANDLE hSession,CK_USER_TYPE userType,CK_UTF8CHAR_PTR pPin,CK_ULONG ulPinLen)440 C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
441 CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
442 {
443 CK_RV rv = CKR_OK;
444 kernel_session_t *session_p;
445 kernel_slot_t *pslot;
446 boolean_t ses_lock_held = B_FALSE;
447 crypto_login_t c_login;
448 int r;
449
450 if (!kernel_initialized)
451 return (CKR_CRYPTOKI_NOT_INITIALIZED);
452
453 if ((userType != CKU_SO) && (userType != CKU_USER)) {
454 return (CKR_USER_TYPE_INVALID);
455 }
456
457 /*
458 * Obtain the session pointer. Also, increment the session
459 * reference count.
460 */
461 rv = handle2session(hSession, &session_p);
462 if (rv != CKR_OK)
463 return (rv);
464
465 /* Acquire the slot lock */
466 pslot = slot_table[session_p->ses_slotid];
467 (void) pthread_mutex_lock(&pslot->sl_mutex);
468
469 /* Check if the slot is logged in already */
470 if ((pslot->sl_state == CKU_USER) || (pslot->sl_state == CKU_SO)) {
471 rv = CKR_USER_ALREADY_LOGGED_IN;
472 goto clean_exit;
473 }
474
475 /* To login as SO, every session in this slot needs to be R/W */
476 if (userType == CKU_SO) {
477 kernel_session_t *sp;
478 boolean_t found;
479
480 found = B_FALSE;
481 sp = pslot->sl_sess_list;
482 while (sp) {
483 /*
484 * Need not to lock individual sessions before
485 * accessing their "ses_RO" and "next" fields,
486 * because they are always accessed under the
487 * slot's mutex protection.
488 */
489 if (sp->ses_RO) {
490 found = B_TRUE;
491 break;
492 }
493 sp = sp->next;
494 }
495
496 if (found) {
497 rv = CKR_SESSION_READ_ONLY_EXISTS;
498 goto clean_exit;
499 }
500 }
501
502 /* Now make the ioctl call; no need to acquire the session lock. */
503 c_login.co_session = session_p->k_session;
504 c_login.co_user_type = userType;
505 c_login.co_pin_len = ulPinLen;
506 c_login.co_pin = (char *)pPin;
507
508 while ((r = ioctl(kernel_fd, CRYPTO_LOGIN, &c_login)) < 0) {
509 if (errno != EINTR)
510 break;
511 }
512 if (r < 0) {
513 rv = CKR_FUNCTION_FAILED;
514 } else {
515 rv = crypto2pkcs11_error_number(c_login.co_return_value);
516 }
517
518 if (rv == CKR_OK) {
519 /* Set the slot's session state. */
520 pslot->sl_state = userType;
521 }
522
523 clean_exit:
524
525 REFRELE(session_p, ses_lock_held);
526 (void) pthread_mutex_unlock(&pslot->sl_mutex);
527 return (rv);
528 }
529
530
531 CK_RV
C_Logout(CK_SESSION_HANDLE hSession)532 C_Logout(CK_SESSION_HANDLE hSession)
533 {
534 CK_RV rv = CKR_OK;
535 kernel_session_t *session_p;
536 kernel_slot_t *pslot;
537 boolean_t ses_lock_held = B_FALSE;
538 crypto_logout_t c_logout;
539 int r;
540
541 if (!kernel_initialized)
542 return (CKR_CRYPTOKI_NOT_INITIALIZED);
543
544 /*
545 * Obtain the session pointer. Also, increment the session
546 * reference count.
547 */
548 rv = handle2session(hSession, &session_p);
549 if (rv != CKR_OK)
550 return (rv);
551
552 /* Acquire the slot lock. */
553 pslot = slot_table[session_p->ses_slotid];
554 (void) pthread_mutex_lock(&pslot->sl_mutex);
555
556 /* Check if the user or SO was logged in */
557 if (pslot->sl_state == CKU_PUBLIC) {
558 rv = CKR_USER_NOT_LOGGED_IN;
559 goto clean_exit;
560 }
561
562 /* Now make the ioctl call. No need to acquire the session lock. */
563 c_logout.cl_session = session_p->k_session;
564 while ((r = ioctl(kernel_fd, CRYPTO_LOGOUT, &c_logout)) < 0) {
565 if (errno != EINTR)
566 break;
567 }
568 if (r < 0) {
569 rv = CKR_FUNCTION_FAILED;
570 } else {
571 rv = crypto2pkcs11_error_number(c_logout.cl_return_value);
572 }
573
574 if (rv != CKR_OK) {
575 goto clean_exit;
576 }
577
578 /*
579 * If this slot was logged in as USER previously, we need to clean up
580 * all private object wrappers in library for this slot.
581 */
582 kernel_cleanup_pri_objects_in_slot(pslot, session_p);
583
584 if (rv == CKR_OK) {
585 /* Reset the slot's session state. */
586 pslot->sl_state = CKU_PUBLIC;
587 }
588
589 clean_exit:
590 REFRELE(session_p, ses_lock_held);
591 (void) pthread_mutex_unlock(&pslot->sl_mutex);
592 return (rv);
593 }
594