500 lines
18 KiB
C++
500 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ssl.h"
|
|
#include "ssl3prot.h"
|
|
#include "sslerr.h"
|
|
#include "sslproto.h"
|
|
#include "sslexp.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "tls_connect.h"
|
|
|
|
namespace nss_test {
|
|
|
|
static void IncrementCounterArg(void *arg) {
|
|
if (arg) {
|
|
auto *called = reinterpret_cast<size_t *>(arg);
|
|
++*called;
|
|
}
|
|
}
|
|
|
|
static PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
|
|
PRUint8 *data, unsigned int *len,
|
|
unsigned int maxLen, void *arg) {
|
|
IncrementCounterArg(arg);
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
|
|
PRUint8 *data, unsigned int *len,
|
|
unsigned int maxLen, void *arg) {
|
|
IncrementCounterArg(arg);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
static SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
|
|
const PRUint8 *data, unsigned int len,
|
|
SSLAlertDescription *alert, void *arg) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
// All of the (current) set of supported extensions, plus a few extra.
|
|
static const uint16_t kManyExtensions[] = {
|
|
ssl_server_name_xtn,
|
|
ssl_cert_status_xtn,
|
|
ssl_supported_groups_xtn,
|
|
ssl_ec_point_formats_xtn,
|
|
ssl_signature_algorithms_xtn,
|
|
ssl_signature_algorithms_cert_xtn,
|
|
ssl_use_srtp_xtn,
|
|
ssl_app_layer_protocol_xtn,
|
|
ssl_signed_cert_timestamp_xtn,
|
|
ssl_padding_xtn,
|
|
ssl_extended_master_secret_xtn,
|
|
ssl_certificate_compression_xtn,
|
|
ssl_session_ticket_xtn,
|
|
ssl_tls13_key_share_xtn,
|
|
ssl_tls13_pre_shared_key_xtn,
|
|
ssl_tls13_early_data_xtn,
|
|
ssl_tls13_supported_versions_xtn,
|
|
ssl_tls13_cookie_xtn,
|
|
ssl_tls13_psk_key_exchange_modes_xtn,
|
|
ssl_tls13_ticket_early_data_info_xtn,
|
|
ssl_tls13_certificate_authorities_xtn,
|
|
ssl_next_proto_nego_xtn,
|
|
ssl_renegotiation_info_xtn,
|
|
ssl_record_size_limit_xtn,
|
|
ssl_tls13_encrypted_client_hello_xtn,
|
|
1,
|
|
0xffff};
|
|
// The list here includes all extensions we expect to use (SSL_MAX_EXTENSIONS),
|
|
// plus the deprecated values (see sslt.h), and two extra dummy values.
|
|
PR_STATIC_ASSERT((SSL_MAX_EXTENSIONS + 5) == PR_ARRAY_SIZE(kManyExtensions));
|
|
|
|
void InstallManyWriters(std::shared_ptr<TlsAgent> agent,
|
|
SSLExtensionWriter writer, size_t *installed = nullptr,
|
|
size_t *called = nullptr) {
|
|
for (size_t i = 0; i < PR_ARRAY_SIZE(kManyExtensions); ++i) {
|
|
SSLExtensionSupport support = ssl_ext_none;
|
|
SECStatus rv = SSL_GetExtensionSupport(kManyExtensions[i], &support);
|
|
ASSERT_EQ(SECSuccess, rv) << "SSL_GetExtensionSupport cannot fail";
|
|
|
|
rv = SSL_InstallExtensionHooks(agent->ssl_fd(), kManyExtensions[i], writer,
|
|
called, NoopExtensionHandler, nullptr);
|
|
if (support == ssl_ext_native_only) {
|
|
EXPECT_EQ(SECFailure, rv);
|
|
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
|
} else {
|
|
if (installed) {
|
|
++*installed;
|
|
}
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopClient) {
|
|
EnsureTlsSetup();
|
|
size_t installed = 0;
|
|
size_t called = 0;
|
|
InstallManyWriters(client_, NoopExtensionWriter, &installed, &called);
|
|
EXPECT_LT(0U, installed);
|
|
Connect();
|
|
EXPECT_EQ(installed, called);
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionAllNoopServer) {
|
|
EnsureTlsSetup();
|
|
size_t installed = 0;
|
|
size_t called = 0;
|
|
InstallManyWriters(server_, NoopExtensionWriter, &installed, &called);
|
|
EXPECT_LT(0U, installed);
|
|
Connect();
|
|
// Extension writers are all called for each of ServerHello,
|
|
// EncryptedExtensions, and Certificate.
|
|
EXPECT_EQ(installed * 3, called);
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterClient) {
|
|
EnsureTlsSetup();
|
|
InstallManyWriters(client_, EmptyExtensionWriter);
|
|
InstallManyWriters(server_, EmptyExtensionWriter);
|
|
Connect();
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionEmptyWriterServer) {
|
|
EnsureTlsSetup();
|
|
InstallManyWriters(server_, EmptyExtensionWriter);
|
|
// Sending extensions that the client doesn't expect leads to extensions
|
|
// appearing even if the client didn't send one, or in the wrong messages.
|
|
client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
|
|
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
|
|
ConnectExpectFail();
|
|
}
|
|
|
|
// Install an writer to disable sending of a natively-supported extension.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionWriterDisable) {
|
|
EnsureTlsSetup();
|
|
|
|
// This option enables sending the extension via the native support.
|
|
SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
|
|
SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// This installs an override that doesn't do anything. You have to specify
|
|
// something; passing all nullptr values removes an existing handler.
|
|
rv = SSL_InstallExtensionHooks(
|
|
client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NoopExtensionWriter,
|
|
nullptr, NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(
|
|
client_, ssl_signed_cert_timestamp_xtn);
|
|
|
|
Connect();
|
|
// So nothing will be sent.
|
|
EXPECT_FALSE(capture->captured());
|
|
}
|
|
|
|
// An extension that is unlikely to be parsed as valid.
|
|
static uint8_t kNonsenseExtension[] = {91, 82, 73, 64, 55, 46, 37, 28, 19};
|
|
|
|
static PRBool NonsenseExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
|
|
PRUint8 *data, unsigned int *len,
|
|
unsigned int maxLen, void *arg) {
|
|
TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
|
|
EXPECT_NE(nullptr, agent);
|
|
EXPECT_NE(nullptr, data);
|
|
EXPECT_NE(nullptr, len);
|
|
EXPECT_EQ(0U, *len);
|
|
EXPECT_LT(0U, maxLen);
|
|
EXPECT_EQ(agent->ssl_fd(), fd);
|
|
|
|
if (message != ssl_hs_client_hello && message != ssl_hs_server_hello &&
|
|
message != ssl_hs_encrypted_extensions) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
*len = static_cast<unsigned int>(sizeof(kNonsenseExtension));
|
|
EXPECT_GE(maxLen, *len);
|
|
if (maxLen < *len) {
|
|
return PR_FALSE;
|
|
}
|
|
PORT_Memcpy(data, kNonsenseExtension, *len);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Override the extension handler for an natively-supported and produce
|
|
// nonsense, which results in a handshake failure.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionOverride) {
|
|
EnsureTlsSetup();
|
|
|
|
// This option enables sending the extension via the native support.
|
|
SECStatus rv = SSL_OptionSet(client_->ssl_fd(),
|
|
SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, PR_TRUE);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// This installs an override that sends nonsense.
|
|
rv = SSL_InstallExtensionHooks(
|
|
client_->ssl_fd(), ssl_signed_cert_timestamp_xtn, NonsenseExtensionWriter,
|
|
client_.get(), NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Capture it to see what we got.
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(
|
|
client_, ssl_signed_cert_timestamp_xtn);
|
|
|
|
ConnectExpectAlert(server_, kTlsAlertDecodeError);
|
|
|
|
EXPECT_TRUE(capture->captured());
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
capture->extension());
|
|
}
|
|
|
|
static SECStatus NonsenseExtensionHandler(PRFileDesc *fd,
|
|
SSLHandshakeType message,
|
|
const PRUint8 *data, unsigned int len,
|
|
SSLAlertDescription *alert,
|
|
void *arg) {
|
|
TlsAgent *agent = reinterpret_cast<TlsAgent *>(arg);
|
|
EXPECT_EQ(agent->ssl_fd(), fd);
|
|
if (agent->role() == TlsAgent::SERVER) {
|
|
EXPECT_EQ(ssl_hs_client_hello, message);
|
|
} else {
|
|
EXPECT_TRUE(message == ssl_hs_server_hello ||
|
|
message == ssl_hs_encrypted_extensions);
|
|
}
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
DataBuffer(data, len));
|
|
EXPECT_NE(nullptr, alert);
|
|
return SECSuccess;
|
|
}
|
|
|
|
// Send nonsense in an extension from client to server.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionClientToServer) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nonsense.
|
|
const uint16_t extension_code = 0xffe5;
|
|
SECStatus rv = SSL_InstallExtensionHooks(
|
|
client_->ssl_fd(), extension_code, NonsenseExtensionWriter, client_.get(),
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Capture it to see what we got.
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(client_, extension_code);
|
|
|
|
// Handle it so that the handshake completes.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
NoopExtensionWriter, nullptr,
|
|
NonsenseExtensionHandler, server_.get());
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
Connect();
|
|
|
|
EXPECT_TRUE(capture->captured());
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
capture->extension());
|
|
}
|
|
|
|
static PRBool NonsenseExtensionWriterSH(PRFileDesc *fd,
|
|
SSLHandshakeType message, PRUint8 *data,
|
|
unsigned int *len, unsigned int maxLen,
|
|
void *arg) {
|
|
if (message == ssl_hs_server_hello) {
|
|
return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Send nonsense in an extension from server to client, in ServerHello.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientSH) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
const uint16_t extension_code = 0xff5e;
|
|
SECStatus rv = SSL_InstallExtensionHooks(
|
|
client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
|
|
NonsenseExtensionHandler, client_.get());
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Have the server send nonsense.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
NonsenseExtensionWriterSH, server_.get(),
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Capture the extension from the ServerHello only and check it.
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code);
|
|
capture->SetHandshakeTypes({kTlsHandshakeServerHello});
|
|
|
|
Connect();
|
|
|
|
EXPECT_TRUE(capture->captured());
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
capture->extension());
|
|
}
|
|
|
|
static PRBool NonsenseExtensionWriterEE(PRFileDesc *fd,
|
|
SSLHandshakeType message, PRUint8 *data,
|
|
unsigned int *len, unsigned int maxLen,
|
|
void *arg) {
|
|
if (message == ssl_hs_encrypted_extensions) {
|
|
return NonsenseExtensionWriter(fd, message, data, len, maxLen, arg);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Send nonsense in an extension from server to client, in EncryptedExtensions.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionServerToClientEE) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
const uint16_t extension_code = 0xff5e;
|
|
SECStatus rv = SSL_InstallExtensionHooks(
|
|
client_->ssl_fd(), extension_code, EmptyExtensionWriter, nullptr,
|
|
NonsenseExtensionHandler, client_.get());
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Have the server send nonsense.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
NonsenseExtensionWriterEE, server_.get(),
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Capture the extension from the EncryptedExtensions only and check it.
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code);
|
|
capture->SetHandshakeTypes({kTlsHandshakeEncryptedExtensions});
|
|
capture->EnableDecryption();
|
|
|
|
Connect();
|
|
|
|
EXPECT_TRUE(capture->captured());
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
capture->extension());
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionUnsolicitedServer) {
|
|
EnsureTlsSetup();
|
|
|
|
const uint16_t extension_code = 0xff5e;
|
|
SECStatus rv = SSL_InstallExtensionHooks(
|
|
server_->ssl_fd(), extension_code, NonsenseExtensionWriter, server_.get(),
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Capture it to see what we got.
|
|
auto capture = MakeTlsFilter<TlsExtensionCapture>(server_, extension_code);
|
|
|
|
client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
|
|
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
|
|
ConnectExpectFail();
|
|
|
|
EXPECT_TRUE(capture->captured());
|
|
EXPECT_EQ(DataBuffer(kNonsenseExtension, sizeof(kNonsenseExtension)),
|
|
capture->extension());
|
|
}
|
|
|
|
SECStatus RejectExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
|
|
const PRUint8 *data, unsigned int len,
|
|
SSLAlertDescription *alert, void *arg) {
|
|
return SECFailure;
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionServerReject) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nonsense.
|
|
const uint16_t extension_code = 0xffe7;
|
|
SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Reject the extension for no good reason.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
NoopExtensionWriter, nullptr,
|
|
RejectExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
|
|
}
|
|
|
|
// Send nonsense in an extension from client to server.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionClientReject) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
const uint16_t extension_code = 0xff58;
|
|
SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
RejectExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Have the server send nonsense.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
|
|
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
|
|
ConnectExpectFail();
|
|
}
|
|
|
|
static const uint8_t kCustomAlert = 0xf6;
|
|
|
|
SECStatus AlertExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
|
|
const PRUint8 *data, unsigned int len,
|
|
SSLAlertDescription *alert, void *arg) {
|
|
*alert = kCustomAlert;
|
|
return SECFailure;
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionServerRejectAlert) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nonsense.
|
|
const uint16_t extension_code = 0xffea;
|
|
SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Reject the extension for no good reason.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
NoopExtensionWriter, nullptr,
|
|
AlertExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
ConnectExpectAlert(server_, kCustomAlert);
|
|
}
|
|
|
|
// Send nonsense in an extension from client to server.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionClientRejectAlert) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
const uint16_t extension_code = 0xff5a;
|
|
SECStatus rv = SSL_InstallExtensionHooks(client_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
AlertExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
// Have the server send nonsense.
|
|
rv = SSL_InstallExtensionHooks(server_->ssl_fd(), extension_code,
|
|
EmptyExtensionWriter, nullptr,
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
|
|
client_->ExpectSendAlert(kCustomAlert);
|
|
server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
|
|
ConnectExpectFail();
|
|
}
|
|
|
|
// Configure a custom extension hook badly.
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyWriter) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
SECStatus rv =
|
|
SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6c, EmptyExtensionWriter,
|
|
nullptr, nullptr, nullptr);
|
|
EXPECT_EQ(SECFailure, rv);
|
|
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionOnlyHandler) {
|
|
EnsureTlsSetup();
|
|
|
|
// This installs an override that sends nothing but expects nonsense.
|
|
SECStatus rv =
|
|
SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff6d, nullptr, nullptr,
|
|
NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECFailure, rv);
|
|
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
|
}
|
|
|
|
TEST_F(TlsConnectStreamTls13, CustomExtensionOverrunBuffer) {
|
|
EnsureTlsSetup();
|
|
// This doesn't actually overrun the buffer, but it says that it does.
|
|
auto overrun_writer = [](PRFileDesc *fd, SSLHandshakeType message,
|
|
PRUint8 *data, unsigned int *len,
|
|
unsigned int maxLen, void *arg) -> PRBool {
|
|
*len = maxLen + 1;
|
|
return PR_TRUE;
|
|
};
|
|
SECStatus rv =
|
|
SSL_InstallExtensionHooks(client_->ssl_fd(), 0xff71, overrun_writer,
|
|
nullptr, NoopExtensionHandler, nullptr);
|
|
EXPECT_EQ(SECSuccess, rv);
|
|
client_->StartConnect();
|
|
client_->Handshake();
|
|
client_->CheckErrorCode(SEC_ERROR_APPLICATION_CALLBACK_ERROR);
|
|
}
|
|
|
|
} // namespace nss_test
|