xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaSession.c (revision d288ba7491829a622697c947c3f1a30aec18c133)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d3a28a55Sdinak  * Common Development and Distribution License (the "License").
6d3a28a55Sdinak  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*d288ba74SAnthony Scarpino  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Session Management Functions
287c478bd9Sstevel@tonic-gate  * (as defined in PKCS#11 spec spection 11.6)
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <string.h>
327c478bd9Sstevel@tonic-gate #include "metaGlobal.h"
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate extern meta_session_t *meta_sessionlist_head;
357c478bd9Sstevel@tonic-gate extern pthread_rwlock_t meta_sessionlist_lock;
367c478bd9Sstevel@tonic-gate extern CK_ULONG num_meta_sessions;
377c478bd9Sstevel@tonic-gate extern CK_ULONG num_rw_meta_sessions;
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate  * meta_OpenSession
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  * NOTES:
437c478bd9Sstevel@tonic-gate  * 1) The pApplication and Notify args are not used, as the metaslot does not
447c478bd9Sstevel@tonic-gate  *    support application callbacks.
457c478bd9Sstevel@tonic-gate  * 2) the slotID argument is not checked or used because this function
467c478bd9Sstevel@tonic-gate  *    is only called from the framework.
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate /* ARGSUSED */
497c478bd9Sstevel@tonic-gate CK_RV
meta_OpenSession(CK_SLOT_ID slotID,CK_FLAGS flags,CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession)507c478bd9Sstevel@tonic-gate meta_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
517c478bd9Sstevel@tonic-gate     CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
527c478bd9Sstevel@tonic-gate {
537c478bd9Sstevel@tonic-gate 	meta_session_t *new_session;
547c478bd9Sstevel@tonic-gate 	CK_RV rv;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate 	if (!metaslot_enabled) {
577c478bd9Sstevel@tonic-gate 		return (CKR_SLOT_ID_INVALID);
587c478bd9Sstevel@tonic-gate 	}
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate 	if (phSession == NULL) {
617c478bd9Sstevel@tonic-gate 		return (CKR_ARGUMENTS_BAD);
627c478bd9Sstevel@tonic-gate 	}
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	/* Check for any unknown flags. */
657c478bd9Sstevel@tonic-gate 	if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION)) {
667c478bd9Sstevel@tonic-gate 		return (CKR_ARGUMENTS_BAD);
677c478bd9Sstevel@tonic-gate 	}
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate 	if (!(flags & CKF_SERIAL_SESSION)) {
707c478bd9Sstevel@tonic-gate 		return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
717c478bd9Sstevel@tonic-gate 	}
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	if (meta_slotManager_token_write_protected() &&
747c478bd9Sstevel@tonic-gate 	    (flags & CKF_RW_SESSION)) {
757c478bd9Sstevel@tonic-gate 		return (CKR_TOKEN_WRITE_PROTECTED);
767c478bd9Sstevel@tonic-gate 	}
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	rv = meta_session_alloc(&new_session);
797c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
807c478bd9Sstevel@tonic-gate 		return (rv);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate 	new_session->session_flags = flags;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	rv = meta_session_activate(new_session);
857c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK) {
867c478bd9Sstevel@tonic-gate 		meta_session_dealloc(new_session);
877c478bd9Sstevel@tonic-gate 		return (rv);
887c478bd9Sstevel@tonic-gate 	}
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	*phSession = (CK_SESSION_HANDLE) new_session;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	num_meta_sessions++;
937c478bd9Sstevel@tonic-gate 	if (flags & CKF_RW_SESSION) {
947c478bd9Sstevel@tonic-gate 		num_rw_meta_sessions++;
957c478bd9Sstevel@tonic-gate 	}
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	return (CKR_OK);
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * meta_CloseSession
1037c478bd9Sstevel@tonic-gate  *
1047c478bd9Sstevel@tonic-gate  */
1057c478bd9Sstevel@tonic-gate CK_RV
meta_CloseSession(CK_SESSION_HANDLE hSession)1067c478bd9Sstevel@tonic-gate meta_CloseSession(CK_SESSION_HANDLE hSession)
1077c478bd9Sstevel@tonic-gate {
1087c478bd9Sstevel@tonic-gate 	CK_RV rv;
1097c478bd9Sstevel@tonic-gate 	meta_session_t *session;
1107c478bd9Sstevel@tonic-gate 	CK_FLAGS flags;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
1137c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
1147c478bd9Sstevel@tonic-gate 		return (rv);
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	/* save info about session flags before they are destroyed */
1177c478bd9Sstevel@tonic-gate 	flags = session->session_flags;
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	rv = meta_session_deactivate(session, B_FALSE);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	if (rv == CKR_OK)
1227c478bd9Sstevel@tonic-gate 		meta_session_dealloc(session);
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	num_meta_sessions--;
1257c478bd9Sstevel@tonic-gate 	if (flags & CKF_RW_SESSION) {
1267c478bd9Sstevel@tonic-gate 		num_rw_meta_sessions--;
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	return (rv);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate /*
1347c478bd9Sstevel@tonic-gate  * meta_CloseAllSessions
1357c478bd9Sstevel@tonic-gate  *
1367c478bd9Sstevel@tonic-gate  * This is a simple loop that closes the sessionlist head (resulting in a
1377c478bd9Sstevel@tonic-gate  * new list head) until the list is empty.
1387c478bd9Sstevel@tonic-gate  *
1397c478bd9Sstevel@tonic-gate  */
1407c478bd9Sstevel@tonic-gate CK_RV
meta_CloseAllSessions(CK_SLOT_ID slotID)1417c478bd9Sstevel@tonic-gate meta_CloseAllSessions(CK_SLOT_ID slotID)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate 	CK_RV rv;
1447c478bd9Sstevel@tonic-gate 	meta_session_t *session;
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	if (!metaslot_enabled) {
1477c478bd9Sstevel@tonic-gate 		return (CKR_SLOT_ID_INVALID);
1487c478bd9Sstevel@tonic-gate 	}
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	if (slotID != METASLOT_SLOTID)
1517c478bd9Sstevel@tonic-gate 		return (CKR_SLOT_ID_INVALID);
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
1547c478bd9Sstevel@tonic-gate 	while ((session = meta_sessionlist_head) != NULL) {
1557c478bd9Sstevel@tonic-gate 		rv = meta_handle2session((CK_SESSION_HANDLE)session, &session);
1567c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK) {
1577c478bd9Sstevel@tonic-gate 			/*NOTREACHED*/
1587c478bd9Sstevel@tonic-gate 			(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
1597c478bd9Sstevel@tonic-gate 			return (CKR_FUNCTION_FAILED);
1607c478bd9Sstevel@tonic-gate 		}
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 		(void) meta_session_deactivate(session, B_TRUE);
1637c478bd9Sstevel@tonic-gate 		meta_session_dealloc(session);
1647c478bd9Sstevel@tonic-gate 	}
1657c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	/* All open sessions should be closed, just reset the variables */
1687c478bd9Sstevel@tonic-gate 	num_meta_sessions = 0;
1697c478bd9Sstevel@tonic-gate 	num_rw_meta_sessions = 0;
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	return (CKR_OK);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate /*
1767c478bd9Sstevel@tonic-gate  * meta_GetSessionInfo
1777c478bd9Sstevel@tonic-gate  *
1787c478bd9Sstevel@tonic-gate  */
1797c478bd9Sstevel@tonic-gate CK_RV
meta_GetSessionInfo(CK_SESSION_HANDLE hSession,CK_SESSION_INFO_PTR pInfo)1807c478bd9Sstevel@tonic-gate meta_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	CK_RV rv;
1837c478bd9Sstevel@tonic-gate 	meta_session_t *session;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	if (pInfo == NULL)
1867c478bd9Sstevel@tonic-gate 		return (CKR_ARGUMENTS_BAD);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
1897c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
1907c478bd9Sstevel@tonic-gate 		return (rv);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	pInfo->slotID = METASLOT_SLOTID;
1937c478bd9Sstevel@tonic-gate 	pInfo->flags = session->session_flags;
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	if (metaslot_logged_in()) {
1967c478bd9Sstevel@tonic-gate 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
1977c478bd9Sstevel@tonic-gate 			pInfo->state = CKS_RO_USER_FUNCTIONS;
1987c478bd9Sstevel@tonic-gate 		} else {
1997c478bd9Sstevel@tonic-gate 			pInfo->state = CKS_RW_USER_FUNCTIONS;
2007c478bd9Sstevel@tonic-gate 		}
2017c478bd9Sstevel@tonic-gate 	} else {
2027c478bd9Sstevel@tonic-gate 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
2037c478bd9Sstevel@tonic-gate 			pInfo->state = CKS_RO_PUBLIC_SESSION;
2047c478bd9Sstevel@tonic-gate 		} else {
2057c478bd9Sstevel@tonic-gate 			pInfo->state = CKS_RW_PUBLIC_SESSION;
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 	}
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	pInfo->ulDeviceError = 0;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	REFRELEASE(session);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	return (CKR_OK);
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate CK_RV
meta_getopstatelen(meta_session_t * session,CK_ULONG * out_length)2177c478bd9Sstevel@tonic-gate meta_getopstatelen(meta_session_t *session, CK_ULONG *out_length)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	CK_RV rv = CKR_OK;
2207c478bd9Sstevel@tonic-gate 	slot_session_t *slot_session;
2217c478bd9Sstevel@tonic-gate 	CK_ULONG length;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	*out_length = sizeof (meta_opstate_t);
224d3a28a55Sdinak 	if (session->op1.type != 0) {
2257c478bd9Sstevel@tonic-gate 		slot_session = session->op1.session;
2267c478bd9Sstevel@tonic-gate 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
2277c478bd9Sstevel@tonic-gate 		    slot_session->hSession, NULL, &length);
2287c478bd9Sstevel@tonic-gate 		if (rv == CKR_OK)
2297c478bd9Sstevel@tonic-gate 			*out_length += length;
2307c478bd9Sstevel@tonic-gate 	}
2317c478bd9Sstevel@tonic-gate 	return (rv);
2327c478bd9Sstevel@tonic-gate }
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /*
2357c478bd9Sstevel@tonic-gate  * meta_GetOperationState
2367c478bd9Sstevel@tonic-gate  *
2377c478bd9Sstevel@tonic-gate  */
2387c478bd9Sstevel@tonic-gate CK_RV
meta_GetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG_PTR pulOperationStateLen)2397c478bd9Sstevel@tonic-gate meta_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
2407c478bd9Sstevel@tonic-gate     CK_ULONG_PTR pulOperationStateLen)
2417c478bd9Sstevel@tonic-gate {
2427c478bd9Sstevel@tonic-gate 	CK_RV rv;
2437c478bd9Sstevel@tonic-gate 	meta_session_t *session;
2447c478bd9Sstevel@tonic-gate 	slot_session_t *slot_session = NULL;
2457c478bd9Sstevel@tonic-gate 	meta_opstate_t opstate;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	if (pulOperationStateLen == NULL)
2487c478bd9Sstevel@tonic-gate 		return (CKR_ARGUMENTS_BAD);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
2517c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
2527c478bd9Sstevel@tonic-gate 		return (rv);
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	/*
2557c478bd9Sstevel@tonic-gate 	 * If no operation is active, then bail out.
2567c478bd9Sstevel@tonic-gate 	 */
257d3a28a55Sdinak 	if (session->op1.type == 0) {
2587c478bd9Sstevel@tonic-gate 		rv = CKR_OPERATION_NOT_INITIALIZED;
2597c478bd9Sstevel@tonic-gate 		goto endgetopstate;
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	/*
2637c478bd9Sstevel@tonic-gate 	 * If the caller did not give an OpState buffer,
2647c478bd9Sstevel@tonic-gate 	 * shortcut and just return the size needed to hold
2657c478bd9Sstevel@tonic-gate 	 * a metaslot OpState record later.
2667c478bd9Sstevel@tonic-gate 	 * The actual size of the returned state will be the
2677c478bd9Sstevel@tonic-gate 	 * sizeof(meta_opstate_t) + SIZE (op1 state),
2687c478bd9Sstevel@tonic-gate 	 * so we have to get the size of
2697c478bd9Sstevel@tonic-gate 	 * the operation states now.
2707c478bd9Sstevel@tonic-gate 	 */
2717c478bd9Sstevel@tonic-gate 	if (pOperationState == NULL) {
2727c478bd9Sstevel@tonic-gate 		rv = meta_getopstatelen(session, pulOperationStateLen);
2737c478bd9Sstevel@tonic-gate 		REFRELEASE(session);
2747c478bd9Sstevel@tonic-gate 		return (rv);
2757c478bd9Sstevel@tonic-gate 	}
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	/*
2787c478bd9Sstevel@tonic-gate 	 * To be here, the caller must have supplied an
2797c478bd9Sstevel@tonic-gate 	 * already initialized meta_opstate_t pointer.
2807c478bd9Sstevel@tonic-gate 	 * Use it to get the real state info from the operation(s).
2817c478bd9Sstevel@tonic-gate 	 *
2827c478bd9Sstevel@tonic-gate 	 * The format of the Metaslot Opstate record:
2837c478bd9Sstevel@tonic-gate 	 * {
2847c478bd9Sstevel@tonic-gate 	 *    struct metaopstate
2857c478bd9Sstevel@tonic-gate 	 *    [ op1 state data ]
2867c478bd9Sstevel@tonic-gate 	 * }
2877c478bd9Sstevel@tonic-gate 	 */
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	/*
2907c478bd9Sstevel@tonic-gate 	 * If the buffer is not even big enough for the metaslot
2917c478bd9Sstevel@tonic-gate 	 * opstate data, return error and set the returned
2927c478bd9Sstevel@tonic-gate 	 * state length to indicate the minimum needed.
2937c478bd9Sstevel@tonic-gate 	 */
2947c478bd9Sstevel@tonic-gate 	if (*pulOperationStateLen < sizeof (meta_opstate_t)) {
2957c478bd9Sstevel@tonic-gate 		rv = meta_getopstatelen(session, pulOperationStateLen);
2967c478bd9Sstevel@tonic-gate 		/*
2977c478bd9Sstevel@tonic-gate 		 * Remap the error so the caller knows that they
2987c478bd9Sstevel@tonic-gate 		 * used an invalid buffer size in the first place.
2997c478bd9Sstevel@tonic-gate 		 */
3007c478bd9Sstevel@tonic-gate 		if (rv == CKR_OK)
3017c478bd9Sstevel@tonic-gate 			rv = CKR_BUFFER_TOO_SMALL;
3027c478bd9Sstevel@tonic-gate 		goto endgetopstate;
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	(void) memset(&opstate, 0, sizeof (meta_opstate_t));
3067c478bd9Sstevel@tonic-gate 	opstate.magic_marker = METASLOT_OPSTATE_MAGIC;
3077c478bd9Sstevel@tonic-gate 
308d3a28a55Sdinak 	if (session->op1.type != 0) {
3097c478bd9Sstevel@tonic-gate 		slot_session = session->op1.session;
3107c478bd9Sstevel@tonic-gate 		opstate.state[0].op_type = session->op1.type;
3117c478bd9Sstevel@tonic-gate 		opstate.state[0].op_slotnum = slot_session->slotnum;
3127c478bd9Sstevel@tonic-gate 		opstate.state[0].op_state_len = *pulOperationStateLen -
3137c478bd9Sstevel@tonic-gate 		    sizeof (meta_opstate_t);
314f4526a4aShaimay 		opstate.state[0].op_init_app = session->init.app;
315f4526a4aShaimay 		opstate.state[0].op_init_done = session->init.done;
3167c478bd9Sstevel@tonic-gate 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
3177c478bd9Sstevel@tonic-gate 		    slot_session->hSession,
3187c478bd9Sstevel@tonic-gate 		    pOperationState + sizeof (meta_opstate_t),
3197c478bd9Sstevel@tonic-gate 		    &(opstate.state[0].op_state_len));
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 		if (rv == CKR_BUFFER_TOO_SMALL) {
3227c478bd9Sstevel@tonic-gate 			/*
3237c478bd9Sstevel@tonic-gate 			 * This should not happen, but if it does,
3247c478bd9Sstevel@tonic-gate 			 * recalculate the entire size needed
3257c478bd9Sstevel@tonic-gate 			 * and return the error.
3267c478bd9Sstevel@tonic-gate 			 */
3277c478bd9Sstevel@tonic-gate 			rv = meta_getopstatelen(session, pulOperationStateLen);
3287c478bd9Sstevel@tonic-gate 			if (rv == CKR_OK)
3297c478bd9Sstevel@tonic-gate 				rv = CKR_BUFFER_TOO_SMALL;
3307c478bd9Sstevel@tonic-gate 		}
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK)
3337c478bd9Sstevel@tonic-gate 			goto endgetopstate;
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate endgetopstate:
3377c478bd9Sstevel@tonic-gate 	if (rv == CKR_OK && pOperationState != NULL) {
3387c478bd9Sstevel@tonic-gate 		(void) memcpy(pOperationState, (void *)&opstate,
3397c478bd9Sstevel@tonic-gate 		    sizeof (meta_opstate_t));
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 		*pulOperationStateLen = sizeof (meta_opstate_t) +
3427c478bd9Sstevel@tonic-gate 		    opstate.state[0].op_state_len;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	REFRELEASE(session);
3467c478bd9Sstevel@tonic-gate 	return (rv);
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate static CK_RV
meta_set_opstate(slot_session_t * slot_session,meta_object_t * meta_enc_key,meta_object_t * meta_auth_key,struct opstate_data * state,CK_BYTE * databuf)3507c478bd9Sstevel@tonic-gate meta_set_opstate(slot_session_t *slot_session,
3517c478bd9Sstevel@tonic-gate 		meta_object_t *meta_enc_key,
3527c478bd9Sstevel@tonic-gate 		meta_object_t *meta_auth_key,
3537c478bd9Sstevel@tonic-gate 		struct opstate_data *state,
3547c478bd9Sstevel@tonic-gate 		CK_BYTE *databuf)
3557c478bd9Sstevel@tonic-gate {
3567c478bd9Sstevel@tonic-gate 	CK_RV rv;
357d3a28a55Sdinak 	static CK_ULONG encrypt_optypes = (CKF_ENCRYPT | CKF_DECRYPT);
358d3a28a55Sdinak 	static CK_ULONG sign_optypes = (CKF_SIGN | CKF_VERIFY |
359d3a28a55Sdinak 	    CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER);
3607c478bd9Sstevel@tonic-gate 	slot_object_t *enc_key_obj = NULL, *auth_key_obj = NULL;
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	if (state->op_type & encrypt_optypes) {
363d3a28a55Sdinak 		rv = meta_object_get_clone(meta_enc_key, slot_session->slotnum,
3647c478bd9Sstevel@tonic-gate 		    slot_session, &enc_key_obj);
3657c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK) {
3667c478bd9Sstevel@tonic-gate 			return (rv);
3677c478bd9Sstevel@tonic-gate 		}
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 	if (state->op_type & sign_optypes) {
370d3a28a55Sdinak 		rv = meta_object_get_clone(meta_auth_key, slot_session->slotnum,
3717c478bd9Sstevel@tonic-gate 		    slot_session, &auth_key_obj);
3727c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK) {
3737c478bd9Sstevel@tonic-gate 			return (rv);
3747c478bd9Sstevel@tonic-gate 		}
3757c478bd9Sstevel@tonic-gate 	}
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	/*
3787c478bd9Sstevel@tonic-gate 	 * Check to see if the keys are needed to restore the
3797c478bd9Sstevel@tonic-gate 	 * state on the first operation.
3807c478bd9Sstevel@tonic-gate 	 */
3817c478bd9Sstevel@tonic-gate 	rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
3827c478bd9Sstevel@tonic-gate 	    slot_session->hSession, databuf, state->op_state_len,
3837c478bd9Sstevel@tonic-gate 	    enc_key_obj ? enc_key_obj->hObject : CK_INVALID_HANDLE,
3847c478bd9Sstevel@tonic-gate 	    auth_key_obj ? auth_key_obj->hObject : CK_INVALID_HANDLE);
3857c478bd9Sstevel@tonic-gate 	/*
3867c478bd9Sstevel@tonic-gate 	 * If the operation did not need a key, try again.
3877c478bd9Sstevel@tonic-gate 	 */
3887c478bd9Sstevel@tonic-gate 	if (rv == CKR_KEY_NOT_NEEDED) {
3897c478bd9Sstevel@tonic-gate 		rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
3907c478bd9Sstevel@tonic-gate 		    slot_session->hSession, databuf, state->op_state_len,
3917c478bd9Sstevel@tonic-gate 		    CK_INVALID_HANDLE, CK_INVALID_HANDLE);
3927c478bd9Sstevel@tonic-gate 		/*
3937c478bd9Sstevel@tonic-gate 		 * Strange case... If the first try returned
3947c478bd9Sstevel@tonic-gate 		 * KEY_NOT_NEEDED, and this one returns KEY_NEEDED,
3957c478bd9Sstevel@tonic-gate 		 * we want to remap the return so the caller sees
3967c478bd9Sstevel@tonic-gate 		 * the original "CKR_KEY_NOT_NEEDED" return value.
3977c478bd9Sstevel@tonic-gate 		 * This ensures that a correct caller will retry
3987c478bd9Sstevel@tonic-gate 		 * without the unnecessary key argument and this
3997c478bd9Sstevel@tonic-gate 		 * 2nd attempt will not happen again.
4007c478bd9Sstevel@tonic-gate 		 */
4017c478bd9Sstevel@tonic-gate 		if (rv == CKR_KEY_NEEDED) {
4027c478bd9Sstevel@tonic-gate 			rv  = CKR_KEY_NOT_NEEDED;
4037c478bd9Sstevel@tonic-gate 		}
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	return (rv);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate /*
4107c478bd9Sstevel@tonic-gate  * meta_SetOperationState
4117c478bd9Sstevel@tonic-gate  *
4127c478bd9Sstevel@tonic-gate  */
4137c478bd9Sstevel@tonic-gate CK_RV
meta_SetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG ulOperationStateLen,CK_OBJECT_HANDLE hEncryptionKey,CK_OBJECT_HANDLE hAuthenticationKey)4147c478bd9Sstevel@tonic-gate meta_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
4157c478bd9Sstevel@tonic-gate     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
4167c478bd9Sstevel@tonic-gate     CK_OBJECT_HANDLE hAuthenticationKey)
4177c478bd9Sstevel@tonic-gate {
4187c478bd9Sstevel@tonic-gate 	CK_RV rv = CKR_OK;
4197c478bd9Sstevel@tonic-gate 	meta_session_t *session;
4207c478bd9Sstevel@tonic-gate 	slot_session_t *slot_session = NULL;
4217c478bd9Sstevel@tonic-gate 	meta_opstate_t opstate;
4227c478bd9Sstevel@tonic-gate 	meta_object_t *meta_enc_key = NULL, *meta_auth_key = NULL;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	/*
4257c478bd9Sstevel@tonic-gate 	 * Make sure the opstate info buffer is big enough to be valid.
4267c478bd9Sstevel@tonic-gate 	 */
4277c478bd9Sstevel@tonic-gate 	if (ulOperationStateLen < sizeof (meta_opstate_t) ||
4287c478bd9Sstevel@tonic-gate 	    pOperationState == NULL)
4297c478bd9Sstevel@tonic-gate 		return (CKR_ARGUMENTS_BAD);
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	/* Copy the opstate info into the structure */
4327c478bd9Sstevel@tonic-gate 	(void) memcpy(&opstate, pOperationState, sizeof (meta_opstate_t));
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	/* verify that a metaslot operation state was supplied */
4357c478bd9Sstevel@tonic-gate 	if (opstate.magic_marker != METASLOT_OPSTATE_MAGIC)
4367c478bd9Sstevel@tonic-gate 		return (CKR_SAVED_STATE_INVALID);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	/*
4397c478bd9Sstevel@tonic-gate 	 * Now, check the size again to make sure the "real" state
4407c478bd9Sstevel@tonic-gate 	 * data is present.  Length of state provided must be exact.
4417c478bd9Sstevel@tonic-gate 	 */
4427c478bd9Sstevel@tonic-gate 	if (ulOperationStateLen != (sizeof (meta_opstate_t) +
4437c478bd9Sstevel@tonic-gate 	    opstate.state[0].op_state_len))
4447c478bd9Sstevel@tonic-gate 		return (CKR_SAVED_STATE_INVALID);
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
4477c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
4487c478bd9Sstevel@tonic-gate 		return (rv);
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	if (hEncryptionKey != CK_INVALID_HANDLE) {
4517c478bd9Sstevel@tonic-gate 		rv = meta_handle2object(hEncryptionKey, &meta_enc_key);
4527c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK)
4537c478bd9Sstevel@tonic-gate 			goto cleanup;
4547c478bd9Sstevel@tonic-gate 	}
4557c478bd9Sstevel@tonic-gate 	if (hAuthenticationKey != CK_INVALID_HANDLE) {
4567c478bd9Sstevel@tonic-gate 		rv = meta_handle2object(hAuthenticationKey, &meta_auth_key);
4577c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK)
4587c478bd9Sstevel@tonic-gate 			goto cleanup;
4597c478bd9Sstevel@tonic-gate 	}
4607c478bd9Sstevel@tonic-gate 
461d3a28a55Sdinak 	if (opstate.state[0].op_type != 0) {
462d3a28a55Sdinak 		if (session->op1.type != 0)
4637c478bd9Sstevel@tonic-gate 			meta_operation_cleanup(session, session->op1.type,
4647c478bd9Sstevel@tonic-gate 			    B_FALSE);
4657c478bd9Sstevel@tonic-gate 
466504c4cfbSKrishna Yenduri 		if (session->op1.session != NULL) {
467504c4cfbSKrishna Yenduri 			slot_session = session->op1.session;
468504c4cfbSKrishna Yenduri 		} else {
4697c478bd9Sstevel@tonic-gate 			rv = meta_get_slot_session(opstate.state[0].op_slotnum,
4707c478bd9Sstevel@tonic-gate 			    &slot_session, session->session_flags);
4717c478bd9Sstevel@tonic-gate 			if (rv != CKR_OK)
4727c478bd9Sstevel@tonic-gate 				goto cleanup;
473504c4cfbSKrishna Yenduri 		}
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 		session->op1.type = opstate.state[0].op_type;
4767c478bd9Sstevel@tonic-gate 		session->op1.session = slot_session;
477f4526a4aShaimay 		session->init.app = opstate.state[0].op_init_app;
478f4526a4aShaimay 		session->init.done = opstate.state[0].op_init_done;
4797c478bd9Sstevel@tonic-gate 
480d3a28a55Sdinak 		rv = meta_set_opstate(slot_session, meta_enc_key,
481d3a28a55Sdinak 		    meta_auth_key, &(opstate.state[0]),
4827c478bd9Sstevel@tonic-gate 		    pOperationState + sizeof (meta_opstate_t));
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 		if (rv != CKR_OK) {
4857c478bd9Sstevel@tonic-gate 			meta_operation_cleanup(session, session->op1.type,
4867c478bd9Sstevel@tonic-gate 			    FALSE);
4877c478bd9Sstevel@tonic-gate 			goto cleanup;
4887c478bd9Sstevel@tonic-gate 		}
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate cleanup:
4927c478bd9Sstevel@tonic-gate 	if (meta_enc_key != NULL)
4937c478bd9Sstevel@tonic-gate 		OBJRELEASE(meta_enc_key);
4947c478bd9Sstevel@tonic-gate 	if (meta_auth_key != NULL)
4957c478bd9Sstevel@tonic-gate 		OBJRELEASE(meta_auth_key);
4967c478bd9Sstevel@tonic-gate 	REFRELEASE(session);
4977c478bd9Sstevel@tonic-gate 	return (rv);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate /*
5017c478bd9Sstevel@tonic-gate  * meta_Login
5027c478bd9Sstevel@tonic-gate  *
5037c478bd9Sstevel@tonic-gate  * This allows the user to login to the object token. The metaslot itself
5047c478bd9Sstevel@tonic-gate  * does not have any kind of PIN.
5057c478bd9Sstevel@tonic-gate  *
5067c478bd9Sstevel@tonic-gate  */
5077c478bd9Sstevel@tonic-gate CK_RV
meta_Login(CK_SESSION_HANDLE hSession,CK_USER_TYPE userType,CK_UTF8CHAR_PTR pPin,CK_ULONG ulPinLen)5087c478bd9Sstevel@tonic-gate meta_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
5097c478bd9Sstevel@tonic-gate     CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate 	CK_RV rv;
5127c478bd9Sstevel@tonic-gate 	meta_session_t *session;
5137c478bd9Sstevel@tonic-gate 	slot_session_t *login_session = NULL;
5147c478bd9Sstevel@tonic-gate 	CK_TOKEN_INFO token_info;
5157c478bd9Sstevel@tonic-gate 	CK_SLOT_ID true_id, fw_st_id;
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
5187c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
5197c478bd9Sstevel@tonic-gate 		return (rv);
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	if (metaslot_logged_in()) {
5227c478bd9Sstevel@tonic-gate 		rv = CKR_USER_ALREADY_LOGGED_IN;
5237c478bd9Sstevel@tonic-gate 		goto finish;
5247c478bd9Sstevel@tonic-gate 	}
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	/* Note: CKU_SO is not supported. */
5277c478bd9Sstevel@tonic-gate 	if (userType != CKU_USER) {
5287c478bd9Sstevel@tonic-gate 		rv = CKR_USER_TYPE_INVALID;
5297c478bd9Sstevel@tonic-gate 		goto finish;
5307c478bd9Sstevel@tonic-gate 	}
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	rv = meta_get_slot_session(get_keystore_slotnum(), &login_session,
5337c478bd9Sstevel@tonic-gate 	    session->session_flags);
5347c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
5357c478bd9Sstevel@tonic-gate 		goto finish;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	fw_st_id = login_session->fw_st_id;
5397c478bd9Sstevel@tonic-gate 	rv = FUNCLIST(fw_st_id)->C_Login(login_session->hSession, userType,
5407c478bd9Sstevel@tonic-gate 	    pPin, ulPinLen);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK) {
5437c478bd9Sstevel@tonic-gate 		goto finish;
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/*
5477c478bd9Sstevel@tonic-gate 	 * Note:
5487c478bd9Sstevel@tonic-gate 	 *
5497c478bd9Sstevel@tonic-gate 	 * For some slots (eg: the pkcs11_softtoken.so), C_Login()
5507c478bd9Sstevel@tonic-gate 	 * returning OK don't mean that the login is truely
5517c478bd9Sstevel@tonic-gate 	 * successful.  For pkcs11_softtoken.so, the CKF_USER_PIN_TO_BE_CHANGED
5527c478bd9Sstevel@tonic-gate 	 * is set to indicate that the pin needs to be changed, and
5537c478bd9Sstevel@tonic-gate 	 * the login is not really successful.  We will check
5547c478bd9Sstevel@tonic-gate 	 * that flag for this special condition.  Checking for
5557c478bd9Sstevel@tonic-gate 	 * this flag shouldn't be harmful for other slots that doesn't
5567c478bd9Sstevel@tonic-gate 	 * behave like pkcs11_softtoken.so.
5577c478bd9Sstevel@tonic-gate 	 */
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	true_id = TRUEID(fw_st_id);
5607c478bd9Sstevel@tonic-gate 	rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, &token_info);
5617c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK) {
5627c478bd9Sstevel@tonic-gate 		goto finish;
5637c478bd9Sstevel@tonic-gate 	}
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	metaslot_set_logged_in_flag(B_TRUE);
5667c478bd9Sstevel@tonic-gate 	if (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) {
5677c478bd9Sstevel@tonic-gate 		metaslot_set_logged_in_flag(B_FALSE);
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate finish:
5707c478bd9Sstevel@tonic-gate 	if (login_session)
5717c478bd9Sstevel@tonic-gate 		meta_release_slot_session(login_session);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	REFRELEASE(session);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	return (rv);
5767c478bd9Sstevel@tonic-gate }
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate /*
5797c478bd9Sstevel@tonic-gate  * meta_Logout
5807c478bd9Sstevel@tonic-gate  *
5817c478bd9Sstevel@tonic-gate  */
5827c478bd9Sstevel@tonic-gate CK_RV
meta_Logout(CK_SESSION_HANDLE hSession)5837c478bd9Sstevel@tonic-gate meta_Logout(CK_SESSION_HANDLE hSession)
5847c478bd9Sstevel@tonic-gate {
5857c478bd9Sstevel@tonic-gate 	CK_RV rv = CKR_OK;
5867c478bd9Sstevel@tonic-gate 	meta_session_t *session;
5877c478bd9Sstevel@tonic-gate 	slot_session_t *logout_session = NULL;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	rv = meta_handle2session(hSession, &session);
5907c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
5917c478bd9Sstevel@tonic-gate 		return (rv);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	if (!metaslot_logged_in()) {
5947c478bd9Sstevel@tonic-gate 		rv = CKR_USER_NOT_LOGGED_IN;
5957c478bd9Sstevel@tonic-gate 		goto finish;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	rv = meta_get_slot_session(get_keystore_slotnum(), &logout_session,
5997c478bd9Sstevel@tonic-gate 	    session->session_flags);
6007c478bd9Sstevel@tonic-gate 	if (rv != CKR_OK)
6017c478bd9Sstevel@tonic-gate 		goto finish;
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	rv = FUNCLIST(logout_session->fw_st_id)->C_Logout(
6047c478bd9Sstevel@tonic-gate 	    logout_session->hSession);
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	/* If the C_Logout fails, just ignore the error. */
6077c478bd9Sstevel@tonic-gate 	metaslot_set_logged_in_flag(B_FALSE);
608*d288ba74SAnthony Scarpino 	(void) meta_token_object_deactivate(PRIVATE_TOKEN);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate finish:
6117c478bd9Sstevel@tonic-gate 	if (logout_session)
6127c478bd9Sstevel@tonic-gate 		meta_release_slot_session(logout_session);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	REFRELEASE(session);
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	return (rv);
6177c478bd9Sstevel@tonic-gate }
618