Files
intel-linux-sgx/SampleCode/LocalAttestation/LocalAttestationCode/EnclaveMessageExchange.cpp
T
Li, Xun c505e6129a Linux 2.6 Open Source Gold Release
Added support for Reproducible Enclave Build using Docker file.
Added support for Intel AVX-512 instructions and Intel SHA Extensions New Instructions (SHA-NI) in trusted libraries.
Support both EPID and ECDSA based quote for quoting related interfaces in sgx_uae_service library.
Updated key exchange library to support both EPID and ECDSA based remote attestation.
Support new interface to check platform information blob from remote attestation response message.
Fixed bugs.

Signed-off-by: Li, Xun <xun.li@intel.com>
2019-07-05 14:12:24 +08:00

711 lines
24 KiB
C++

/*
* Copyright (C) 2011-2019 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sgx_trts.h"
#include "sgx_utils.h"
#include "EnclaveMessageExchange.h"
#include "sgx_eid.h"
#include "error_codes.h"
#include "sgx_ecp_types.h"
#include "sgx_thread.h"
#include <map>
#include "dh_session_protocol.h"
#include "sgx_dh.h"
#include "sgx_tcrypto.h"
#include "LocalAttestationCode_t.h"
#ifdef __cplusplus
extern "C" {
#endif
uint32_t enclave_to_enclave_call_dispatcher(char* decrypted_data, size_t decrypted_data_length, char** resp_buffer, size_t* resp_length);
uint32_t message_exchange_response_generator(char* decrypted_data, char** resp_buffer, size_t* resp_length);
uint32_t verify_peer_enclave_trust(sgx_dh_session_enclave_identity_t* peer_enclave_identity);
#ifdef __cplusplus
}
#endif
#define MAX_SESSION_COUNT 16
//number of open sessions
uint32_t g_session_count = 0;
ATTESTATION_STATUS generate_session_id(uint32_t *session_id);
//Array of open session ids
session_id_tracker_t *g_session_id_tracker[MAX_SESSION_COUNT];
//Map between the source enclave id and the session information associated with that particular session
std::map<sgx_enclave_id_t, dh_session_t>g_dest_session_info_map;
//Create a session with the destination enclave
ATTESTATION_STATUS create_session(sgx_enclave_id_t src_enclave_id,
sgx_enclave_id_t dest_enclave_id,
dh_session_t *session_info)
{
sgx_dh_msg1_t dh_msg1; //Diffie-Hellman Message 1
sgx_key_128bit_t dh_aek; // Session Key
sgx_dh_msg2_t dh_msg2; //Diffie-Hellman Message 2
sgx_dh_msg3_t dh_msg3; //Diffie-Hellman Message 3
uint32_t session_id;
uint32_t retstatus;
sgx_status_t status = SGX_SUCCESS;
sgx_dh_session_t sgx_dh_session;
sgx_dh_session_enclave_identity_t responder_identity;
if(!session_info)
{
return INVALID_PARAMETER_ERROR;
}
memset(&dh_aek,0, sizeof(sgx_key_128bit_t));
memset(&dh_msg1, 0, sizeof(sgx_dh_msg1_t));
memset(&dh_msg2, 0, sizeof(sgx_dh_msg2_t));
memset(&dh_msg3, 0, sizeof(sgx_dh_msg3_t));
memset(session_info, 0, sizeof(dh_session_t));
//Intialize the session as a session initiator
status = sgx_dh_init_session(SGX_DH_SESSION_INITIATOR, &sgx_dh_session);
if(SGX_SUCCESS != status)
{
return status;
}
//Ocall to request for a session with the destination enclave and obtain session id and Message 1 if successful
status = session_request_ocall(&retstatus, src_enclave_id, dest_enclave_id, &dh_msg1, &session_id);
if (status == SGX_SUCCESS)
{
if ((ATTESTATION_STATUS)retstatus != SUCCESS)
return ((ATTESTATION_STATUS)retstatus);
}
else
{
return ATTESTATION_SE_ERROR;
}
//Process the message 1 obtained from desination enclave and generate message 2
status = sgx_dh_initiator_proc_msg1(&dh_msg1, &dh_msg2, &sgx_dh_session);
if(SGX_SUCCESS != status)
{
return status;
}
//Send Message 2 to Destination Enclave and get Message 3 in return
status = exchange_report_ocall(&retstatus, src_enclave_id, dest_enclave_id, &dh_msg2, &dh_msg3, session_id);
if (status == SGX_SUCCESS)
{
if ((ATTESTATION_STATUS)retstatus != SUCCESS)
return ((ATTESTATION_STATUS)retstatus);
}
else
{
return ATTESTATION_SE_ERROR;
}
//Process Message 3 obtained from the destination enclave
status = sgx_dh_initiator_proc_msg3(&dh_msg3, &sgx_dh_session, &dh_aek, &responder_identity);
if(SGX_SUCCESS != status)
{
return status;
}
// Verify the identity of the destination enclave
if(verify_peer_enclave_trust(&responder_identity) != SUCCESS)
{
return INVALID_SESSION;
}
memcpy(session_info->active.AEK, &dh_aek, sizeof(sgx_key_128bit_t));
session_info->session_id = session_id;
session_info->active.counter = 0;
session_info->status = ACTIVE;
memset(&dh_aek,0, sizeof(sgx_key_128bit_t));
return status;
}
//Handle the request from Source Enclave for a session
ATTESTATION_STATUS session_request(sgx_enclave_id_t src_enclave_id,
sgx_dh_msg1_t *dh_msg1,
uint32_t *session_id )
{
dh_session_t session_info;
sgx_dh_session_t sgx_dh_session;
sgx_status_t status = SGX_SUCCESS;
if(!session_id || !dh_msg1)
{
return INVALID_PARAMETER_ERROR;
}
//Intialize the session as a session responder
status = sgx_dh_init_session(SGX_DH_SESSION_RESPONDER, &sgx_dh_session);
if(SGX_SUCCESS != status)
{
return status;
}
//get a new SessionID
if ((status = (sgx_status_t)generate_session_id(session_id)) != SUCCESS)
return status; //no more sessions available
//Allocate memory for the session id tracker
g_session_id_tracker[*session_id] = (session_id_tracker_t *)malloc(sizeof(session_id_tracker_t));
if(!g_session_id_tracker[*session_id])
{
return MALLOC_ERROR;
}
memset(g_session_id_tracker[*session_id], 0, sizeof(session_id_tracker_t));
g_session_id_tracker[*session_id]->session_id = *session_id;
session_info.status = IN_PROGRESS;
//Generate Message1 that will be returned to Source Enclave
status = sgx_dh_responder_gen_msg1((sgx_dh_msg1_t*)dh_msg1, &sgx_dh_session);
if(SGX_SUCCESS != status)
{
SAFE_FREE(g_session_id_tracker[*session_id]);
return status;
}
memcpy(&session_info.in_progress.dh_session, &sgx_dh_session, sizeof(sgx_dh_session_t));
//Store the session information under the correspoding source enlave id key
g_dest_session_info_map.insert(std::pair<sgx_enclave_id_t, dh_session_t>(src_enclave_id, session_info));
return status;
}
//Verify Message 2, generate Message3 and exchange Message 3 with Source Enclave
ATTESTATION_STATUS exchange_report(sgx_enclave_id_t src_enclave_id,
sgx_dh_msg2_t *dh_msg2,
sgx_dh_msg3_t *dh_msg3,
uint32_t session_id)
{
sgx_key_128bit_t dh_aek; // Session key
dh_session_t *session_info;
ATTESTATION_STATUS status = SUCCESS;
sgx_dh_session_t sgx_dh_session;
sgx_dh_session_enclave_identity_t initiator_identity;
if(!dh_msg2 || !dh_msg3)
{
return INVALID_PARAMETER_ERROR;
}
memset(&dh_aek,0, sizeof(sgx_key_128bit_t));
do
{
//Retreive the session information for the corresponding source enclave id
std::map<sgx_enclave_id_t, dh_session_t>::iterator it = g_dest_session_info_map.find(src_enclave_id);
if(it != g_dest_session_info_map.end())
{
session_info = &it->second;
}
else
{
status = INVALID_SESSION;
break;
}
if(session_info->status != IN_PROGRESS)
{
status = INVALID_SESSION;
break;
}
memcpy(&sgx_dh_session, &session_info->in_progress.dh_session, sizeof(sgx_dh_session_t));
dh_msg3->msg3_body.additional_prop_length = 0;
//Process message 2 from source enclave and obtain message 3
sgx_status_t se_ret = sgx_dh_responder_proc_msg2(dh_msg2,
dh_msg3,
&sgx_dh_session,
&dh_aek,
&initiator_identity);
if(SGX_SUCCESS != se_ret)
{
status = se_ret;
break;
}
//Verify source enclave's trust
if(verify_peer_enclave_trust(&initiator_identity) != SUCCESS)
{
return INVALID_SESSION;
}
//save the session ID, status and initialize the session nonce
session_info->session_id = session_id;
session_info->status = ACTIVE;
session_info->active.counter = 0;
memcpy(session_info->active.AEK, &dh_aek, sizeof(sgx_key_128bit_t));
memset(&dh_aek,0, sizeof(sgx_key_128bit_t));
g_session_count++;
}while(0);
if(status != SUCCESS)
{
end_session(src_enclave_id);
}
return status;
}
//Request for the response size, send the request message to the destination enclave and receive the response message back
ATTESTATION_STATUS send_request_receive_response(sgx_enclave_id_t src_enclave_id,
sgx_enclave_id_t dest_enclave_id,
dh_session_t *session_info,
char *inp_buff,
size_t inp_buff_len,
size_t max_out_buff_size,
char **out_buff,
size_t* out_buff_len)
{
const uint8_t* plaintext;
uint32_t plaintext_length;
sgx_status_t status;
uint32_t retstatus;
secure_message_t* req_message;
secure_message_t* resp_message;
uint8_t *decrypted_data;
uint32_t decrypted_data_length;
uint32_t plain_text_offset;
uint8_t l_tag[TAG_SIZE];
plaintext = (const uint8_t*)(" ");
plaintext_length = 0;
if(!session_info || !inp_buff)
{
return INVALID_PARAMETER_ERROR;
}
//Check if the nonce for the session has not exceeded 2^32-2 if so end session and start a new session
if(session_info->active.counter == ((uint32_t) - 2))
{
close_session(src_enclave_id, dest_enclave_id);
create_session(src_enclave_id, dest_enclave_id, session_info);
}
//Allocate memory for the AES-GCM request message
req_message = (secure_message_t*)malloc(sizeof(secure_message_t)+ inp_buff_len);
if(!req_message)
{
return MALLOC_ERROR;
}
memset(req_message,0,sizeof(secure_message_t)+ inp_buff_len);
const uint32_t data2encrypt_length = (uint32_t)inp_buff_len;
//Set the payload size to data to encrypt length
req_message->message_aes_gcm_data.payload_size = data2encrypt_length;
//Use the session nonce as the payload IV
memcpy(req_message->message_aes_gcm_data.reserved,&session_info->active.counter,sizeof(session_info->active.counter));
//Set the session ID of the message to the current session id
req_message->session_id = session_info->session_id;
//Prepare the request message with the encrypted payload
status = sgx_rijndael128GCM_encrypt(&session_info->active.AEK, (uint8_t*)inp_buff, data2encrypt_length,
reinterpret_cast<uint8_t *>(&(req_message->message_aes_gcm_data.payload)),
reinterpret_cast<uint8_t *>(&(req_message->message_aes_gcm_data.reserved)),
sizeof(req_message->message_aes_gcm_data.reserved), plaintext, plaintext_length,
&(req_message->message_aes_gcm_data.payload_tag));
if(SGX_SUCCESS != status)
{
SAFE_FREE(req_message);
return status;
}
//Allocate memory for the response payload to be copied
*out_buff = (char*)malloc(max_out_buff_size);
if(!*out_buff)
{
SAFE_FREE(req_message);
return MALLOC_ERROR;
}
memset(*out_buff, 0, max_out_buff_size);
//Allocate memory for the response message
resp_message = (secure_message_t*)malloc(sizeof(secure_message_t)+ max_out_buff_size);
if(!resp_message)
{
SAFE_FREE(req_message);
return MALLOC_ERROR;
}
memset(resp_message, 0, sizeof(secure_message_t)+ max_out_buff_size);
//Ocall to send the request to the Destination Enclave and get the response message back
status = send_request_ocall(&retstatus, src_enclave_id, dest_enclave_id, req_message,
(sizeof(secure_message_t)+ inp_buff_len), max_out_buff_size,
resp_message, (sizeof(secure_message_t)+ max_out_buff_size));
if (status == SGX_SUCCESS)
{
if ((ATTESTATION_STATUS)retstatus != SUCCESS)
{
SAFE_FREE(req_message);
SAFE_FREE(resp_message);
return ((ATTESTATION_STATUS)retstatus);
}
}
else
{
SAFE_FREE(req_message);
SAFE_FREE(resp_message);
return ATTESTATION_SE_ERROR;
}
//Code to process the response message from the Destination Enclave
decrypted_data_length = resp_message->message_aes_gcm_data.payload_size;
plain_text_offset = decrypted_data_length;
decrypted_data = (uint8_t*)malloc(decrypted_data_length);
if(!decrypted_data)
{
SAFE_FREE(req_message);
SAFE_FREE(resp_message);
return MALLOC_ERROR;
}
memset(&l_tag, 0, 16);
memset(decrypted_data, 0, decrypted_data_length);
//Decrypt the response message payload
status = sgx_rijndael128GCM_decrypt(&session_info->active.AEK, resp_message->message_aes_gcm_data.payload,
decrypted_data_length, decrypted_data,
reinterpret_cast<uint8_t *>(&(resp_message->message_aes_gcm_data.reserved)),
sizeof(resp_message->message_aes_gcm_data.reserved), &(resp_message->message_aes_gcm_data.payload[plain_text_offset]), plaintext_length,
&resp_message->message_aes_gcm_data.payload_tag);
if(SGX_SUCCESS != status)
{
SAFE_FREE(req_message);
SAFE_FREE(decrypted_data);
SAFE_FREE(resp_message);
return status;
}
// Verify if the nonce obtained in the response is equal to the session nonce + 1 (Prevents replay attacks)
if(*(resp_message->message_aes_gcm_data.reserved) != (session_info->active.counter + 1 ))
{
SAFE_FREE(req_message);
SAFE_FREE(resp_message);
SAFE_FREE(decrypted_data);
return INVALID_PARAMETER_ERROR;
}
//Update the value of the session nonce in the source enclave
session_info->active.counter = session_info->active.counter + 1;
memcpy(out_buff_len, &decrypted_data_length, sizeof(decrypted_data_length));
memcpy(*out_buff, decrypted_data, decrypted_data_length);
SAFE_FREE(decrypted_data);
SAFE_FREE(req_message);
SAFE_FREE(resp_message);
return SUCCESS;
}
//Process the request from the Source enclave and send the response message back to the Source enclave
ATTESTATION_STATUS generate_response(sgx_enclave_id_t src_enclave_id,
secure_message_t* req_message,
size_t req_message_size,
size_t max_payload_size,
secure_message_t* resp_message,
size_t resp_message_size)
{
const uint8_t* plaintext;
uint32_t plaintext_length;
uint8_t *decrypted_data;
uint32_t decrypted_data_length;
uint32_t plain_text_offset;
ms_in_msg_exchange_t * ms;
size_t resp_data_length;
size_t resp_message_calc_size;
char* resp_data;
uint8_t l_tag[TAG_SIZE];
size_t header_size, expected_payload_size;
dh_session_t *session_info;
secure_message_t* temp_resp_message;
uint32_t ret;
sgx_status_t status;
plaintext = (const uint8_t*)(" ");
plaintext_length = 0;
if(!req_message || !resp_message)
{
return INVALID_PARAMETER_ERROR;
}
//Get the session information from the map corresponding to the source enclave id
std::map<sgx_enclave_id_t, dh_session_t>::iterator it = g_dest_session_info_map.find(src_enclave_id);
if(it != g_dest_session_info_map.end())
{
session_info = &it->second;
}
else
{
return INVALID_SESSION;
}
if(session_info->status != ACTIVE)
{
return INVALID_SESSION;
}
//Set the decrypted data length to the payload size obtained from the message
decrypted_data_length = req_message->message_aes_gcm_data.payload_size;
header_size = sizeof(secure_message_t);
expected_payload_size = req_message_size - header_size;
//Verify the size of the payload
if(expected_payload_size != decrypted_data_length)
return INVALID_PARAMETER_ERROR;
memset(&l_tag, 0, 16);
plain_text_offset = decrypted_data_length;
decrypted_data = (uint8_t*)malloc(decrypted_data_length);
if(!decrypted_data)
{
return MALLOC_ERROR;
}
memset(decrypted_data, 0, decrypted_data_length);
//Decrypt the request message payload from source enclave
status = sgx_rijndael128GCM_decrypt(&session_info->active.AEK, req_message->message_aes_gcm_data.payload,
decrypted_data_length, decrypted_data,
reinterpret_cast<uint8_t *>(&(req_message->message_aes_gcm_data.reserved)),
sizeof(req_message->message_aes_gcm_data.reserved), &(req_message->message_aes_gcm_data.payload[plain_text_offset]), plaintext_length,
&req_message->message_aes_gcm_data.payload_tag);
if(SGX_SUCCESS != status)
{
SAFE_FREE(decrypted_data);
return status;
}
//Casting the decrypted data to the marshaling structure type to obtain type of request (generic message exchange/enclave to enclave call)
ms = (ms_in_msg_exchange_t *)decrypted_data;
// Verify if the nonce obtained in the request is equal to the session nonce
if((uint32_t)*(req_message->message_aes_gcm_data.reserved) != session_info->active.counter || *(req_message->message_aes_gcm_data.reserved) > ((2^32)-2))
{
SAFE_FREE(decrypted_data);
return INVALID_PARAMETER_ERROR;
}
if(ms->msg_type == MESSAGE_EXCHANGE)
{
//Call the generic secret response generator for message exchange
ret = message_exchange_response_generator((char*)decrypted_data, &resp_data, &resp_data_length);
if(ret !=0)
{
SAFE_FREE(decrypted_data);
SAFE_FREE(resp_data);
return INVALID_SESSION;
}
}
else if(ms->msg_type == ENCLAVE_TO_ENCLAVE_CALL)
{
//Call the destination enclave's dispatcher to call the appropriate function in the destination enclave
ret = enclave_to_enclave_call_dispatcher((char*)decrypted_data, decrypted_data_length, &resp_data, &resp_data_length);
if(ret !=0)
{
SAFE_FREE(decrypted_data);
SAFE_FREE(resp_data);
return INVALID_SESSION;
}
}
else
{
SAFE_FREE(decrypted_data);
return INVALID_REQUEST_TYPE_ERROR;
}
if(resp_data_length > max_payload_size)
{
SAFE_FREE(resp_data);
SAFE_FREE(decrypted_data);
return OUT_BUFFER_LENGTH_ERROR;
}
resp_message_calc_size = sizeof(secure_message_t)+ resp_data_length;
if(resp_message_calc_size > resp_message_size)
{
SAFE_FREE(resp_data);
SAFE_FREE(decrypted_data);
return OUT_BUFFER_LENGTH_ERROR;
}
//Code to build the response back to the Source Enclave
temp_resp_message = (secure_message_t*)malloc(resp_message_calc_size);
if(!temp_resp_message)
{
SAFE_FREE(resp_data);
SAFE_FREE(decrypted_data);
return MALLOC_ERROR;
}
memset(temp_resp_message,0,sizeof(secure_message_t)+ resp_data_length);
const uint32_t data2encrypt_length = (uint32_t)resp_data_length;
temp_resp_message->session_id = session_info->session_id;
temp_resp_message->message_aes_gcm_data.payload_size = data2encrypt_length;
//Increment the Session Nonce (Replay Protection)
session_info->active.counter = session_info->active.counter + 1;
//Set the response nonce as the session nonce
memcpy(&temp_resp_message->message_aes_gcm_data.reserved,&session_info->active.counter,sizeof(session_info->active.counter));
//Prepare the response message with the encrypted payload
status = sgx_rijndael128GCM_encrypt(&session_info->active.AEK, (uint8_t*)resp_data, data2encrypt_length,
reinterpret_cast<uint8_t *>(&(temp_resp_message->message_aes_gcm_data.payload)),
reinterpret_cast<uint8_t *>(&(temp_resp_message->message_aes_gcm_data.reserved)),
sizeof(temp_resp_message->message_aes_gcm_data.reserved), plaintext, plaintext_length,
&(temp_resp_message->message_aes_gcm_data.payload_tag));
if(SGX_SUCCESS != status)
{
SAFE_FREE(resp_data);
SAFE_FREE(decrypted_data);
SAFE_FREE(temp_resp_message);
return status;
}
memset(resp_message, 0, sizeof(secure_message_t)+ resp_data_length);
memcpy(resp_message, temp_resp_message, sizeof(secure_message_t)+ resp_data_length);
SAFE_FREE(decrypted_data);
SAFE_FREE(resp_data);
SAFE_FREE(temp_resp_message);
return SUCCESS;
}
//Close a current session
ATTESTATION_STATUS close_session(sgx_enclave_id_t src_enclave_id,
sgx_enclave_id_t dest_enclave_id)
{
sgx_status_t status;
uint32_t retstatus;
//Ocall to ask the destination enclave to end the session
status = end_session_ocall(&retstatus, src_enclave_id, dest_enclave_id);
if (status == SGX_SUCCESS)
{
if ((ATTESTATION_STATUS)retstatus != SUCCESS)
return ((ATTESTATION_STATUS)retstatus);
}
else
{
return ATTESTATION_SE_ERROR;
}
return SUCCESS;
}
//Respond to the request from the Source Enclave to close the session
ATTESTATION_STATUS end_session(sgx_enclave_id_t src_enclave_id)
{
ATTESTATION_STATUS status = SUCCESS;
int i;
dh_session_t session_info;
uint32_t session_id;
//Get the session information from the map corresponding to the source enclave id
std::map<sgx_enclave_id_t, dh_session_t>::iterator it = g_dest_session_info_map.find(src_enclave_id);
if(it != g_dest_session_info_map.end())
{
session_info = it->second;
}
else
{
return INVALID_SESSION;
}
session_id = session_info.session_id;
//Erase the session information for the current session
g_dest_session_info_map.erase(src_enclave_id);
//Update the session id tracker
if (g_session_count > 0)
{
//check if session exists
for (i=1; i <= MAX_SESSION_COUNT; i++)
{
if(g_session_id_tracker[i-1] != NULL && g_session_id_tracker[i-1]->session_id == session_id)
{
memset(g_session_id_tracker[i-1], 0, sizeof(session_id_tracker_t));
SAFE_FREE(g_session_id_tracker[i-1]);
g_session_count--;
break;
}
}
}
return status;
}
//Returns a new sessionID for the source destination session
ATTESTATION_STATUS generate_session_id(uint32_t *session_id)
{
ATTESTATION_STATUS status = SUCCESS;
if(!session_id)
{
return INVALID_PARAMETER_ERROR;
}
//if the session structure is untintialized, set that as the next session ID
for (int i = 0; i < MAX_SESSION_COUNT; i++)
{
if (g_session_id_tracker[i] == NULL)
{
*session_id = i;
return status;
}
}
status = NO_AVAILABLE_SESSION_ERROR;
return status;
}