pjproject/pjlib/src/pj/ssl_sock_apple.m

2217 lines
74 KiB
Objective-C

/*
* Copyright (C) 2019-2020 Teluu Inc. (http://www.teluu.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/ssl_sock.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/rand.h>
/* Only build when PJ_HAS_SSL_SOCK and the implementation is Apple SSL. */
#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
(PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_APPLE)
#define THIS_FILE "ssl_sock_apple.m"
/* Set to 1 to enable debugging messages. */
#define SSL_DEBUG 0
#define SSL_SOCK_IMP_USE_CIRC_BUF
#define SSL_SOCK_IMP_USE_OWN_NETWORK
#include "ssl_sock_imp_common.h"
#include "ssl_sock_imp_common.c"
#include "TargetConditionals.h"
#include <err.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CommonCrypto/CommonDigest.h>
#include <Foundation/NSLock.h>
#include <Network/Network.h>
#include <Security/Security.h>
/* IMPORTANT note from Apple's Concurrency Programming Guide doc:
* "Because Grand Central Dispatch manages the relationship between the tasks
* you provide and the threads on which those tasks run, you should generally
* avoid calling POSIX thread routines from your task code"
*
* Since network events happen in a dispatch function block, we need to make
* sure not to call any PJLIB functions there (not even pj_pool_alloc() nor
* pj_log()). Instead, we will post those events to a singleton event manager
* to be polled by ioqueue polling thread(s).
*/
/* Secure socket structure definition. */
typedef struct applessl_sock_t {
pj_ssl_sock_t base;
nw_listener_t listener;
nw_listener_state_t lis_state;
nw_connection_t connection;
nw_connection_state_t con_state;
dispatch_queue_t queue;
dispatch_semaphore_t ev_semaphore;
SecTrustRef trust;
tls_ciphersuite_t cipher;
sec_identity_t identity;
} applessl_sock_t;
/*
*******************************************************************
* Event manager
*******************************************************************
*/
typedef enum event_id
{
EVENT_ACCEPT,
EVENT_CONNECT,
EVENT_VERIFY_CERT,
EVENT_HANDSHAKE_COMPLETE,
EVENT_DATA_READ,
EVENT_DATA_SENT,
EVENT_DISCARD
} event_id;
typedef struct event_t
{
PJ_DECL_LIST_MEMBER(struct event_t);
event_id type;
pj_ssl_sock_t *ssock;
pj_bool_t async;
union
{
struct
{
nw_connection_t newconn;
pj_sockaddr src_addr;
int src_addr_len;
pj_status_t status;
} accept_ev;
struct
{
pj_status_t status;
} connect_ev;
struct
{
pj_status_t status;
} handshake_ev;
struct
{
pj_ioqueue_op_key_t *send_key;
pj_ssize_t sent;
} data_sent_ev;
struct
{
void *data;
pj_size_t size;
pj_status_t status;
pj_size_t remainder;
} data_read_ev;
} body;
} event_t;
typedef struct event_manager
{
NSLock *lock;
event_t event_list;
event_t free_event_list;
} event_manager;
static event_manager *event_mgr = NULL;
#if SSL_DEBUG
static pj_thread_desc queue_th_desc;
static pj_thread_t *queue_th;
#endif
/*
*******************************************************************
* Event manager's functions
*******************************************************************
*/
static pj_status_t verify_cert(applessl_sock_t *assock, pj_ssl_cert_t *cert);
static void event_manager_destroy()
{
event_manager *mgr = event_mgr;
event_mgr = NULL;
while (!pj_list_empty(&mgr->free_event_list)) {
event_t *event = mgr->free_event_list.next;
pj_list_erase(event);
free(event);
}
while (!pj_list_empty(&mgr->event_list)) {
event_t *event = mgr->event_list.next;
pj_list_erase(event);
free(event);
}
[mgr->lock release];
free(mgr);
}
static pj_status_t event_manager_create()
{
event_manager *mgr;
if (event_mgr)
return PJ_SUCCESS;
mgr = malloc(sizeof(event_manager));
if (!mgr) return PJ_ENOMEM;
mgr->lock = [[NSLock alloc]init];
pj_list_init(&mgr->event_list);
pj_list_init(&mgr->free_event_list);
event_mgr = mgr;
pj_atexit(&event_manager_destroy);
return PJ_SUCCESS;
}
/* Post event to the event manager. If the event is posted
* synchronously, the function will wait until the event is processed.
*/
static pj_status_t event_manager_post_event(pj_ssl_sock_t *ssock,
event_t *event_item,
pj_bool_t async)
{
event_manager *mgr = event_mgr;
event_t *event;
#if SSL_DEBUG
if (!pj_thread_is_registered()) {
pj_bzero(queue_th_desc, sizeof(pj_thread_desc));
pj_thread_register("sslq", queue_th_desc, &queue_th);
}
PJ_LOG(3, (THIS_FILE, "Posting event %p %d", ssock, event_item->type));
#endif
if (ssock->is_closing || !ssock->pool || !mgr)
return PJ_EGONE;
#if SSL_DEBUG
PJ_LOG(3,(THIS_FILE, "Post event success %p %d",ssock, event_item->type));
#endif
[mgr->lock lock];
if (pj_list_empty(&mgr->free_event_list)) {
event = malloc(sizeof(event_t));
} else {
event = mgr->free_event_list.next;
pj_list_erase(event);
}
pj_memcpy(event, event_item, sizeof(event_t));
event->ssock = ssock;
event->async = async;
pj_list_push_back(&mgr->event_list, event);
[mgr->lock unlock];
if (!async) {
dispatch_semaphore_wait(((applessl_sock_t *)ssock)->ev_semaphore,
DISPATCH_TIME_FOREVER);
}
return PJ_SUCCESS;
}
/* Remove all events associated with the socket. */
static void event_manager_remove_events(pj_ssl_sock_t *ssock)
{
event_t *event;
[event_mgr->lock lock];
event = event_mgr->event_list.next;
while (event != &event_mgr->event_list) {
event_t *event_ = event;
event = event->next;
if (event_->ssock == ssock) {
pj_list_erase(event_);
/* If not async, signal the waiting socket */
if (!event_->async) {
applessl_sock_t * assock;
assock = (applessl_sock_t *)event_->ssock;
dispatch_semaphore_signal(assock->ev_semaphore);
}
}
}
[event_mgr->lock unlock];
}
pj_status_t ssl_network_event_poll()
{
if (!event_mgr)
return PJ_SUCCESS;
while (!pj_list_empty(&event_mgr->event_list)) {
pj_ssl_sock_t *ssock;
applessl_sock_t * assock;
event_t *event;
pj_bool_t ret = PJ_TRUE, add_ref = PJ_FALSE;
[event_mgr->lock lock];
/* Check again, this time by holding the lock */
if (pj_list_empty(&event_mgr->event_list)) {
[event_mgr->lock unlock];
break;
}
event = event_mgr->event_list.next;
ssock = event->ssock;
assock = (applessl_sock_t *)ssock;
pj_list_erase(event);
if (ssock->is_closing || !ssock->pool ||
(!ssock->is_server && !assock->connection) ||
(ssock->is_server && !assock->listener))
{
PJ_LOG(3, (THIS_FILE, "Warning: Discarding SSL event type %d of "
"a closing socket %p", event->type, ssock));
event->type = EVENT_DISCARD;
} else if (ssock->param.grp_lock) {
if (pj_grp_lock_get_ref(ssock->param.grp_lock) > 0) {
/* Prevent ssock from being destroyed while
* we are calling the callback.
*/
add_ref = PJ_TRUE;
pj_grp_lock_add_ref(ssock->param.grp_lock);
} else {
PJ_LOG(3, (THIS_FILE, "Warning: Discarding SSL event type %d "
" of a destroyed socket %p", event->type, ssock));
event->type = EVENT_DISCARD;
}
}
[event_mgr->lock unlock];
switch (event->type) {
case EVENT_ACCEPT:
ret = ssock_on_accept_complete(event->ssock,
PJ_INVALID_SOCKET,
event->body.accept_ev.newconn,
&event->body.accept_ev.src_addr,
event->body.accept_ev.src_addr_len,
event->body.accept_ev.status);
break;
case EVENT_CONNECT:
ret = ssock_on_connect_complete(event->ssock,
event->body.connect_ev.status);
break;
case EVENT_VERIFY_CERT:
verify_cert(assock, event->ssock->cert);
break;
case EVENT_HANDSHAKE_COMPLETE:
event->ssock->ssl_state = SSL_STATE_ESTABLISHED;
ret = on_handshake_complete(event->ssock,
event->body.handshake_ev.status);
break;
case EVENT_DATA_SENT:
ret = ssock_on_data_sent(event->ssock,
event->body.data_sent_ev.send_key,
event->body.data_sent_ev.sent);
break;
case EVENT_DATA_READ:
ret = ssock_on_data_read(event->ssock,
event->body.data_read_ev.data,
event->body.data_read_ev.size,
event->body.data_read_ev.status,
&event->body.data_read_ev.remainder);
break;
default:
break;
}
/* If not async and not destroyed, signal the waiting socket */
if (event->type != EVENT_DISCARD && ret && !event->async && ret) {
dispatch_semaphore_signal(assock->ev_semaphore);
}
/* Put the event into the free list to be reused */
[event_mgr->lock lock];
if (add_ref) {
pj_grp_lock_dec_ref(ssock->param.grp_lock);
}
pj_list_push_back(&event_mgr->free_event_list, event);
[event_mgr->lock unlock];
}
return 0;
}
/*
*******************************************************************
* Static/internal functions.
*******************************************************************
*/
#define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + \
PJ_ERRNO_SPACE_SIZE*6)
#define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE
/* Convert from Apple SSL error to pj_status_t. */
static pj_status_t pj_status_from_err(applessl_sock_t *assock,
const char *msg,
OSStatus err)
{
pj_status_t status = (pj_status_t)-err;
CFStringRef errmsg;
errmsg = SecCopyErrorMessageString(err, NULL);
PJ_LOG(3, (THIS_FILE, "Apple SSL error %s [%d]: %s",
(msg? msg: ""), err,
CFStringGetCStringPtr(errmsg, kCFStringEncodingUTF8)));
CFRelease(errmsg);
if (status > PJ_SSL_ERRNO_SPACE_SIZE)
status = PJ_SSL_ERRNO_SPACE_SIZE;
status += PJ_SSL_ERRNO_START;
if (assock)
assock->base.last_err = err;
return status;
}
/* Read cert or key file */
static pj_status_t create_data_from_file(CFDataRef *data,
pj_str_t *fname, pj_str_t *path)
{
CFURLRef file;
CFReadStreamRef read_stream;
UInt8 data_buf[8192];
CFIndex nbytes = 0;
if (path) {
CFURLRef filepath;
CFStringRef path_str;
path_str = CFStringCreateWithBytes(NULL, (const UInt8 *)path->ptr,
path->slen,
kCFStringEncodingUTF8, false);
if (!path_str) return PJ_ENOMEM;
filepath = CFURLCreateWithFileSystemPath(NULL, path_str,
kCFURLPOSIXPathStyle, true);
CFRelease(path_str);
if (!filepath) return PJ_ENOMEM;
path_str = CFStringCreateWithBytes(NULL, (const UInt8 *)fname->ptr,
fname->slen,
kCFStringEncodingUTF8, false);
if (!path_str) {
CFRelease(filepath);
return PJ_ENOMEM;
}
file = CFURLCreateCopyAppendingPathComponent(NULL, filepath,
path_str, false);
CFRelease(path_str);
CFRelease(filepath);
} else {
file = CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)fname->ptr, fname->slen, false);
}
if (!file)
return PJ_ENOMEM;
read_stream = CFReadStreamCreateWithFile(NULL, file);
CFRelease(file);
if (!read_stream)
return PJ_ENOTFOUND;
if (!CFReadStreamOpen(read_stream)) {
PJ_LOG(2, (THIS_FILE, "Failed opening file"));
CFRelease(read_stream);
return PJ_EINVAL;
}
nbytes = CFReadStreamRead(read_stream, data_buf,
sizeof(data_buf));
if (nbytes > 0)
*data = CFDataCreate(NULL, data_buf, nbytes);
else
*data = NULL;
CFReadStreamClose(read_stream);
CFRelease(read_stream);
return (*data? PJ_SUCCESS: PJ_EINVAL);
}
static pj_status_t create_identity_from_cert(applessl_sock_t *assock,
pj_ssl_cert_t *cert,
sec_identity_t *p_identity)
{
CFStringRef password = NULL;
CFDataRef cert_data = NULL;
void *keys[1] = {NULL};
void *values[1] = {NULL};
CFDictionaryRef options;
CFArrayRef items;
CFIndex i, count;
SecIdentityRef identity = NULL;
OSStatus err;
pj_status_t status;
/* Init */
*p_identity = NULL;
if (cert->privkey_file.slen || cert->privkey_buf.slen ||
cert->privkey_pass.slen)
{
PJ_LOG(5, (THIS_FILE, "Ignoring supplied private key. Private key "
"must be placed in the keychain instead."));
}
if (cert->cert_file.slen) {
status = create_data_from_file(&cert_data, &cert->cert_file, NULL);
if (status != PJ_SUCCESS) {
PJ_PERROR(2, (THIS_FILE, status, "Failed reading cert file"));
return status;
}
} else if (cert->cert_buf.slen) {
cert_data = CFDataCreate(NULL, (const UInt8 *)cert->cert_buf.ptr,
cert->cert_buf.slen);
if (!cert_data)
return PJ_ENOMEM;
}
if (cert_data) {
if (cert->privkey_pass.slen) {
password = CFStringCreateWithBytes(NULL,
(const UInt8 *)cert->privkey_pass.ptr,
cert->privkey_pass.slen,
kCFStringEncodingUTF8,
false);
keys[0] = (void *)kSecImportExportPassphrase;
values[0] = (void *)password;
}
options = CFDictionaryCreate(NULL, (const void **)keys,
(const void **)values,
(password? 1: 0), NULL, NULL);
if (!options)
return PJ_ENOMEM;
#if TARGET_OS_IPHONE
err = SecPKCS12Import(cert_data, options, &items);
#else
{
SecExternalFormat ext_format[3] = {kSecFormatPKCS12,
kSecFormatPEMSequence,
kSecFormatX509Cert/* DER */};
SecExternalItemType ext_type = kSecItemTypeCertificate;
SecItemImportExportKeyParameters key_params;
pj_bzero(&key_params, sizeof(key_params));
key_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
key_params.passphrase = password;
for (i = 0; i < (CFIndex)PJ_ARRAY_SIZE(ext_format); i++) {
items = NULL;
err = SecItemImport(cert_data, NULL, &ext_format[i],
&ext_type, 0, &key_params, NULL, &items);
if (err == noErr && items) {
break;
}
}
}
#endif
CFRelease(options);
if (password)
CFRelease(password);
CFRelease(cert_data);
if (err != noErr || !items) {
return pj_status_from_err(assock, "SecItemImport", err);
}
count = CFArrayGetCount(items);
for (i = 0; i < count; i++) {
CFTypeRef item;
CFTypeID item_id;
item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
item_id = CFGetTypeID(item);
if (item_id == CFDictionaryGetTypeID()) {
identity = (SecIdentityRef)
CFDictionaryGetValue((CFDictionaryRef) item,
kSecImportItemIdentity);
break;
}
#if !TARGET_OS_IPHONE
else if (item_id == SecCertificateGetTypeID()) {
err = SecIdentityCreateWithCertificate(NULL,
(SecCertificateRef) item, &identity);
if (err != noErr) {
pj_status_from_err(assock, "SecIdentityCreate", err);
if (err == errSecItemNotFound) {
PJ_LOG(2, (THIS_FILE, "Private key must be placed in "
"the keychain"));
}
} else {
break;
}
}
#endif
}
CFRelease(items);
if (!identity) {
PJ_LOG(2, (THIS_FILE, "Failed extracting identity from "
"the cert file"));
return PJ_EINVAL;
}
*p_identity = sec_identity_create(identity);
CFRelease(identity);
}
return PJ_SUCCESS;
}
static pj_status_t verify_cert(applessl_sock_t *assock, pj_ssl_cert_t *cert)
{
CFDataRef ca_data = NULL;
SecTrustRef trust = assock->trust;
bool result;
CFErrorRef error;
pj_status_t status = PJ_SUCCESS;
OSStatus err = noErr;
if (trust && cert && cert->CA_file.slen) {
status = create_data_from_file(&ca_data, &cert->CA_file,
(cert->CA_path.slen? &cert->CA_path:
NULL));
if (status != PJ_SUCCESS)
PJ_LOG(2, (THIS_FILE, "Failed reading CA file"));
} else if (trust && cert && cert->CA_buf.slen) {
ca_data = CFDataCreate(NULL, (const UInt8 *)cert->CA_buf.ptr,
cert->CA_buf.slen);
if (!ca_data)
PJ_LOG(2, (THIS_FILE, "Not enough memory for CA buffer"));
}
if (ca_data) {
SecCertificateRef ca_cert;
CFMutableArrayRef ca_array;
ca_cert = SecCertificateCreateWithData(NULL, ca_data);
CFRelease(ca_data);
if (!ca_cert) {
PJ_LOG(2, (THIS_FILE, "Failed creating certificate from "
"CA file/buffer. It has to be "
"in DER format."));
status = PJ_EINVAL;
goto on_return;
}
ca_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!ca_array) {
PJ_LOG(2, (THIS_FILE, "Not enough memory for CA array"));
CFRelease(ca_cert);
status = PJ_ENOMEM;
goto on_return;
}
CFArrayAppendValue(ca_array, ca_cert);
CFRelease(ca_cert);
err = SecTrustSetAnchorCertificates(trust, ca_array);
CFRelease(ca_array);
if (err != noErr)
pj_status_from_err(assock, "SetAnchorCerts", err);
err = SecTrustSetAnchorCertificatesOnly(trust, true);
if (err != noErr)
pj_status_from_err(assock, "SetAnchorCertsOnly", err);
}
result = SecTrustEvaluateWithError(trust, &error);
if (!result) {
pj_ssl_sock_t *ssock = &assock->base;
SecTrustResultType trust_result;
err = SecTrustGetTrustResult(trust, &trust_result);
if (err == noErr) {
#if SSL_DEBUG
PJ_LOG(3, (THIS_FILE, "SSL trust evaluation: %d", trust_result));
#endif
switch (trust_result) {
case kSecTrustResultInvalid:
ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
break;
case kSecTrustResultDeny:
case kSecTrustResultFatalTrustFailure:
ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
break;
case kSecTrustResultRecoverableTrustFailure:
/* Doc: "If you receive this result, you can retry
* after changing settings. For example, if trust is
* denied because the certificate has expired, ..."
* But this error can also mean another (recoverable)
* failure, though.
*/
ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
break;
case kSecTrustResultOtherError:
ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
break;
default:
break;
}
}
if (error)
CFRelease(error);
/* Evaluation failed */
status = PJ_EEOF;
}
on_return:
if (status != PJ_SUCCESS && assock->base.verify_status == 0)
assock->base.verify_status |= PJ_SSL_CERT_EUNKNOWN;
return status;
}
/*
*******************************************************************
* Network functions.
*******************************************************************
*/
/* Send data. */
static pj_status_t network_send(pj_ssl_sock_t *ssock,
pj_ioqueue_op_key_t *send_key,
const void *data,
pj_ssize_t *size,
unsigned flags)
{
PJ_UNUSED_ARG(flags);
applessl_sock_t *assock = (applessl_sock_t *)ssock;
dispatch_data_t content;
if (!assock->connection)
return PJ_EGONE;
content = dispatch_data_create(data, *size, assock->queue,
DISPATCH_DATA_DESTRUCTOR_DEFAULT);
if (!content)
return PJ_ENOMEM;
nw_connection_send(assock->connection, content,
NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true,
^(nw_error_t error)
{
event_t event;
if (error != NULL) {
errno = nw_error_get_error_code(error);
if (errno == 89) {
/* Error 89 is network cancelled, not a send error. */
return;
} else {
warn("Send error");
}
}
event.type = EVENT_DATA_SENT;
event.body.data_sent_ev.send_key = send_key;
if (error != NULL) {
event.body.data_sent_ev.sent = (errno > 0)? -errno: errno;
} else {
event.body.data_sent_ev.sent = dispatch_data_get_size(content);
}
event_manager_post_event(ssock, &event, PJ_TRUE);
});
dispatch_release(content);
return PJ_EPENDING;
}
static pj_status_t network_start_read(pj_ssl_sock_t *ssock,
unsigned async_count,
unsigned buff_size,
void *readbuf[],
pj_uint32_t flags)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
unsigned i;
if (!assock->connection)
return PJ_EGONE;
for (i = 0; i < async_count; i++) {
nw_connection_receive(assock->connection, 1, buff_size,
^(dispatch_data_t content, nw_content_context_t context,
bool is_complete, nw_error_t error)
{
pj_status_t status = PJ_SUCCESS;
/* If the context is marked as complete, and is the final context,
* we're read-closed.
*/
if (is_complete &&
(context == NULL || nw_content_context_get_is_final(context)))
{
return;
}
if (error != NULL) {
errno = nw_error_get_error_code(error);
if (errno == 89) {
/* Since error 89 is network intentionally cancelled by
* us, we immediately return.
*/
return;
} else {
warn("Read error, stopping further receives");
status = PJ_EEOF;
}
}
dispatch_block_t schedule_next_receive =
^{
/* If there was no error in receiving, request more data. */
if (!error && !is_complete && assock->connection) {
network_start_read(ssock, async_count, buff_size,
readbuf, flags);
}
};
if (content) {
dispatch_data_apply(content,
^(dispatch_data_t region, size_t offset,
const void *buffer, size_t inSize)
{
PJ_UNUSED_ARG(region);
PJ_UNUSED_ARG(offset);
/* This block can be invoked multiple times,
* each for every contiguous memory region in the content.
*/
event_t event;
memcpy(ssock->asock_rbuf[i], buffer, inSize);
event.type = EVENT_DATA_READ;
event.body.data_read_ev.data = ssock->asock_rbuf[i];
event.body.data_read_ev.size = inSize;
event.body.data_read_ev.status = status;
event.body.data_read_ev.remainder = 0;
event_manager_post_event(ssock, &event, PJ_FALSE);
return (bool)true;
});
schedule_next_receive();
} else {
if (status != PJ_SUCCESS) {
event_t event;
/* Report read error to application */
event.type = EVENT_DATA_READ;
event.body.data_read_ev.data = NULL;
event.body.data_read_ev.size = 0;
event.body.data_read_ev.status = status;
event.body.data_read_ev.remainder = 0;
event_manager_post_event(ssock, &event, PJ_TRUE);
}
schedule_next_receive();
}
});
}
return PJ_SUCCESS;
}
/* Get address of local endpoint */
static pj_status_t network_get_localaddr(pj_ssl_sock_t *ssock,
pj_sockaddr_t *addr,
int *namelen)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
nw_path_t path;
nw_endpoint_t endpoint;
const struct sockaddr *address;
path = nw_connection_copy_current_path(assock->connection);
if (!path)
return PJ_EINVALIDOP;
endpoint = nw_path_copy_effective_local_endpoint(path);
nw_release(path);
if (!endpoint)
return PJ_EINVALIDOP;
address = nw_endpoint_get_address(endpoint);
if (address) {
pj_sockaddr_cp(addr, address);
*namelen = pj_sockaddr_get_addr_len(addr);
}
nw_release(endpoint);
return PJ_SUCCESS;
}
static pj_status_t network_create_params(pj_ssl_sock_t * ssock,
const pj_sockaddr_t *localaddr,
pj_uint16_t port_range,
nw_parameters_t *p_params)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
char ip_addr[PJ_INET6_ADDRSTRLEN];
unsigned port;
char port_str[PJ_INET6_ADDRSTRLEN];
nw_endpoint_t local_endpoint;
nw_parameters_t parameters;
nw_parameters_configure_protocol_block_t configure_tls;
nw_protocol_stack_t protocol_stack;
nw_protocol_options_t ip_options;
tls_protocol_version_t min_proto = tls_protocol_version_TLSv10;
tls_protocol_version_t max_proto = tls_protocol_version_TLSv13;
/* Set min and max protocol version */
if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) {
ssock->param.proto = PJ_SSL_SOCK_PROTO_TLS1 |
PJ_SSL_SOCK_PROTO_TLS1_1 |
PJ_SSL_SOCK_PROTO_TLS1_2 |
PJ_SSL_SOCK_PROTO_TLS1_3;
}
if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) {
max_proto = tls_protocol_version_TLSv13;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) {
max_proto = tls_protocol_version_TLSv12;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) {
max_proto = tls_protocol_version_TLSv11;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) {
max_proto = tls_protocol_version_TLSv10;
} else {
PJ_LOG(3, (THIS_FILE, "Unsupported TLS protocol"));
return PJ_EINVAL;
}
if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) {
min_proto = tls_protocol_version_TLSv10;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) {
min_proto = tls_protocol_version_TLSv11;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) {
min_proto = tls_protocol_version_TLSv12;
} else if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) {
min_proto = tls_protocol_version_TLSv13;
}
/* Set certificate */
if (ssock->cert) {
pj_status_t status = create_identity_from_cert(assock, ssock->cert,
&assock->identity);
if (status != PJ_SUCCESS)
return status;
}
configure_tls = ^(nw_protocol_options_t tls_options)
{
sec_protocol_options_t sec_options;
sec_options = nw_tls_copy_sec_protocol_options(tls_options);
/* Set identity */
if (ssock->cert && assock->identity) {
sec_protocol_options_set_local_identity(sec_options,
assock->identity);
}
sec_protocol_options_set_min_tls_protocol_version(sec_options,
min_proto);
sec_protocol_options_set_max_tls_protocol_version(sec_options,
max_proto);
/* Set cipher list */
if (ssock->param.ciphers_num > 0) {
unsigned i;
for (i = 0; i < ssock->param.ciphers_num; i++) {
sec_protocol_options_append_tls_ciphersuite(sec_options,
(tls_ciphersuite_t)ssock->param.ciphers[i]);
}
}
if (!ssock->is_server && ssock->param.server_name.slen) {
sec_protocol_options_set_tls_server_name(sec_options,
ssock->param.server_name.ptr);
}
sec_protocol_options_set_tls_renegotiation_enabled(sec_options,
true);
/* This must be disabled, otherwise server may think this is
* a resumption of a previously closed connection, and our
* verify block may never be invoked!
*/
sec_protocol_options_set_tls_resumption_enabled(sec_options, false);
/* SSL verification options */
sec_protocol_options_set_peer_authentication_required(sec_options,
true);
/* Handshake flow:
* 1. Server's challenge block, provide server's trust
* 2. Client's verify block, to verify server's trust
* 3. Client's challenge block, provide client's trust
* 4. Only if client's trust is not NULL, server's verify block,
* to verify client's trust.
*/
sec_protocol_options_set_challenge_block(sec_options,
^(sec_protocol_metadata_t metadata,
sec_protocol_challenge_complete_t complete)
{
PJ_UNUSED_ARG(metadata);
complete(assock->identity);
}, assock->queue);
sec_protocol_options_set_verify_block(sec_options,
^(sec_protocol_metadata_t metadata, sec_trust_t trust_ref,
sec_protocol_verify_complete_t complete)
{
event_t event;
pj_status_t status;
bool result = true;
assock->trust = trust_ref? sec_trust_copy_ref(trust_ref): nil;
assock->cipher =
sec_protocol_metadata_get_negotiated_tls_ciphersuite(metadata);
/* For client, call on_connect_complete() callback first. */
if (!ssock->is_server && ssock->ssl_state == SSL_STATE_NULL) {
if (!assock->connection)
complete(false);
event.type = EVENT_CONNECT;
event.body.connect_ev.status = PJ_SUCCESS;
status = event_manager_post_event(ssock, &event, PJ_FALSE);
if (status == PJ_EGONE)
complete(false);
}
event.type = EVENT_VERIFY_CERT;
status = event_manager_post_event(ssock, &event, PJ_FALSE);
if (status == PJ_EGONE)
complete(false);
/* Check the result of cert verification. */
if (ssock->verify_status != PJ_SSL_CERT_ESUCCESS) {
if (ssock->param.verify_peer) {
/* Verification failed. */
result = false;
} else {
/* When verification is not requested just return ok here,
* however application can still get the verification status.
*/
result = true;
}
}
complete(result);
}, assock->queue);
nw_release(sec_options);
};
parameters = nw_parameters_create_secure_tcp(configure_tls,
NW_PARAMETERS_DEFAULT_CONFIGURATION);
protocol_stack = nw_parameters_copy_default_protocol_stack(parameters);
ip_options = nw_protocol_stack_copy_internet_protocol(protocol_stack);
if (ssock->param.sock_af == pj_AF_INET()) {
nw_ip_options_set_version(ip_options, nw_ip_version_4);
} else if (ssock->param.sock_af == pj_AF_INET6()) {
nw_ip_options_set_version(ip_options, nw_ip_version_6);
}
nw_release(ip_options);
nw_release(protocol_stack);
if (ssock->is_server && ssock->param.reuse_addr) {
nw_parameters_set_reuse_local_address(parameters, true);
}
/* Create local endpoint.
* Currently we ignore QoS and socket options.
*/
pj_sockaddr_print(localaddr, ip_addr,sizeof(ip_addr),0);
if (port_range) {
pj_uint16_t max_try = MAX_BIND_RETRY;
if (port_range && port_range < max_try) {
max_try = port_range;
}
for (; max_try; --max_try) {
pj_uint16_t base_port;
base_port = pj_sockaddr_get_port(localaddr);
port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1));
pj_utoa(port, port_str);
local_endpoint = nw_endpoint_create_host(ip_addr, port_str);
if (local_endpoint)
break;
}
} else {
port = pj_sockaddr_get_port(localaddr);
pj_utoa(port, port_str);
local_endpoint = nw_endpoint_create_host(ip_addr, port_str);
}
if (!local_endpoint) {
PJ_LOG(2, (THIS_FILE, "Failed creating local endpoint"));
return PJ_EINVALIDOP;
}
nw_parameters_set_local_endpoint(parameters, local_endpoint);
nw_release(local_endpoint);
*p_params = parameters;
return PJ_SUCCESS;
}
/* Setup assock's connection state callback and start the connection */
static pj_status_t network_setup_connection(pj_ssl_sock_t *ssock,
void *connection)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
assock->connection = (nw_connection_t)connection;
pj_status_t status;
/* Initialize input circular buffer */
status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 8192);
if (status != PJ_SUCCESS)
return status;
/* Initialize output circular buffer */
status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 8192);
if (status != PJ_SUCCESS)
return status;
nw_connection_set_queue(assock->connection, assock->queue);
assock->con_state = nw_connection_state_invalid;
nw_connection_set_state_changed_handler(assock->connection,
^(nw_connection_state_t state, nw_error_t error)
{
pj_status_t status = PJ_SUCCESS;
pj_bool_t call_cb = PJ_FALSE;
#if SSL_DEBUG
if (!pj_thread_is_registered()) {
pj_bzero(queue_th_desc, sizeof(pj_thread_desc));
pj_thread_register("sslq", queue_th_desc, &queue_th);
}
PJ_LOG(3, (THIS_FILE, "SSL state change %p %d", assock, state));
#endif
if (error && state != nw_connection_state_cancelled) {
errno = nw_error_get_error_code(error);
warn("Connection failed %p", assock);
status = PJ_STATUS_FROM_OS(errno);
#if SSL_DEBUG
PJ_LOG(3, (THIS_FILE, "SSL state and errno %d %d", state, errno));
#endif
call_cb = PJ_TRUE;
}
if (state == nw_connection_state_ready) {
if (ssock->is_server) {
nw_protocol_definition_t tls_def;
nw_protocol_metadata_t prot_meta;
sec_protocol_metadata_t meta;
tls_def = nw_protocol_copy_tls_definition();
prot_meta = nw_connection_copy_protocol_metadata(connection,
tls_def);
meta = nw_tls_copy_sec_protocol_metadata(prot_meta);
assock->cipher =
sec_protocol_metadata_get_negotiated_tls_ciphersuite(meta);
if (ssock->param.require_client_cert &&
!sec_protocol_metadata_access_peer_certificate_chain(
meta, ^(sec_certificate_t certificate) {} ))
{
status = PJ_EEOF;
}
nw_release(tls_def);
nw_release(prot_meta);
nw_release(meta);
}
call_cb = PJ_TRUE;
} else if (state == nw_connection_state_cancelled) {
/* We release the reference in ssl_destroy() */
// nw_release(assock->connection);
// assock->connection = nil;
}
if (call_cb) {
event_t event;
event.type = EVENT_HANDSHAKE_COMPLETE;
event.body.handshake_ev.status = status;
event_manager_post_event(ssock, &event, PJ_TRUE);
if (ssock->is_server && status == PJ_SUCCESS) {
status = network_start_read(ssock, ssock->param.async_cnt,
(unsigned)ssock->param.read_buffer_size,
ssock->asock_rbuf, 0);
}
}
assock->con_state = state;
});
nw_connection_start(assock->connection);
return PJ_SUCCESS;
}
static pj_status_t network_start_accept(pj_ssl_sock_t *ssock,
pj_pool_t *pool,
const pj_sockaddr_t *localaddr,
int addr_len,
const pj_ssl_sock_param *newsock_param)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
pj_status_t status;
nw_parameters_t parameters = NULL;
status = network_create_params(ssock, localaddr, 0, &parameters);
if (status != PJ_SUCCESS)
return status;
/* Create listener */
assock->listener = nw_listener_create(parameters);
nw_release(parameters);
if (!assock->listener) {
PJ_LOG(2, (THIS_FILE, "Failed creating listener"));
return PJ_EINVALIDOP;
}
nw_listener_set_queue(assock->listener, assock->queue);
/* Hold a reference until cancelled */
nw_retain(assock->listener);
assock->lis_state = nw_listener_state_invalid;
nw_listener_set_state_changed_handler(assock->listener,
^(nw_listener_state_t state, nw_error_t error)
{
errno = error ? nw_error_get_error_code(error) : 0;
if (state == nw_listener_state_failed) {
warn("listener failed\n");
pj_sockaddr_set_port(&ssock->local_addr, 0);
dispatch_semaphore_signal(assock->ev_semaphore);
} else if (state == nw_listener_state_ready) {
/* Update local port */
pj_sockaddr_set_port(&ssock->local_addr,
nw_listener_get_port(assock->listener));
dispatch_semaphore_signal(assock->ev_semaphore);
} else if (state == nw_listener_state_cancelled) {
/* We release the reference in ssl_destroy() */
// nw_release(assock->listener);
// assock->listener = nil;
}
assock->lis_state = state;
});
nw_listener_set_new_connection_handler(assock->listener,
^(nw_connection_t connection)
{
nw_endpoint_t endpoint = nw_connection_copy_endpoint(connection);
const struct sockaddr *address;
event_t event;
address = nw_endpoint_get_address(endpoint);
event.type = EVENT_ACCEPT;
event.body.accept_ev.newconn = connection;
pj_sockaddr_cp(&event.body.accept_ev.src_addr, address);
event.body.accept_ev.src_addr_len = pj_sockaddr_get_addr_len(address);
event.body.accept_ev.status = PJ_SUCCESS;
nw_retain(connection);
event_manager_post_event(ssock, &event, PJ_TRUE);
nw_release(endpoint);
});
/* Update local address */
ssock->addr_len = addr_len;
pj_sockaddr_cp(&ssock->local_addr, localaddr);
/* Start accepting */
pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
ssock->newsock_param.grp_lock = NULL;
/* Start listening to the address */
nw_listener_start(assock->listener);
/* Wait until it's ready */
dispatch_semaphore_wait(assock->ev_semaphore, DISPATCH_TIME_FOREVER);
if (pj_sockaddr_get_port(&ssock->local_addr) == 0) {
/* Failed. */
status = PJ_EEOF;
goto on_error;
}
return PJ_SUCCESS;
on_error:
ssl_reset_sock_state(ssock);
return status;
}
static pj_status_t network_start_connect(pj_ssl_sock_t *ssock,
pj_ssl_start_connect_param *connect_param)
{
char ip_addr[PJ_INET6_ADDRSTRLEN];
unsigned port;
char port_str[PJ_INET6_ADDRSTRLEN];
nw_endpoint_t endpoint;
nw_parameters_t parameters;
nw_connection_t connection;
pj_status_t status;
pj_pool_t *pool = connect_param->pool;
const pj_sockaddr_t *localaddr = connect_param->localaddr;
pj_uint16_t port_range = connect_param->local_port_range;
const pj_sockaddr_t *remaddr = connect_param->remaddr;
int addr_len = connect_param->addr_len;
PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
PJ_EINVAL);
status = network_create_params(ssock, localaddr, port_range,
&parameters);
if (status != PJ_SUCCESS)
return status;
/* Create remote endpoint */
pj_sockaddr_print(remaddr, ip_addr,sizeof(ip_addr),0);
port = pj_sockaddr_get_port(remaddr);
pj_utoa(port, port_str);
endpoint = nw_endpoint_create_host(ip_addr, port_str);
if (!endpoint) {
PJ_LOG(2, (THIS_FILE, "Failed creating remote endpoint"));
nw_release(parameters);
return PJ_EINVALIDOP;
}
connection = nw_connection_create(endpoint, parameters);
nw_release(endpoint);
nw_release(parameters);
if (!connection) {
PJ_LOG(2, (THIS_FILE, "Failed creating connection"));
return PJ_EINVALIDOP;
}
/* Hold a reference until cancelled */
nw_retain(connection);
status = network_setup_connection(ssock, connection);
if (status != PJ_SUCCESS)
return status;
/* Save remote address */
pj_sockaddr_cp(&ssock->rem_addr, remaddr);
/* Update local address */
ssock->addr_len = addr_len;
pj_sockaddr_cp(&ssock->local_addr, localaddr);
return PJ_EPENDING;
}
/*
*******************************************************************
* SSL functions.
*******************************************************************
*/
static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
{
applessl_sock_t *assock;
/* Create event manager */
if (event_manager_create() != PJ_SUCCESS)
return NULL;
assock = PJ_POOL_ZALLOC_T(pool, applessl_sock_t);
assock->queue = dispatch_queue_create("ssl_queue", DISPATCH_QUEUE_SERIAL);
assock->ev_semaphore = dispatch_semaphore_create(0);
if (!assock->queue || !assock->ev_semaphore) {
ssl_destroy(&assock->base);
return NULL;
}
return (pj_ssl_sock_t *)assock;
}
static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
{
/* Nothing to do here. SSL has been configured before connection
* is started.
*/
PJ_UNUSED_ARG(ssock);
return PJ_SUCCESS;
}
static void close_connection(applessl_sock_t *assock)
{
if (assock->connection) {
unsigned i;
nw_connection_t conn = assock->connection;
assock->connection = nil;
nw_connection_force_cancel(conn);
nw_release(conn);
/* We need to wait until the connection is at cancelled state,
* otherwise events will still be delivered even though we
* already force cancel and release the connection.
*/
for (i = 0; i < 40; i++) {
if (assock->con_state == nw_connection_state_cancelled) break;
pj_thread_sleep(50);
}
event_manager_remove_events(&assock->base);
if (assock->con_state != nw_connection_state_cancelled) {
PJ_LOG(3, (THIS_FILE, "Warning: Failed to cancel SSL connection "
"%p %d", assock, assock->con_state));
}
#if SSL_DEBUG
PJ_LOG(3, (THIS_FILE, "SSL connection %p closed", assock));
#endif
}
}
/* Close sockets */
static void ssl_close_sockets(pj_ssl_sock_t *ssock)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
if (assock->identity) {
nw_release(assock->identity);
assock->identity = nil;
}
if (assock->trust) {
nw_release(assock->trust);
assock->trust = nil;
}
/* This can happen when pj_ssl_sock_create() fails. */
if (!ssock->write_mutex)
return;
pj_lock_acquire(ssock->write_mutex);
close_connection(assock);
pj_lock_release(ssock->write_mutex);
}
/* Destroy Apple SSL. */
static void ssl_destroy(pj_ssl_sock_t *ssock)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
close_connection(assock);
if (assock->listener) {
unsigned i;
nw_listener_set_new_connection_handler(assock->listener, nil);
nw_listener_cancel(assock->listener);
for (i = 0; i < 20; i++) {
if (assock->lis_state == nw_listener_state_cancelled) break;
pj_thread_sleep(50);
}
if (assock->lis_state != nw_listener_state_cancelled) {
PJ_LOG(3, (THIS_FILE, "Warning: Failed to cancel SSL listener "
"%p %d", assock, assock->lis_state));
}
nw_release(assock->listener);
assock->listener = nil;
}
event_manager_remove_events(ssock);
/* Important: if we are called from a blocking dispatch block,
* we need to signal it before destroying ourselves.
*/
if (assock->ev_semaphore) {
dispatch_semaphore_signal(assock->ev_semaphore);
}
if (assock->queue) {
dispatch_release(assock->queue);
assock->queue = NULL;
}
if (assock->ev_semaphore) {
dispatch_release(assock->ev_semaphore);
assock->ev_semaphore = nil;
}
/* Destroy circular buffers */
circ_deinit(&ssock->circ_buf_input);
circ_deinit(&ssock->circ_buf_output);
PJ_LOG(4, (THIS_FILE, "SSL %p destroyed", ssock));
}
/* Reset socket state. */
static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
{
pj_lock_acquire(ssock->circ_buf_output_mutex);
ssock->ssl_state = SSL_STATE_NULL;
pj_lock_release(ssock->circ_buf_output_mutex);
#if SSL_DEBUG
PJ_LOG(3, (THIS_FILE, "SSL reset sock state %p", ssock));
#endif
ssl_close_sockets(ssock);
}
/* This function is taken from Apple's sslAppUtils.cpp (version 58286.41.2),
* with some modifications.
*/
const char *sslGetCipherSuiteString(SSLCipherSuite cs)
{
switch (cs) {
/* TLS addenda using AES-CBC, RFC 3268 */
case TLS_RSA_WITH_AES_128_CBC_SHA:
return "TLS_RSA_WITH_AES_128_CBC_SHA";
case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
return "TLS_DH_DSS_WITH_AES_128_CBC_SHA";
case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
return "TLS_DH_RSA_WITH_AES_128_CBC_SHA";
case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA";
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA";
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
return "TLS_DH_anon_WITH_AES_128_CBC_SHA";
case TLS_RSA_WITH_AES_256_CBC_SHA:
return "TLS_RSA_WITH_AES_256_CBC_SHA";
case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
return "TLS_DH_DSS_WITH_AES_256_CBC_SHA";
case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
return "TLS_DH_RSA_WITH_AES_256_CBC_SHA";
case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA";
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA";
case TLS_DH_anon_WITH_AES_256_CBC_SHA:
return "TLS_DH_anon_WITH_AES_256_CBC_SHA";
/* ECDSA addenda, RFC 4492 */
case TLS_ECDH_ECDSA_WITH_NULL_SHA:
return "TLS_ECDH_ECDSA_WITH_NULL_SHA";
case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
return "TLS_ECDH_ECDSA_WITH_RC4_128_SHA";
case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA";
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA";
case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
return "TLS_ECDHE_ECDSA_WITH_NULL_SHA";
case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
return "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA";
case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
case TLS_ECDH_RSA_WITH_NULL_SHA:
return "TLS_ECDH_RSA_WITH_NULL_SHA";
case TLS_ECDH_RSA_WITH_RC4_128_SHA:
return "TLS_ECDH_RSA_WITH_RC4_128_SHA";
case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA";
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA";
case TLS_ECDHE_RSA_WITH_NULL_SHA:
return "TLS_ECDHE_RSA_WITH_NULL_SHA";
case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
return "TLS_ECDHE_RSA_WITH_RC4_128_SHA";
case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA";
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA";
case TLS_ECDH_anon_WITH_NULL_SHA:
return "TLS_ECDH_anon_WITH_NULL_SHA";
case TLS_ECDH_anon_WITH_RC4_128_SHA:
return "TLS_ECDH_anon_WITH_RC4_128_SHA";
case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA";
case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
return "TLS_ECDH_anon_WITH_AES_128_CBC_SHA";
case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA";
/* TLS 1.2 addenda, RFC 5246 */
case TLS_RSA_WITH_AES_128_CBC_SHA256:
return "TLS_RSA_WITH_AES_128_CBC_SHA256";
case TLS_RSA_WITH_AES_256_CBC_SHA256:
return "TLS_RSA_WITH_AES_256_CBC_SHA256";
case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
return "TLS_DH_DSS_WITH_AES_128_CBC_SHA256";
case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
return "TLS_DH_RSA_WITH_AES_128_CBC_SHA256";
case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256";
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256";
case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
return "TLS_DH_DSS_WITH_AES_256_CBC_SHA256";
case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
return "TLS_DH_RSA_WITH_AES_256_CBC_SHA256";
case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256";
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256";
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
return "TLS_DH_anon_WITH_AES_128_CBC_SHA256";
case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
return "TLS_DH_anon_WITH_AES_256_CBC_SHA256";
/* TLS addenda using AES-GCM, RFC 5288 */
case TLS_RSA_WITH_AES_128_GCM_SHA256:
return "TLS_RSA_WITH_AES_128_GCM_SHA256";
case TLS_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256";
case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256";
case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384";
case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
return "TLS_DH_RSA_WITH_AES_128_GCM_SHA256";
case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_DH_RSA_WITH_AES_256_GCM_SHA384";
case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
return "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256";
case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
return "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384";
case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
return "TLS_DH_DSS_WITH_AES_128_GCM_SHA256";
case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
return "TLS_DH_DSS_WITH_AES_256_GCM_SHA384";
case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
return "TLS_DH_anon_WITH_AES_128_GCM_SHA256";
case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
return "TLS_DH_anon_WITH_AES_256_GCM_SHA384";
/* ECDSA addenda, RFC 5289 */
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256";
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384";
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256";
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384";
case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
return "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
return "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256";
case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384";
case TLS_AES_128_GCM_SHA256:
return "TLS_AES_128_GCM_SHA256";
case TLS_AES_256_GCM_SHA384:
return "TLS_AES_256_GCM_SHA384";
case TLS_CHACHA20_POLY1305_SHA256:
return "TLS_CHACHA20_POLY1305_SHA256";
case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256";
case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
default:
return "TLS_CIPHER_STRING_UNKNOWN";
}
}
static void ssl_ciphers_populate(void)
{
/* SSLGetSupportedCiphers() is deprecated and we can't find
* the replacement API, so we just list the valid ciphers here
* taken from the tls_ciphersuite_t doc.
*/
tls_ciphersuite_t ciphers[] = {
tls_ciphersuite_AES_128_GCM_SHA256,
tls_ciphersuite_AES_256_GCM_SHA384,
tls_ciphersuite_CHACHA20_POLY1305_SHA256,
tls_ciphersuite_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
tls_ciphersuite_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls_ciphersuite_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
tls_ciphersuite_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls_ciphersuite_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls_ciphersuite_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
tls_ciphersuite_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls_ciphersuite_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls_ciphersuite_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
tls_ciphersuite_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls_ciphersuite_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
tls_ciphersuite_RSA_WITH_3DES_EDE_CBC_SHA,
tls_ciphersuite_RSA_WITH_AES_128_CBC_SHA,
tls_ciphersuite_RSA_WITH_AES_128_CBC_SHA256,
tls_ciphersuite_RSA_WITH_AES_128_GCM_SHA256,
tls_ciphersuite_RSA_WITH_AES_256_CBC_SHA,
tls_ciphersuite_RSA_WITH_AES_256_CBC_SHA256,
tls_ciphersuite_RSA_WITH_AES_256_GCM_SHA384
};
if (!ssl_cipher_num) {
unsigned i;
ssl_cipher_num = PJ_ARRAY_SIZE(ciphers);
for (i = 0; i < ssl_cipher_num; i++) {
ssl_ciphers[i].id = (pj_ssl_cipher)ciphers[i];
ssl_ciphers[i].name = sslGetCipherSuiteString(ciphers[i]);
}
}
}
static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
return (pj_ssl_cipher) assock->cipher;
}
#if !TARGET_OS_IPHONE
static void get_info_and_cn(CFArrayRef array, CFMutableStringRef info,
CFStringRef *cn)
{
const void *keys[] = {kSecOIDOrganizationalUnitName, kSecOIDCountryName,
kSecOIDStateProvinceName, kSecOIDLocalityName,
kSecOIDOrganizationName, kSecOIDCommonName};
const char *labels[] = { "OU=", "C=", "ST=", "L=", "O=", "CN="};
pj_bool_t add_separator = PJ_FALSE;
int i, n;
*cn = NULL;
for(i = 0; i < (int)PJ_ARRAY_SIZE(keys); i++) {
for (n = 0 ; n < CFArrayGetCount(array); n++) {
CFDictionaryRef dict;
CFTypeRef dictkey;
CFStringRef str;
dict = CFArrayGetValueAtIndex(array, n);
if (CFGetTypeID(dict) != CFDictionaryGetTypeID())
continue;
dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
if (!CFEqual(dictkey, keys[i]))
continue;
str = (CFStringRef) CFDictionaryGetValue(dict,
kSecPropertyKeyValue);
if (CFStringGetLength(str) > 0) {
if (add_separator) {
CFStringAppendCString(info, "/", kCFStringEncodingUTF8);
}
CFStringAppendCString(info, labels[i], kCFStringEncodingUTF8);
CFStringAppend(info, str);
add_separator = PJ_TRUE;
if (CFEqual(keys[i], kSecOIDCommonName))
*cn = str;
}
}
}
}
static CFDictionaryRef get_cert_oid(SecCertificateRef cert, CFStringRef oid,
CFTypeRef *value)
{
void *key[1];
CFArrayRef key_arr;
CFDictionaryRef vals, dict;
key[0] = (void *)oid;
key_arr = CFArrayCreate(NULL, (const void **)key, 1,
&kCFTypeArrayCallBacks);
vals = SecCertificateCopyValues(cert, key_arr, NULL);
dict = CFDictionaryGetValue(vals, key[0]);
if (!dict) {
CFRelease(key_arr);
CFRelease(vals);
return NULL;
}
*value = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
CFRelease(key_arr);
return vals;
}
#endif
/* Get certificate info; in case the certificate info is already populated,
* this function will check if the contents need updating by inspecting the
* issuer and the serial number.
*/
static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
SecCertificateRef cert)
{
pj_bool_t update_needed;
char buf[512];
size_t bufsize = sizeof(buf);
const pj_uint8_t *serial_no = NULL;
size_t serialsize = 0;
CFMutableStringRef issuer_info;
CFStringRef str;
CFDataRef serial = NULL;
#if !TARGET_OS_IPHONE
CFStringRef issuer_cn = NULL;
CFDictionaryRef dict;
#endif
pj_assert(pool && ci && cert);
/* Get issuer */
issuer_info = CFStringCreateMutable(NULL, 0);
#if !TARGET_OS_IPHONE
{
/* Unfortunately, unlike on Mac, on iOS we don't have these APIs
* to query the certificate info such as the issuer, version,
* validity, and alt names.
*/
CFArrayRef issuer_vals;
dict = get_cert_oid(cert, kSecOIDX509V1IssuerName,
(CFTypeRef *)&issuer_vals);
if (dict) {
get_info_and_cn(issuer_vals, issuer_info, &issuer_cn);
if (issuer_cn)
issuer_cn = CFStringCreateCopy(NULL, issuer_cn);
CFRelease(dict);
}
}
#endif
CFStringGetCString(issuer_info, buf, bufsize, kCFStringEncodingUTF8);
/* Get serial no */
if (__builtin_available(macOS 10.13, iOS 11.0, *)) {
serial = SecCertificateCopySerialNumberData(cert, NULL);
if (serial) {
serial_no = CFDataGetBytePtr(serial);
serialsize = CFDataGetLength(serial);
}
}
/* Check if the contents need to be updated */
update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
pj_memcmp(ci->serial_no, serial_no, serialsize);
if (!update_needed) {
CFRelease(issuer_info);
return;
}
/* Update cert info */
pj_bzero(ci, sizeof(pj_ssl_cert_info));
/* Version */
#if !TARGET_OS_IPHONE
{
CFStringRef version;
dict = get_cert_oid(cert, kSecOIDX509V1Version,
(CFTypeRef *)&version);
if (dict) {
ci->version = CFStringGetIntValue(version);
CFRelease(dict);
}
}
#endif
/* Issuer */
pj_strdup2(pool, &ci->issuer.info, buf);
#if !TARGET_OS_IPHONE
if (issuer_cn) {
CFStringGetCString(issuer_cn, buf, bufsize, kCFStringEncodingUTF8);
pj_strdup2(pool, &ci->issuer.cn, buf);
CFRelease(issuer_cn);
}
#endif
CFRelease(issuer_info);
/* Serial number */
if (serial) {
if (serialsize > sizeof(ci->serial_no))
serialsize = sizeof(ci->serial_no);
pj_memcpy(ci->serial_no, serial_no, serialsize);
CFRelease(serial);
}
/* Subject */
str = SecCertificateCopySubjectSummary(cert);
CFStringGetCString(str, buf, bufsize, kCFStringEncodingUTF8);
pj_strdup2(pool, &ci->subject.cn, buf);
CFRelease(str);
#if !TARGET_OS_IPHONE
{
CFArrayRef subject;
CFMutableStringRef subject_info;
dict = get_cert_oid(cert, kSecOIDX509V1SubjectName,
(CFTypeRef *)&subject);
if (dict) {
subject_info = CFStringCreateMutable(NULL, 0);
get_info_and_cn(subject, subject_info, &str);
CFStringGetCString(subject_info, buf, bufsize, kCFStringEncodingUTF8);
pj_strdup2(pool, &ci->subject.info, buf);
CFRelease(dict);
CFRelease(subject_info);
}
}
#endif
/* Validity */
#if !TARGET_OS_IPHONE
{
CFNumberRef validity;
double interval;
dict = get_cert_oid(cert, kSecOIDX509V1ValidityNotBefore,
(CFTypeRef *)&validity);
if (dict) {
if (CFNumberGetValue(validity, CFNumberGetType(validity),
&interval))
{
/* Darwin's absolute reference date is 1 Jan 2001 00:00:00 GMT */
ci->validity.start.sec = (unsigned long)interval + 978278400L;
}
CFRelease(dict);
}
dict = get_cert_oid(cert, kSecOIDX509V1ValidityNotAfter,
(CFTypeRef *)&validity);
if (dict) {
if (CFNumberGetValue(validity, CFNumberGetType(validity),
&interval))
{
ci->validity.end.sec = (unsigned long)interval + 978278400L;
}
CFRelease(dict);
}
}
#endif
/* Subject Alternative Name extension */
#if !TARGET_OS_IPHONE
{
CFArrayRef altname;
CFIndex i;
dict = get_cert_oid(cert, kSecOIDSubjectAltName, (CFTypeRef *)&altname);
if (!dict || !CFArrayGetCount(altname))
return;
ci->subj_alt_name.entry = pj_pool_calloc(pool, CFArrayGetCount(altname),
sizeof(*ci->subj_alt_name.entry));
for (i = 0; i < CFArrayGetCount(altname); ++i) {
CFDictionaryRef item;
CFStringRef label, value;
pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN;
item = CFArrayGetValueAtIndex(altname, i);
if (CFGetTypeID(item) != CFDictionaryGetTypeID())
continue;
label = (CFStringRef)CFDictionaryGetValue(item, kSecPropertyKeyLabel);
if (CFGetTypeID(label) != CFStringGetTypeID())
continue;
value = (CFStringRef)CFDictionaryGetValue(item, kSecPropertyKeyValue);
if (!CFStringCompare(label, CFSTR("DNS Name"),
kCFCompareCaseInsensitive))
{
if (CFGetTypeID(value) != CFStringGetTypeID())
continue;
CFStringGetCString(value, buf, bufsize, kCFStringEncodingUTF8);
type = PJ_SSL_CERT_NAME_DNS;
} else if (!CFStringCompare(label, CFSTR("IP Address"),
kCFCompareCaseInsensitive))
{
if (CFGetTypeID(value) != CFStringGetTypeID())
continue;
CFStringGetCString(value, buf, bufsize, kCFStringEncodingUTF8);
type = PJ_SSL_CERT_NAME_IP;
} else if (!CFStringCompare(label, CFSTR("Email Address"),
kCFCompareCaseInsensitive))
{
if (CFGetTypeID(value) != CFStringGetTypeID())
continue;
CFStringGetCString(value, buf, bufsize, kCFStringEncodingUTF8);
type = PJ_SSL_CERT_NAME_RFC822;
} else if (!CFStringCompare(label, CFSTR("URI"),
kCFCompareCaseInsensitive))
{
CFStringRef uri;
if (CFGetTypeID(value) != CFURLGetTypeID())
continue;
uri = CFURLGetString((CFURLRef)value);
CFStringGetCString(uri, buf, bufsize, kCFStringEncodingUTF8);
type = PJ_SSL_CERT_NAME_URI;
}
if (type != PJ_SSL_CERT_NAME_UNKNOWN) {
ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
if (type == PJ_SSL_CERT_NAME_IP) {
char ip_buf[PJ_INET6_ADDRSTRLEN+10];
int len = CFStringGetLength(value);
int af = pj_AF_INET();
if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6();
pj_inet_ntop2(af, buf, ip_buf, sizeof(ip_buf));
pj_strdup2(pool,
&ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
ip_buf);
} else {
pj_strdup2(pool,
&ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
buf);
}
ci->subj_alt_name.cnt++;
}
}
CFRelease(dict);
}
#endif
}
/* Update local & remote certificates info. This function should be
* called after handshake successfully completed.
*/
static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
{
applessl_sock_t *assock = (applessl_sock_t *)ssock;
SecTrustRef trust = assock->trust;
CFIndex count;
SecCertificateRef cert;
pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);
/* Get active local certificate */
if (assock->identity) {
CFArrayRef cert_arr;
cert_arr = sec_identity_copy_certificates_ref(assock->identity);
if (cert_arr) {
count = CFArrayGetCount(cert_arr);
if (count > 0) {
CFTypeRef elmt;
elmt = (CFTypeRef) CFArrayGetValueAtIndex(cert_arr, 0);
if (CFGetTypeID(elmt) == SecCertificateGetTypeID()) {
cert = (SecCertificateRef)elmt;
get_cert_info(ssock->pool, &ssock->local_cert_info, cert);
}
}
CFRelease(cert_arr);
}
}
/* Get active remote certificate */
if (trust) {
count = SecTrustGetCertificateCount(trust);
if (count > 0) {
cert = SecTrustGetCertificateAtIndex(trust, 0);
get_cert_info(ssock->pool, &ssock->remote_cert_info, cert);
}
}
}
static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server)
{
PJ_UNUSED_ARG(ssock);
PJ_UNUSED_ARG(is_server);
}
static void ssl_set_peer_name(pj_ssl_sock_t *ssock)
{
/* Setting server name is done when configuring tls before connection
* is started.
*/
PJ_UNUSED_ARG(ssock);
}
static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
{
/* Nothing to do here, just return EPENDING. Handshake has
* automatically been performed when starting a connection.
*/
PJ_UNUSED_ARG(ssock);
return PJ_EPENDING;
}
static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
{
pj_size_t circ_buf_size, read_size;
pj_lock_acquire(ssock->circ_buf_input_mutex);
if (circ_empty(&ssock->circ_buf_input)) {
pj_lock_release(ssock->circ_buf_input_mutex);
*size = 0;
return PJ_SUCCESS;
}
circ_buf_size = circ_size(&ssock->circ_buf_input);
read_size = PJ_MIN(circ_buf_size, (pj_size_t)*size);
circ_read(&ssock->circ_buf_input, data, read_size);
pj_lock_release(ssock->circ_buf_input_mutex);
*size = read_size;
return PJ_SUCCESS;
}
/*
* Write the plain data to buffer. It will be encrypted later during
* sending.
*/
static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
pj_ssize_t size, int *nwritten)
{
pj_status_t status;
status = circ_write(&ssock->circ_buf_output, data, size);
*nwritten = (status == PJ_SUCCESS)? (int)size: 0;
return status;
}
static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock)
{
PJ_UNUSED_ARG(ssock);
/* According to the doc,
* sec_protocol_options_set_tls_renegotiation_enabled() should
* enable TLS session renegotiation for versions 1.2 and earlier.
* But we can't trigger renegotiation manually, or can we?
*/
return PJ_ENOTSUP;
}
#endif /* PJ_SSL_SOCK_IMP_APPLE */