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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <md5.h>
30 #include <pthread.h>
31 #include <syslog.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <sys/sha1.h>
36 #include <security/cryptoki.h>
37 #include "softGlobal.h"
38 #include "softSession.h"
39 #include "softObject.h"
40 #include "softOps.h"
41 #include "softKeystore.h"
42 #include "softKeystoreUtil.h"
43 
44 
45 CK_ULONG soft_session_cnt = 0;		/* the number of opened sessions */
46 CK_ULONG soft_session_rw_cnt = 0;	/* the number of opened R/W sessions */
47 
48 /*
49  * Delete all the sessions. First, obtain the global session
50  * list lock. Then start to delete one session at a time.
51  * Release the global session list lock before returning to
52  * caller.
53  */
54 CK_RV
55 soft_delete_all_sessions(boolean_t force)
56 {
57 
58 	CK_RV rv = CKR_OK;
59 	CK_RV rv1;
60 	soft_session_t *session_p;
61 	soft_session_t *session_p1;
62 
63 	/* Acquire the global session list lock */
64 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
65 
66 	session_p = soft_session_list;
67 
68 	/* Delete all the sessions in the session list */
69 	while (session_p) {
70 		session_p1 = session_p->next;
71 
72 		/*
73 		 * Delete a session by calling soft_delete_session()
74 		 * with a session pointer and a boolean arguments.
75 		 * Boolean value TRUE is used to indicate that the
76 		 * caller holds the lock on the global session list.
77 		 *
78 		 */
79 		rv1 = soft_delete_session(session_p, force, B_TRUE);
80 
81 		/* Record the very first error code */
82 		if (rv == CKR_OK) {
83 			rv = rv1;
84 		}
85 
86 		session_p = session_p1;
87 	}
88 
89 	/* No session left */
90 	soft_session_list = NULL;
91 
92 	/* Release the global session list lock */
93 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
94 
95 	return (rv);
96 
97 }
98 
99 
100 /*
101  * Create a new session struct, and add it to the session linked list.
102  *
103  * This function will acquire the global session list lock, and release
104  * it after adding the session to the session linked list.
105  */
106 CK_RV
107 soft_add_session(CK_FLAGS flags, CK_VOID_PTR pApplication,
108 	CK_NOTIFY notify, CK_ULONG *sessionhandle_p)
109 {
110 
111 	soft_session_t *new_sp = NULL;
112 
113 	/* Allocate a new session struct */
114 	new_sp = calloc(1, sizeof (soft_session_t));
115 	if (new_sp == NULL) {
116 		return (CKR_HOST_MEMORY);
117 	}
118 
119 	new_sp->magic_marker = SOFTTOKEN_SESSION_MAGIC;
120 	new_sp->pApplication = pApplication;
121 	new_sp->Notify = notify;
122 	new_sp->flags = flags;
123 	new_sp->state = CKS_RO_PUBLIC_SESSION;
124 	new_sp->object_list = NULL;
125 	new_sp->ses_refcnt = 0;
126 	new_sp->ses_close_sync = 0;
127 
128 	(void) pthread_mutex_lock(&soft_giant_mutex);
129 	if (soft_slot.authenticated) {
130 		(void) pthread_mutex_unlock(&soft_giant_mutex);
131 		if (flags & CKF_RW_SESSION) {
132 			new_sp->state = CKS_RW_USER_FUNCTIONS;
133 		} else {
134 			new_sp->state = CKS_RO_USER_FUNCTIONS;
135 		}
136 	} else {
137 		(void) pthread_mutex_unlock(&soft_giant_mutex);
138 		if (flags & CKF_RW_SESSION) {
139 			new_sp->state = CKS_RW_PUBLIC_SESSION;
140 		} else {
141 			new_sp->state = CKS_RO_PUBLIC_SESSION;
142 		}
143 	}
144 
145 	/* Initialize the lock for the newly created session */
146 	if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) {
147 		free(new_sp);
148 		return (CKR_CANT_LOCK);
149 	}
150 
151 	(void) pthread_cond_init(&new_sp->ses_free_cond, NULL);
152 
153 	/* Acquire the global session list lock */
154 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
155 
156 	/* Insert the new session in front of session list */
157 	if (soft_session_list == NULL) {
158 		soft_session_list = new_sp;
159 		new_sp->next = NULL;
160 		new_sp->prev = NULL;
161 	} else {
162 		soft_session_list->prev = new_sp;
163 		new_sp->next = soft_session_list;
164 		new_sp->prev = NULL;
165 		soft_session_list = new_sp;
166 	}
167 
168 	/* Type casting the address of a session struct to a session handle */
169 	*sessionhandle_p =  (CK_ULONG)new_sp;
170 	++soft_session_cnt;
171 	if (flags & CKF_RW_SESSION)
172 		++soft_session_rw_cnt;
173 
174 	if (soft_session_cnt == 1)
175 		/*
176 		 * This is the first session to be opened, so we can set
177 		 * validate the public token objects in token list now.
178 		 */
179 		soft_validate_token_objects(B_TRUE);
180 
181 	/* Release the global session list lock */
182 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
183 
184 	return (CKR_OK);
185 
186 }
187 
188 /*
189  * This function adds the to-be-freed session to a linked list.
190  * When the number of sessions queued in the linked list reaches the
191  * maximum threshold MAX_SES_TO_BE_FREED, it will free the first
192  * session (FIFO) in the list.
193  */
194 void
195 session_delay_free(soft_session_t *sp)
196 {
197 	soft_session_t *tmp;
198 
199 	(void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex);
200 
201 	/* Add the newly deleted session at the end of the list */
202 	sp->next = NULL;
203 	if (ses_delay_freed.first == NULL) {
204 		ses_delay_freed.last = sp;
205 		ses_delay_freed.first = sp;
206 	} else {
207 		ses_delay_freed.last->next = sp;
208 		ses_delay_freed.last = sp;
209 	}
210 
211 	if (++ses_delay_freed.count >= MAX_SES_TO_BE_FREED) {
212 		/*
213 		 * Free the first session in the list only if
214 		 * the total count reaches maximum threshold.
215 		 */
216 		ses_delay_freed.count--;
217 		tmp = ses_delay_freed.first->next;
218 		free(ses_delay_freed.first);
219 		ses_delay_freed.first = tmp;
220 	}
221 	(void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex);
222 }
223 
224 /*
225  * Delete a session:
226  * - Remove the session from the session linked list.
227  *   Holding the lock on the global session list is needed to do this.
228  * - Release all the objects created by the session.
229  *
230  * The boolean argument lock_held is used to indicate that whether
231  * the caller of this function holds the lock on the global session
232  * list or not.
233  * - When called by soft_delete_all_sessions(), which is called by
234  *   C_Finalize() or C_CloseAllSessions() -- the lock_held = TRUE.
235  * - When called by C_CloseSession() -- the lock_held = FALSE.
236  *
237  * When the caller does not hold the lock on the global session
238  * list, this function will acquire that lock in order to proceed,
239  * and also release that lock before returning to caller.
240  */
241 CK_RV
242 soft_delete_session(soft_session_t *session_p,
243     boolean_t force, boolean_t lock_held)
244 {
245 
246 	/*
247 	 * Check to see if the caller holds the lock on the global
248 	 * session list. If not, we need to acquire that lock in
249 	 * order to proceed.
250 	 */
251 	if (!lock_held) {
252 		/* Acquire the global session list lock */
253 		(void) pthread_mutex_lock(&soft_sessionlist_mutex);
254 	}
255 
256 	/*
257 	 * Remove the session from the session linked list first.
258 	 */
259 	if (soft_session_list == session_p) {
260 		/* Session is the first one in the list */
261 		if (session_p->next) {
262 			soft_session_list = session_p->next;
263 			session_p->next->prev = NULL;
264 		} else {
265 			/* Session is the only one in the list */
266 			soft_session_list = NULL;
267 		}
268 	} else {
269 		/* Session is not the first one in the list */
270 		if (session_p->next) {
271 			/* Session is in the middle of the list */
272 			session_p->prev->next = session_p->next;
273 			session_p->next->prev = session_p->prev;
274 		} else {
275 			/* Session is the last one in the list */
276 			session_p->prev->next = NULL;
277 		}
278 	}
279 
280 	--soft_session_cnt;
281 	if (session_p->flags & CKF_RW_SESSION)
282 		--soft_session_rw_cnt;
283 
284 	if (!lock_held) {
285 		/*
286 		 * If the global session list lock is obtained by
287 		 * this function, then release that lock after
288 		 * removing the session from session linked list.
289 		 * We want the releasing of the objects of the
290 		 * session, and freeing of the session itself to
291 		 * be done without holding the global session list
292 		 * lock.
293 		 */
294 		(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
295 	}
296 
297 
298 	/* Acquire the individual session lock */
299 	(void) pthread_mutex_lock(&session_p->session_mutex);
300 	/*
301 	 * Make sure another thread hasn't freed the session.
302 	 */
303 	if (session_p->magic_marker != SOFTTOKEN_SESSION_MAGIC) {
304 		(void) pthread_mutex_unlock(&session_p->session_mutex);
305 		return (CKR_OK);
306 	}
307 
308 	/*
309 	 * The deletion of a session must be blocked when the session
310 	 * reference count is not zero. This means if any session related
311 	 * operation starts prior to the session close operation gets in,
312 	 * the session closing thread must wait for the non-closing
313 	 * operation to be completed before it can proceed the close
314 	 * operation.
315 	 *
316 	 * Unless we are being forced to shut everything down, this only
317 	 * happens if the libraries _fini() is running not of someone
318 	 * explicitly called C_Finalize().
319 	 */
320 	if (force)
321 		session_p->ses_refcnt = 0;
322 
323 	while (session_p->ses_refcnt != 0) {
324 		/*
325 		 * We set the SESSION_REFCNT_WAITING flag before we put
326 		 * this closing thread in a wait state, so other non-closing
327 		 * operation thread will signal to wake it up only when
328 		 * the session reference count becomes zero and this flag
329 		 * is set.
330 		 */
331 		session_p->ses_close_sync |= SESSION_REFCNT_WAITING;
332 		(void) pthread_cond_wait(&session_p->ses_free_cond,
333 			&session_p->session_mutex);
334 	}
335 
336 	session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING;
337 
338 	/* Mark session as no longer valid. */
339 	session_p->magic_marker = 0;
340 
341 	(void) pthread_cond_destroy(&session_p->ses_free_cond);
342 
343 	/*
344 	 * Remove all the objects created in this session.
345 	 */
346 	soft_delete_all_objects_in_session(session_p);
347 
348 	/* In case application did not call Final */
349 	if (session_p->digest.context != NULL)
350 		free(session_p->digest.context);
351 
352 	if (session_p->encrypt.context != NULL)
353 		/*
354 		 * 1st B_TRUE: encrypt
355 		 * 2nd B_TRUE: caller is holding session_mutex.
356 		 */
357 		soft_crypt_cleanup(session_p, B_TRUE, B_TRUE);
358 
359 	if (session_p->decrypt.context != NULL)
360 		/*
361 		 * 1st B_FALSE: decrypt
362 		 * 2nd B_TRUE: caller is holding session_mutex.
363 		 */
364 		soft_crypt_cleanup(session_p, B_FALSE, B_TRUE);
365 
366 	if (session_p->sign.context != NULL)
367 		free(session_p->sign.context);
368 
369 	if (session_p->verify.context != NULL)
370 		free(session_p->verify.context);
371 
372 	if (session_p->find_objects.context != NULL) {
373 		find_context_t *fcontext;
374 		fcontext = (find_context_t *)session_p->find_objects.context;
375 		free(fcontext->objs_found);
376 		free(fcontext);
377 	}
378 
379 	/* Reset SESSION_IS_CLOSIN flag. */
380 	session_p->ses_close_sync &= ~SESSION_IS_CLOSING;
381 
382 	(void) pthread_mutex_unlock(&session_p->session_mutex);
383 	/* Destroy the individual session lock */
384 	(void) pthread_mutex_destroy(&session_p->session_mutex);
385 
386 	/* Delay freeing the session */
387 	session_delay_free(session_p);
388 
389 	return (CKR_OK);
390 }
391 
392 
393 /*
394  * This function is used to type cast a session handle to a pointer to
395  * the session struct. Also, it does the following things:
396  * 1) Check to see if the session struct is tagged with a session
397  *    magic number. This is to detect when an application passes
398  *    a bogus session pointer.
399  * 2) Acquire the locks on the global session list and on the designated
400  *    session.
401  * 3) Check to see if the session is in the closing state that another
402  *    thread is performing.
403  * 4) Increment the session reference count by one. This is to prevent
404  *    this session from being closed by other thread.
405  * 5) Release the locks held on the designated session and on the global
406  *    session list.
407  */
408 CK_RV
409 handle2session(CK_SESSION_HANDLE hSession, soft_session_t **session_p)
410 {
411 
412 	soft_session_t *sp = (soft_session_t *)(hSession);
413 
414 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
415 	if (all_sessions_closing) {
416 		(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
417 		return (CKR_SESSION_CLOSED);
418 	}
419 	/*
420 	 * We need to free the global session list lock to prevent deadlock
421 	 * between C_CloseSession and C_DestroyObject. S1WS/NSS does
422 	 * explicit deletion (C_DestroyObject) after implicit deletion by
423 	 * C_CloseSession.
424 	 */
425 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
426 
427 	if ((sp == NULL) ||
428 	    (sp->magic_marker != SOFTTOKEN_SESSION_MAGIC)) {
429 		return (CKR_SESSION_HANDLE_INVALID);
430 	}
431 	(void) pthread_mutex_lock(&sp->session_mutex);
432 
433 	if (sp->ses_close_sync & SESSION_IS_CLOSING) {
434 		(void) pthread_mutex_unlock(&sp->session_mutex);
435 		return (CKR_SESSION_CLOSED);
436 	}
437 
438 	/* Increment session ref count. */
439 	sp->ses_refcnt++;
440 
441 	(void) pthread_mutex_unlock(&sp->session_mutex);
442 
443 	*session_p = sp;
444 
445 	return (CKR_OK);
446 }
447 
448 /*
449  * The format to be saved in the pOperationState will be:
450  * 1. internal_op_state_t
451  * 2. crypto_active_op_t
452  * 3. actual context of the active operation
453  */
454 CK_RV
455 soft_get_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState,
456     CK_ULONG_PTR pulOperationStateLen)
457 {
458 
459 	internal_op_state_t op_state;
460 	CK_ULONG op_data_len = 0;
461 
462 	/* Check to see if encrypt operation is active. */
463 	if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) {
464 		return (CKR_STATE_UNSAVEABLE);
465 	}
466 
467 	/* Check to see if decrypt operation is active. */
468 	if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) {
469 		return (CKR_STATE_UNSAVEABLE);
470 	}
471 
472 	/* Check to see if sign operation is active. */
473 	if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) {
474 		return (CKR_STATE_UNSAVEABLE);
475 	}
476 
477 	/* Check to see if verify operation is active. */
478 	if (session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) {
479 		return (CKR_STATE_UNSAVEABLE);
480 	}
481 
482 	/* Check to see if digest operation is active. */
483 	if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) {
484 		op_data_len = sizeof (internal_op_state_t) +
485 		    sizeof (crypto_active_op_t);
486 
487 		switch (session_p->digest.mech.mechanism) {
488 		case CKM_MD5:
489 			op_data_len += sizeof (MD5_CTX);
490 			break;
491 		case CKM_SHA_1:
492 			op_data_len += sizeof (SHA1_CTX);
493 			break;
494 		default:
495 			return (CKR_STATE_UNSAVEABLE);
496 		}
497 
498 		if (pOperationState == NULL_PTR) {
499 			*pulOperationStateLen = op_data_len;
500 			return (CKR_OK);
501 		} else {
502 			if (*pulOperationStateLen < op_data_len) {
503 				*pulOperationStateLen = op_data_len;
504 				return (CKR_BUFFER_TOO_SMALL);
505 			}
506 		}
507 
508 		op_state.op_len = op_data_len;
509 		op_state.op_active = DIGEST_OP;
510 		op_state.op_session_state = session_p->state;
511 
512 		/* Save internal_op_state_t */
513 		(void) memcpy(pOperationState, (CK_BYTE_PTR)&op_state,
514 		    sizeof (internal_op_state_t));
515 
516 		/* Save crypto_active_op_t */
517 		(void) memcpy((CK_BYTE *)pOperationState +
518 		    sizeof (internal_op_state_t),
519 		    &session_p->digest,
520 		    sizeof (crypto_active_op_t));
521 
522 		switch (session_p->digest.mech.mechanism) {
523 		case CKM_MD5:
524 			/* Save MD5_CTX for the active digest operation */
525 			(void) memcpy((CK_BYTE *)pOperationState +
526 			    sizeof (internal_op_state_t) +
527 			    sizeof (crypto_active_op_t),
528 			    session_p->digest.context,
529 			    sizeof (MD5_CTX));
530 			break;
531 
532 		case CKM_SHA_1:
533 			/* Save SHA1_CTX for the active digest operation */
534 			(void) memcpy((CK_BYTE *)pOperationState +
535 			    sizeof (internal_op_state_t) +
536 			    sizeof (crypto_active_op_t),
537 			    session_p->digest.context,
538 			    sizeof (SHA1_CTX));
539 			break;
540 
541 		default:
542 			return (CKR_STATE_UNSAVEABLE);
543 		}
544 	}
545 
546 	*pulOperationStateLen = op_data_len;
547 	return (CKR_OK);
548 
549 }
550 
551 /*
552  * The format to be restored from the pOperationState will be:
553  * 1. internal_op_state_t
554  * 2. crypto_active_op_t
555  * 3. actual context of the saved operation
556  */
557 CK_RV
558 soft_set_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState,
559     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
560     CK_OBJECT_HANDLE hAuthenticationKey)
561 {
562 
563 	CK_RV		rv;
564 	internal_op_state_t op_state;
565 	crypto_active_op_t crypto_tmp;
566 	CK_ULONG offset = 0;
567 
568 	/* Restore internal_op_state_t */
569 	(void) memcpy((CK_BYTE_PTR)&op_state, pOperationState,
570 	    sizeof (internal_op_state_t));
571 
572 	if (session_p->state != op_state.op_session_state) {
573 		/*
574 		 * The supplied session state does not match with
575 		 * the saved session state.
576 		 */
577 		return (CKR_SAVED_STATE_INVALID);
578 	}
579 
580 	if (op_state.op_len != ulOperationStateLen) {
581 		/*
582 		 * The supplied data length does not match with
583 		 * the saved data length.
584 		 */
585 		return (CKR_SAVED_STATE_INVALID);
586 	}
587 
588 	offset = sizeof (internal_op_state_t);
589 
590 	(void) memcpy((CK_BYTE *)&crypto_tmp,
591 	    (CK_BYTE *)pOperationState + offset,
592 	    sizeof (crypto_active_op_t));
593 
594 	switch (op_state.op_active) {
595 	case DIGEST_OP:
596 		if ((hAuthenticationKey != 0) || (hEncryptionKey != 0)) {
597 			return (CKR_KEY_NOT_NEEDED);
598 		}
599 
600 		/*
601 		 * If the destination session has the same mechanism
602 		 * as the source, we can reuse the memory allocated for
603 		 * the crypto context. Otherwise, we free the crypto
604 		 * context of the destination session now.
605 		 */
606 		if (session_p->digest.context) {
607 			if (session_p->digest.mech.mechanism !=
608 			    crypto_tmp.mech.mechanism) {
609 				(void) pthread_mutex_lock(&session_p->
610 				    session_mutex);
611 				free(session_p->digest.context);
612 				session_p->digest.context = NULL;
613 				(void) pthread_mutex_unlock(&session_p->
614 				    session_mutex);
615 			}
616 		}
617 		break;
618 
619 	default:
620 		return (CKR_SAVED_STATE_INVALID);
621 	}
622 
623 	/* Restore crypto_active_op_t */
624 	(void) pthread_mutex_lock(&session_p->session_mutex);
625 	session_p->digest.mech.mechanism = crypto_tmp.mech.mechanism;
626 	session_p->digest.flags = crypto_tmp.flags;
627 	(void) pthread_mutex_unlock(&session_p->session_mutex);
628 
629 	offset += sizeof (crypto_active_op_t);
630 
631 	/*
632 	 * Make sure the supplied crypto operation state is valid
633 	 */
634 	switch (op_state.op_active) {
635 	case DIGEST_OP:
636 
637 		switch (session_p->digest.mech.mechanism) {
638 		case CKM_MD5:
639 			(void) pthread_mutex_lock(&session_p->session_mutex);
640 			if (session_p->digest.context == NULL) {
641 				session_p->digest.context =
642 				    malloc(sizeof (MD5_CTX));
643 
644 				if (session_p->digest.context == NULL) {
645 					(void) pthread_mutex_unlock(
646 					    &session_p->session_mutex);
647 					return (CKR_HOST_MEMORY);
648 				}
649 			}
650 
651 			/* Restore MD5_CTX from the saved digest operation */
652 			(void) memcpy((CK_BYTE *)session_p->digest.context,
653 			    (CK_BYTE *)pOperationState + offset,
654 			    sizeof (MD5_CTX));
655 
656 			(void) pthread_mutex_unlock(&session_p->session_mutex);
657 
658 			rv = CKR_OK;
659 			break;
660 
661 		case CKM_SHA_1:
662 			(void) pthread_mutex_lock(&session_p->session_mutex);
663 			if (session_p->digest.context == NULL) {
664 				session_p->digest.context =
665 				    malloc(sizeof (SHA1_CTX));
666 
667 				if (session_p->digest.context == NULL) {
668 					(void) pthread_mutex_unlock(
669 					    &session_p->session_mutex);
670 					return (CKR_HOST_MEMORY);
671 				}
672 			}
673 
674 			/* Restore SHA1_CTX from the saved digest operation */
675 			(void) memcpy((CK_BYTE *)session_p->digest.context,
676 			    (CK_BYTE *)pOperationState + offset,
677 			    sizeof (SHA1_CTX));
678 
679 			(void) pthread_mutex_unlock(&session_p->session_mutex);
680 
681 			rv = CKR_OK;
682 			break;
683 
684 		default:
685 			rv = CKR_SAVED_STATE_INVALID;
686 			break;
687 		}
688 		break;
689 
690 	default:
691 		rv = CKR_SAVED_STATE_INVALID;
692 		break;
693 	}
694 
695 	return (rv);
696 
697 }
698 
699 
700 CK_RV
701 soft_login(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
702 {
703 
704 	/*
705 	 * Authenticate the input PIN.
706 	 */
707 	return (soft_verify_pin(pPin, ulPinLen));
708 
709 }
710 
711 void
712 soft_logout(void)
713 {
714 
715 	/*
716 	 * Delete all the private token objects from the "token_object_list".
717 	 */
718 	soft_delete_all_in_core_token_objects(PRIVATE_TOKEN);
719 	return;
720 
721 }
722