text: /* SPDX-License-Identifier: BSD-2-Clause */
  
  #include <stdbool.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "checks.h"
  #include "general.h"
  #include "log.h"
  #include "mutex.h"
  #include "pkcs11.h"
  #include "session.h"
  #include "session_table.h"
  #include "token.h"
  #include "tpm.h"
  #include "utils.h"
  
  static CK_RV check_max_sessions(session_table *s_table) {
  
      CK_ULONG all;
  
      session_table_get_cnt(s_table, &all, NULL, NULL);
  
      return (all > MAX_NUM_OF_SESSIONS) ?
          CKR_SESSION_COUNT : CKR_OK;
  }
  
  #define TOKID_SESSION_SHIFT ((sizeof(CK_SESSION_HANDLE) * 8) - 8)
  
  static inline void add_tokid_to_session_handle(unsigned tokid,
          CK_SESSION_HANDLE *handle) {
  
      /*
       * Plop the token id in the high byte
       */
      *handle |= ((typeof(*handle))tokid << TOKID_SESSION_SHIFT);
  }
  
  static inline unsigned get_tokid_from_session_handle_and_cleanse(
          CK_SESSION_HANDLE *handle) {
  
      /*
       * Get the token id from the high byte
       */
      unsigned tokid = (*handle >> TOKID_SESSION_SHIFT);
  
      /*
       * drop the top byte, this is a simple way to deal
       * with CK_SESSION_HANDLE being architecture dependent
       * in size.
       */
      CK_SESSION_HANDLE tmp = *handle;
      tmp = tmp << 8;
      tmp = tmp >> 8;
      *handle = tmp;
  
      return tokid;
  }
  
  CK_RV session_open(CK_SLOT_ID slot_id, CK_FLAGS flags, void *application,
  		CK_NOTIFY notify, CK_SESSION_HANDLE *session) {
  
      (void) notify;
      (void) application; /* can be null */
  
      if (!(flags & CKF_SERIAL_SESSION)) {
          return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
      }
  
      CK_RV rv = CKR_GENERAL_ERROR;
  
  	check_pointer(session);
  
  	token *t = slot_get_token(slot_id);
  	if (!t) {
  	    return CKR_SLOT_ID_INVALID;
  	}
  
  	rv = check_max_sessions(t->s_table);
  	if (rv != CKR_OK) {
  	    return rv;
  	}
  
  	/*
  	 * Cannot open an R/O session when the SO is logged in
  	 */
  	if ((!(flags & CKF_RW_SESSION)) && (t->login_state == token_so_logged_in)) {
  	    return CKR_SESSION_READ_WRITE_SO_EXISTS;
  	}
  
  	rv = session_table_new_entry(t->s_table, session, t, flags);
      if (rv != CKR_OK) {
          return rv;
      }
  
  	add_tokid_to_session_handle(t->id, session);
  
  	return CKR_OK;
  }
  
  CK_RV session_close(CK_SESSION_HANDLE session) {
  
      token *t = NULL;
      unsigned tokid = get_tokid_from_session_handle_and_cleanse(&session);
      check_slot_id(tokid, t, CKR_SESSION_HANDLE_INVALID);
  
      return session_table_free_ctx(t, session);
  }
  
  CK_RV session_closeall(CK_SLOT_ID slot_id) {
  
      token *t;
      check_slot_id(slot_id, t, CKR_SLOT_ID_INVALID);
  
      return session_table_free_ctx_all(t);
  }
  
  CK_RV session_lookup(CK_SESSION_HANDLE session, token **tok, session_ctx **ctx) {
  
      token *tmp = NULL;
      unsigned tokid = get_tokid_from_session_handle_and_cleanse(&session);
      check_slot_id(tokid, tmp, CKR_SESSION_HANDLE_INVALID);
  
      *ctx = session_table_lookup(tmp->s_table, session);
      if (!*ctx) {
          return CKR_SESSION_HANDLE_INVALID;
      }
  
      token_lock(tmp);
  
      *tok = tmp;
  
      return CKR_OK;
  }
  