add public wolfSSL_dtls_export and api tests

This commit is contained in:
Jacob Barthelmeh
2016-05-14 12:49:09 -06:00
parent 3897f78073
commit c8576566cc
5 changed files with 198 additions and 61 deletions

View File

@@ -1001,16 +1001,16 @@ static int dtls_export_load(byte* exp, word32 len, byte ver, WOLFSSL* ssl)
* buf is used to hold the serialized WOLFSSL struct and sz is the size of buf * buf is used to hold the serialized WOLFSSL struct and sz is the size of buf
* passed in. * passed in.
* On success returns the size of serialized session.*/ * On success returns the size of serialized session.*/
int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl) int wolfSSL_dtls_export_internal(byte* buf, word32 sz, WOLFSSL* ssl)
{ {
int ret; int ret;
word32 idx = 0; word32 idx = 0;
word32 totalLen = 0; word32 totalLen = 0;
WOLFSSL_ENTER("wolfSSL_dtls_export"); WOLFSSL_ENTER("wolfSSL_dtls_export_internal");
if (buf == NULL || ssl == NULL) { if (buf == NULL || ssl == NULL) {
WOLFSSL_LEAVE("wolfSSL_dtls_export", BAD_FUNC_ARG); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", BAD_FUNC_ARG);
return BAD_FUNC_ARG; return BAD_FUNC_ARG;
} }
@@ -1022,7 +1022,7 @@ int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl)
totalLen += DTLS_EXPORT_LEN + ssl->buffers.dtlsCtx.peer.sz; totalLen += DTLS_EXPORT_LEN + ssl->buffers.dtlsCtx.peer.sz;
if (totalLen > sz) { if (totalLen > sz) {
WOLFSSL_LEAVE("wolfSSL_dtls_export", BUFFER_E); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", BUFFER_E);
return BUFFER_E; return BUFFER_E;
} }
@@ -1035,16 +1035,16 @@ int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl)
c16toa((word16)DTLS_EXPORT_OPT_SZ, buf + idx); idx += DTLS_EXPORT_LEN; c16toa((word16)DTLS_EXPORT_OPT_SZ, buf + idx); idx += DTLS_EXPORT_LEN;
if ((ret = dtls_export_new(buf + idx, sz - idx, DTLS_EXPORT_VERSION, if ((ret = dtls_export_new(buf + idx, sz - idx, DTLS_EXPORT_VERSION,
ssl)) < 0) { ssl)) < 0) {
WOLFSSL_LEAVE("wolfSSL_dtls_export", ret); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", ret);
return ret; return ret;
} }
idx += DTLS_EXPORT_OPT_SZ; idx += ret;
/* export keys struct and dtls state -- variable length stored in ret */ /* export keys struct and dtls state -- variable length stored in ret */
idx += DTLS_EXPORT_LEN; /* leave room for length */ idx += DTLS_EXPORT_LEN; /* leave room for length */
if ((ret = ExportKeyState(buf + idx, sz - idx, if ((ret = ExportKeyState(buf + idx, sz - idx,
DTLS_EXPORT_VERSION, ssl)) < 0) { DTLS_EXPORT_VERSION, ssl)) < 0) {
WOLFSSL_LEAVE("wolfSSL_dtls_export", ret); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", ret);
return ret; return ret;
} }
c16toa((word16)ret, buf + idx - DTLS_EXPORT_LEN); idx += ret; c16toa((word16)ret, buf + idx - DTLS_EXPORT_LEN); idx += ret;
@@ -1053,10 +1053,10 @@ int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl)
c16toa((word16)DTLS_EXPORT_SPC_SZ, buf + idx); idx += DTLS_EXPORT_LEN; c16toa((word16)DTLS_EXPORT_SPC_SZ, buf + idx); idx += DTLS_EXPORT_LEN;
if ((ret = ExportCipherSpecState(buf + idx, sz - idx, if ((ret = ExportCipherSpecState(buf + idx, sz - idx,
DTLS_EXPORT_VERSION, ssl)) < 0) { DTLS_EXPORT_VERSION, ssl)) < 0) {
WOLFSSL_LEAVE("wolfSSL_dtls_export", ret); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", ret);
return ret; return ret;
} }
idx += DTLS_EXPORT_SPC_SZ; idx += ret;
/* export of dtls peer information */ /* export of dtls peer information */
c16toa((word16)ssl->buffers.dtlsCtx.peer.sz, buf + idx); c16toa((word16)ssl->buffers.dtlsCtx.peer.sz, buf + idx);
@@ -1079,7 +1079,7 @@ int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl)
} }
#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */ #endif /* WOLFSSL_SESSION_EXPORT_DEBUG */
WOLFSSL_LEAVE("wolfSSL_dtls_export", idx); WOLFSSL_LEAVE("wolfSSL_dtls_export_internal", idx);
return idx; return idx;
} }
@@ -1090,7 +1090,7 @@ int wolfSSL_dtls_import_internal(byte* buf, word32 sz, WOLFSSL* ssl)
word32 idx = 0; word32 idx = 0;
word16 length = 0; word16 length = 0;
int version; int version;
int ret, i; int ret;
WOLFSSL_ENTER("wolfSSL_dtls_import_internal"); WOLFSSL_ENTER("wolfSSL_dtls_import_internal");
/* check at least enough room for protocol and length */ /* check at least enough room for protocol and length */
@@ -1185,15 +1185,11 @@ int wolfSSL_dtls_import_internal(byte* buf, word32 sz, WOLFSSL* ssl)
} }
/* peer sa is free'd in SSL_ResourceFree */ /* peer sa is free'd in SSL_ResourceFree */
ssl->buffers.dtlsCtx.peer.sa = XMALLOC(ssl->buffers.dtlsCtx.peer.sz, if ((ret = wolfSSL_dtls_set_peer(ssl, buf + idx,
ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); ssl->buffers.dtlsCtx.peer.sz)) != SSL_SUCCESS) {
if (ssl->buffers.dtlsCtx.peer.sa == NULL) {
WOLFSSL_MSG("Import DTLS peer info error"); WOLFSSL_MSG("Import DTLS peer info error");
return MEMORY_E; return ret;
} }
XMEMCPY(ssl->buffers.dtlsCtx.peer.sa, buf + idx,
ssl->buffers.dtlsCtx.peer.sz);
idx += ssl->buffers.dtlsCtx.peer.sz; idx += ssl->buffers.dtlsCtx.peer.sz;
SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE); SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE);
@@ -1205,17 +1201,9 @@ int wolfSSL_dtls_import_internal(byte* buf, word32 sz, WOLFSSL* ssl)
} }
/* make sure is a valid suite used */ /* make sure is a valid suite used */
ret = MATCH_SUITE_ERROR; if (wolfSSL_get_cipher(ssl) == NULL) {
for (i = 0; i < ssl->suites->suiteSz; i += 2) {
if (ssl->suites->suites[i] == ssl->options.cipherSuite0 &&
ssl->suites->suites[i+1] == ssl->options.cipherSuite) {
ret = 0;
break;
}
}
if (ret != 0) {
WOLFSSL_MSG("Can not match cipher suite imported"); WOLFSSL_MSG("Can not match cipher suite imported");
return ret; return MATCH_SUITE_ERROR;
} }
/* do not allow stream ciphers with DTLS */ /* do not allow stream ciphers with DTLS */

View File

@@ -200,6 +200,38 @@ int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func)
} }
int wolfSSL_dtls_export(unsigned char* buf, unsigned int* sz, WOLFSSL* ssl)
{
int ret;
WOLFSSL_ENTER("wolfSSL_dtls_export");
if (ssl == NULL || sz == NULL) {
return BAD_FUNC_ARG;
}
if (buf == NULL) {
*sz = MAX_EXPORT_BUFFER;
return 0;
}
/* if not DTLS do nothing */
if (!ssl->options.dtls) {
WOLFSSL_MSG("Currently only DTLS export is supported");
return 0;
}
/* copy over keys, options, and dtls state struct */
ret = wolfSSL_dtls_export_internal(buf, *sz, ssl);
if (ret < 0) {
return ret;
}
return ret;
}
/* returns 0 on success */
int wolfSSL_send_session(WOLFSSL* ssl) int wolfSSL_send_session(WOLFSSL* ssl)
{ {
int ret; int ret;
@@ -221,31 +253,25 @@ int wolfSSL_send_session(WOLFSSL* ssl)
if (!ssl->options.dtls) { if (!ssl->options.dtls) {
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Currently only DTLS export is supported"); WOLFSSL_MSG("Currently only DTLS export is supported");
return SSL_SUCCESS; return 0;
} }
if (ssl->dtls_export) { /* copy over keys, options, and dtls state struct */
/* copy over keys, options, and dtls state struct */ ret = wolfSSL_dtls_export_internal(buf, bufSz, ssl);
ret = wolfSSL_dtls_export(buf, bufSz, ssl); if (ret < 0) {
if (ret < 0) {
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
/* if no error ret has size of buffer */
ret = ssl->dtls_export(ssl, buf, ret, NULL);
if (ret != SSL_SUCCESS) {
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return SSL_SUCCESS; return ret;
} }
else {
/* if no error ret has size of buffer */
ret = ssl->dtls_export(ssl, buf, ret, NULL);
if (ret != SSL_SUCCESS) {
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return SSL_FAILURE; return ret;
} }
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return 0;
} }
#endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_DTLS */
#endif /* WOLFSSL_SESSION_EXPORT */ #endif /* WOLFSSL_SESSION_EXPORT */
@@ -6641,7 +6667,6 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
#ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS
else { else {
ssl->options.dtlsHsRetain = 1; ssl->options.dtlsHsRetain = 1;
} }
#endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_DTLS */
@@ -6931,8 +6956,13 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
#endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_DTLS */
#ifdef WOLFSSL_SESSION_EXPORT #ifdef WOLFSSL_SESSION_EXPORT
WOLFSSL_MSG("sending session"); if (ssl->dtls_export) {
wolfSSL_send_session(ssl); if ((ssl->error = wolfSSL_send_session(ssl)) != 0) {
WOLFSSL_MSG("Export DTLS session error");
WOLFSSL_ERROR(ssl->error);
return SSL_FATAL_ERROR;
}
}
#endif #endif
WOLFSSL_LEAVE("SSL_accept()", SSL_SUCCESS); WOLFSSL_LEAVE("SSL_accept()", SSL_SUCCESS);

View File

@@ -493,6 +493,34 @@ static void test_wolfSSL_SetTmpDH_buffer(void)
/* helper functions */ /* helper functions */
#ifdef HAVE_IO_TESTS_DEPENDENCIES #ifdef HAVE_IO_TESTS_DEPENDENCIES
#ifdef WOLFSSL_SESSION_EXPORT
/* set up function for sending session information */
static int test_export(WOLFSSL* inSsl, byte* buf, word32 sz, void* userCtx)
{
WOLFSSL_CTX* ctx;
WOLFSSL* ssl;
AssertNotNull(inSsl);
AssertNotNull(buf);
AssertIntNE(0, sz);
/* Set ctx to DTLS 1.2 */
ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
AssertNotNull(ctx);
ssl = wolfSSL_new(ctx);
AssertNotNull(ssl);
AssertIntGE(wolfSSL_dtls_import(ssl, buf, sz), 0);
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
(void)userCtx;
return SSL_SUCCESS;
}
#endif
static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args) static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args)
{ {
SOCKET_T sockfd = 0; SOCKET_T sockfd = 0;
@@ -711,8 +739,8 @@ done2:
return; return;
} }
/* SNI / ALPN helper functions */ /* SNI / ALPN / session export helper functions */
#if defined(HAVE_SNI) || defined(HAVE_ALPN) #if defined(HAVE_SNI) || defined(HAVE_ALPN) || defined(WOLFSSL_SESSION_EXPORT)
static THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args) static THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args)
{ {
@@ -752,6 +780,9 @@ static THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args)
#ifdef OPENSSL_EXTRA #ifdef OPENSSL_EXTRA
wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack);
#endif #endif
#ifdef WOLFSSL_SESSION_EXPORT
AssertIntEQ(SSL_SUCCESS, wolfSSL_CTX_dtls_set_export(ctx, test_export));
#endif
AssertIntEQ(SSL_SUCCESS, wolfSSL_CTX_load_verify_locations(ctx, cliCert, 0)); AssertIntEQ(SSL_SUCCESS, wolfSSL_CTX_load_verify_locations(ctx, cliCert, 0));
@@ -766,9 +797,21 @@ static THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args)
callbacks->ctx_ready(ctx); callbacks->ctx_ready(ctx);
ssl = wolfSSL_new(ctx); ssl = wolfSSL_new(ctx);
if (wolfSSL_dtls(ssl)) {
SOCKADDR_IN_T cliAddr;
socklen_t cliLen;
tcp_accept(&sfd, &cfd, (func_args*)args, port, 0, 0, 0, 1); cliLen = sizeof(cliAddr);
CloseSocket(sfd); tcp_accept(&sfd, &cfd, (func_args*)args, port, 0, 1, 0, 0);
idx = (int)recvfrom(sfd, input, sizeof(input), MSG_PEEK,
(struct sockaddr*)&cliAddr, &cliLen);
AssertIntGT(idx, 0);
wolfSSL_dtls_set_peer(ssl, &cliAddr, cliLen);
}
else {
tcp_accept(&sfd, &cfd, (func_args*)args, port, 0, 0, 0, 1);
CloseSocket(sfd);
}
AssertIntEQ(SSL_SUCCESS, wolfSSL_set_fd(ssl, cfd)); AssertIntEQ(SSL_SUCCESS, wolfSSL_set_fd(ssl, cfd));
@@ -796,6 +839,20 @@ static THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args)
} }
AssertIntEQ(len, wolfSSL_write(ssl, msg, len)); AssertIntEQ(len, wolfSSL_write(ssl, msg, len));
#ifdef WOLFSSL_SESSION_EXPORT
if (wolfSSL_dtls(ssl)) {
byte* import;
word32 sz;
wolfSSL_dtls_export(NULL, &sz, ssl);
import = (byte*)XMALLOC(sz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
AssertNotNull(import);
idx = wolfSSL_dtls_export(import, &sz, ssl);
AssertIntGE(idx, 0);
AssertIntGE(wolfSSL_dtls_import(ssl, import, idx), 0);
XFREE(import, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif
#ifdef WOLFSSL_TIRTOS #ifdef WOLFSSL_TIRTOS
Task_yield(); Task_yield();
#endif #endif
@@ -861,7 +918,12 @@ static void run_wolfssl_client(void* args)
callbacks->ctx_ready(ctx); callbacks->ctx_ready(ctx);
ssl = wolfSSL_new(ctx); ssl = wolfSSL_new(ctx);
tcp_connect(&sfd, wolfSSLIP, ((func_args*)args)->signal->port, 0, ssl); if (wolfSSL_dtls(ssl)) {
tcp_connect(&sfd, wolfSSLIP, ((func_args*)args)->signal->port, 1, ssl);
}
else {
tcp_connect(&sfd, wolfSSLIP, ((func_args*)args)->signal->port, 0, ssl);
}
AssertIntEQ(SSL_SUCCESS, wolfSSL_set_fd(ssl, sfd)); AssertIntEQ(SSL_SUCCESS, wolfSSL_set_fd(ssl, sfd));
if (callbacks->ssl_ready) if (callbacks->ssl_ready)
@@ -894,7 +956,8 @@ static void run_wolfssl_client(void* args)
#endif #endif
} }
#endif /* defined(HAVE_SNI) || defined(HAVE_ALPN) */ #endif /* defined(HAVE_SNI) || defined(HAVE_ALPN) ||
defined(WOLFSSL_SESSION_EXPORT) */
#endif /* io tests dependencies */ #endif /* io tests dependencies */
@@ -952,6 +1015,52 @@ static void test_wolfSSL_read_write(void)
#endif #endif
} }
static void test_wolfSSL_dtls_export(void)
{
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
defined(WOLFSSL_SESSION_EXPORT)
tcp_ready ready;
func_args client_args;
func_args server_args;
THREAD_TYPE serverThread;
callback_functions server_cbf;
callback_functions client_cbf;
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
InitTcpReady(&ready);
/* set using dtls */
XMEMSET(&server_cbf, 0, sizeof(callback_functions));
XMEMSET(&client_cbf, 0, sizeof(callback_functions));
server_cbf.method = wolfDTLSv1_2_server_method;
client_cbf.method = wolfDTLSv1_2_client_method;
server_args.callbacks = &server_cbf;
client_args.callbacks = &client_cbf;
server_args.signal = &ready;
client_args.signal = &ready;
start_thread(run_wolfssl_server, &server_args, &serverThread);
wait_tcp_ready(&server_args);
run_wolfssl_client(&client_args);
join_thread(serverThread);
AssertTrue(client_args.return_code);
AssertTrue(server_args.return_code);
FreeTcpReady(&ready);
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
printf(testingFmt, "wolfSSL_dtls_export()");
printf(resultFmt, passed);
#endif
}
/*----------------------------------------------------------------------------* /*----------------------------------------------------------------------------*
| TLS extensions tests | TLS extensions tests
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
@@ -1738,6 +1847,7 @@ void ApiTest(void)
test_wolfSSL_SetTmpDH_file(); test_wolfSSL_SetTmpDH_file();
test_wolfSSL_SetTmpDH_buffer(); test_wolfSSL_SetTmpDH_buffer();
test_wolfSSL_read_write(); test_wolfSSL_read_write();
test_wolfSSL_dtls_export();
/* TLS extensions tests */ /* TLS extensions tests */
test_wolfSSL_UseSNI(); test_wolfSSL_UseSNI();

View File

@@ -1203,8 +1203,6 @@ typedef struct ProtocolVersion {
} WOLFSSL_PACK ProtocolVersion; } WOLFSSL_PACK ProtocolVersion;
WOLFSSL_LOCAL int wolfSSL_dtls_import_internal(byte* buf, word32 sz, WOLFSSL* ssl);
WOLFSSL_LOCAL int wolfSSL_dtls_export(byte* buf, word32 sz, WOLFSSL* ssl);
WOLFSSL_LOCAL ProtocolVersion MakeSSLv3(void); WOLFSSL_LOCAL ProtocolVersion MakeSSLv3(void);
WOLFSSL_LOCAL ProtocolVersion MakeTLSv1(void); WOLFSSL_LOCAL ProtocolVersion MakeTLSv1(void);
WOLFSSL_LOCAL ProtocolVersion MakeTLSv1_1(void); WOLFSSL_LOCAL ProtocolVersion MakeTLSv1_1(void);
@@ -1213,6 +1211,14 @@ WOLFSSL_LOCAL ProtocolVersion MakeTLSv1_2(void);
#ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS
WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1(void); WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1(void);
WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1_2(void); WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1_2(void);
#ifdef WOLFSSL_SESSION_EXPORT
WOLFSSL_LOCAL int wolfSSL_dtls_import_internal(byte* buf, word32 sz,
WOLFSSL* ssl);
WOLFSSL_LOCAL int wolfSSL_dtls_export_internal(byte* buf, word32 sz,
WOLFSSL* ssl);
WOLFSSL_LOCAL int wolfSSL_send_session(WOLFSSL* ssl);
#endif
#endif #endif

View File

@@ -229,10 +229,13 @@ WOLFSSL_API WOLFSSL_METHOD *wolfTLSv1_2_client_method(void);
#ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS
typedef int (*wc_dtls_export)(WOLFSSL* ssl, typedef int (*wc_dtls_export)(WOLFSSL* ssl,
unsigned char* exportBuffer, unsigned int sz, void* userCtx); unsigned char* exportBuffer, unsigned int sz, void* userCtx);
WOLFSSL_API int wolfSSL_dtls_import(WOLFSSL* ssl, unsigned char* buf, unsigned int sz); WOLFSSL_API int wolfSSL_dtls_import(WOLFSSL* ssl, unsigned char* buf,
WOLFSSL_API int wolfSSL_CTX_dtls_set_export(WOLFSSL_CTX* ctx, wc_dtls_export func); unsigned int sz);
WOLFSSL_API int wolfSSL_CTX_dtls_set_export(WOLFSSL_CTX* ctx,
wc_dtls_export func);
WOLFSSL_API int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func); WOLFSSL_API int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func);
WOLFSSL_LOCAL int wolfSSL_send_session(WOLFSSL* ssl); WOLFSSL_API int wolfSSL_dtls_export(unsigned char* buf, unsigned int* sz,
WOLFSSL* ssl);
#endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_DTLS */
#endif /* WOLFSSL_SESSION_EXPORT */ #endif /* WOLFSSL_SESSION_EXPORT */