You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libsxmp/sxmp/hub.c

839 lines
24 KiB
C

/*
* Secure X Message Passing Library v2 implementation.
* (sxmplv2) it superseed all versions before due to the:
* - memory consumption
* - new features such as pulse emitting
* - performance optimization
*
* (c) Askele Group 2013-2015 <http://askele.com>
* (c) Alexander Vdolainen 2013-2015, 2016 <avdolainen@zoho.com>
*
* libsxmp is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libsxmp 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef WIN32
#include <Winsock2.h>
#define EBADE 1
#define NETDB_SUCCESS 0
#else
#include <sys/select.h>
#include <netdb.h>
#include <unistd.h>
#include <uuid/uuid.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <tdata/usrtc.h>
#include <sexpr/sexp.h>
#include <sxmp/limits.h>
#include <sxmp/sxmp.h>
#include "internal.h"
static int __insert_rpc_function(usrtc_t *tree, const char *name, int (*rpcf)(void *, sexp_t *))
{
sxl_rpc_t *ent = malloc(sizeof(sxl_rpc_t));
usrtc_node_t *node;
if(!ent) return ENOMEM;
else node = &ent->node;
if(!(ent->name = strdup(name))) {
free(ent);
return ENOMEM;
} else ent->rpcf = rpcf;
usrtc_node_init(node, ent);
usrtc_insert(tree, node, ent->name);
return 0;
}
static void __destroy_rpc_list_tree(usrtc_t *tree)
{
usrtc_node_t *node;
sxl_rpc_t *ent;
for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) {
ent = (sxl_rpc_t *)usrtc_node_getdata(node);
usrtc_delete(tree, node);
free(ent->name);
free(ent);
}
return;
}
/* negotiation functions */
/**
* Proto: (auth-set-credentials "<login>" "<password>")
* Return - message return; opcode as a return of this function.
* in batch mode message with 0 index used always
*/
static int __set_credentials(void *cctx, sexp_t *sx)
{
register int idx;
sxlink_t *link = (sxlink_t *)cctx;
sxhub_t *hub = link->hub;
sexp_t *isx;
char *login = NULL;
char *passwd = NULL;
/* take a deal with S-exp */
SEXP_ITERATE_LIST(sx, isx, idx) {
if(isx->ty == SEXP_LIST) return SXE_BADPROTO;
if(idx > 0 && isx->aty != SEXP_DQUOTE) return SXE_BADPROTO;
if(idx == 1) login = isx->val;
else if(idx == 2) passwd = isx->val;
else if(idx > 2) return SXE_BADPROTO;
}
if(!login || !passwd) return SXE_BADPROTO;
link->pctx->login = strdup(login);
link->pctx->passwd = strdup(passwd);
if(!link->pctx->login || !link->pctx->passwd) {
if(link->pctx->login) free(link->pctx->login);
if(link->pctx->passwd) free(link->pctx->passwd);
return SXE_ENOMEM;
}
if(hub->secure_check) return hub->secure_check(link);
else return SXE_SUCCESS;
}
static int __get_channels_list(void *cctx, sexp_t *sx)
{
sxlink_t *co = (sxlink_t *)cctx;
sxhub_t *ssys = co->hub;
sxmsg_t *msg = co->messages[0];
char *buf = msg->payload;
usrtc_node_t *node;
rpc_typed_list_t *list_ent;
size_t maxlen = 65535 - sizeof(sxmplv2_head_t);
size_t ulen = 0;
/* determine how this function was called */
if(!strcmp(sx->list->val, "get-channels-list"))
co->cp_version = V2;
else co->cp_version = V2_1; /* last supported in this version */
/* call the function */
if(ssys->get_rpc_typed_list_tree)
co->rpc_list = ssys->get_rpc_typed_list_tree(co);
if(!co->rpc_list) return SXE_EPERM;
//buf += sizeof(sxmplv2_head_t);
if(co->cp_version == V2)
ulen += snprintf(buf + ulen, maxlen - ulen, "(set-channels-list ");
else ulen += snprintf(buf + ulen, maxlen - ulen, "(!@c< ");
for(node = usrtc_first(co->rpc_list); node != NULL;
node = usrtc_next(co->rpc_list, node)) { /* fill the list */
list_ent = (rpc_typed_list_t *)usrtc_node_getdata(node);
ulen += snprintf(buf + ulen, maxlen - ulen, "(:%d \"%s\")",
list_ent->type_id, list_ent->description);
}
ulen += snprintf(buf + ulen, maxlen - ulen, ")");
msg->mhead.payload_length = ulen + 1;
if(co->cp_version == V2) {
/* we're ready for messaging mode */
co->flags |= SXMP_MESSAGINGMODE;
co->flags &= ~SXMP_BATCHMODE;
}
return SXE_SUCCESS;
}
static int __get_streams(void *cctx, sexp_t *sx)
{
sxlink_t *link = (sxlink_t *)cctx;
sxhub_t *hub = link->hub;
sxmsg_t *msg = link->messages[0];
char *buf = msg->payload;
usrtc_node_t *node, *rpc_node;
struct sxstream_description *s_desc;
size_t maxlen = 65535 - sizeof(sxmplv2_head_t);
size_t ulen = 0, sts = 0;
int tcid = 0;
if(link->cp_version == V_UNKNOWN) /* oops, version doesn't a happy one */
return SXE_FAILED;
if(!hub->streams) { /* no streams provided */
__return_nil:
ulen = snprintf(buf, maxlen, "(!@s< nil)");
msg->mhead.payload_length = ulen + 1;
/* and now we're ready to exit from batch mode */
link->flags |= SXMP_MESSAGINGMODE;
link->flags &= ~SXMP_BATCHMODE;
return SXE_SUCCESS;
} else { /* set all streams available */
ulen += snprintf(buf + ulen, maxlen - ulen, "(!@s< ");
for(node = usrtc_first(hub->streams); node != NULL; node = usrtc_next(hub->streams, node)) {
s_desc = (struct sxstream_description *)usrtc_node_getdata(node);
tcid = s_desc->pcid;
if((rpc_node = usrtc_lookup(link->rpc_list, &tcid))) { /* channel allowed */
/* ids */
ulen += snprintf(buf + ulen, maxlen - ulen, "(:tcid %d :stid %d :t ", s_desc->pcid,
s_desc->stid);
/* type */
switch(s_desc->type) {
case 0:
ulen += snprintf(buf + ulen, maxlen - ulen, "e ");
break;
case SXE_O_NAMED:
ulen += snprintf(buf + ulen, maxlen - ulen, "n ");
break;
case SXE_O_BINARY:
ulen += snprintf(buf + ulen, maxlen - ulen, "b ");
break;
}
/* flags */
ulen += snprintf(buf + ulen, maxlen - ulen, ":a ");
if(s_desc->flags & SXE_O_READ)
ulen += snprintf(buf + ulen, maxlen - ulen, "r");
else ulen += snprintf(buf + ulen, maxlen - ulen, "-");
if(s_desc->flags & SXE_O_WRITE)
ulen += snprintf(buf + ulen, maxlen - ulen, "w");
else ulen += snprintf(buf + ulen, maxlen - ulen, "-");
if(s_desc->flags & SXE_O_TRUNC)
ulen += snprintf(buf + ulen, maxlen - ulen, "t");
else ulen += snprintf(buf + ulen, maxlen - ulen, "-");
if(s_desc->flags & SXE_O_ASYNC)
ulen += snprintf(buf + ulen, maxlen - ulen, "a");
else ulen += snprintf(buf + ulen, maxlen - ulen, "-");
ulen += snprintf(buf + ulen, maxlen - ulen, ")");
sts++;
}
}
if(!sts) goto __return_nil;
ulen += snprintf(buf + ulen, maxlen - ulen, ")");
msg->mhead.payload_length = ulen + 1;
}
link->flags |= SXMP_MESSAGINGMODE;
link->flags &= ~SXMP_BATCHMODE;
return SXE_SUCCESS;
}
static long __cmp_uint16(const void *a, const void *b)
{
return (long) *(uint16_t*)a - *(uint16_t*)b;
}
#define _MOD_STID 0xa
#define _MOD_TCID 0xb
#define _MOD_TYPE 0xc
#define _MOD_FLAGS 0xd
#define _MOD_UNKWN 0x0
static int __set_streams(void *cctx, sexp_t *sx)
{
register int idx, iidx;
usrtc_t *tree = NULL;
sxlink_t *link = (sxlink_t *)cctx;
sexp_t *isx, *iisx;
struct sxstream_description *s_desc;
usrtc_node_t *node = NULL;
int modp = 0, r, value_mod = 0, mod = _MOD_UNKWN;
int flags, type, stid, pcid;
if(!link->remote_streams) {
tree = malloc(sizeof(usrtc_t));
if(!tree) return SXE_ENOMEM;
usrtc_init(tree, USRTC_SPLAY, MAX_STREAMS_TYPES, __cmp_uint16);
link->remote_streams = tree;
}
SEXP_ITERATE_LIST(sx, isx, idx) {
if(!idx) continue;
if(isx->ty != SEXP_LIST) {
free(tree);
link->remote_streams = NULL;
if(!strcmp(isx->val, "nil")) goto __fini;
else return SXE_BADPROTO;
} else {
SEXP_ITERATE_LIST(isx, iisx, iidx) {
if(iisx->ty == SEXP_LIST) {
r = SXE_BADPROTO;
__clean_up_on_error:
/* clean up all stuff */
for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) {
s_desc = (struct sxstream_description *)usrtc_node_getdata(node);
usrtc_delete(tree, node);
free(s_desc);
}
free(tree);
link->remote_streams = NULL;
return r;
}
if(iisx->val[0] == ':' && value_mod) {
r = SXE_BADPROTO;
goto __clean_up_on_error;
} else if(iisx->val[0] == ':' && !value_mod) {
value_mod = 1;
if(!strcmp(iisx->val, ":stid")) mod = _MOD_STID;
else if(!strcmp(iisx->val, ":tcid")) mod = _MOD_TCID;
else if(!strcmp(iisx->val, ":t")) mod = _MOD_TYPE;
else if(!strcmp(iisx->val, ":a")) mod = _MOD_FLAGS;
else {
r = SXE_BADPROTO;
goto __clean_up_on_error;
}
} else if(iisx->val[0] != ':' && value_mod) {
switch(mod) {
case _MOD_STID:
stid = atoi(iisx->val);
modp++;
break;
case _MOD_TCID:
pcid = atoi(iisx->val);
modp++;
break;
case _MOD_TYPE:
switch(iisx->val[0]) {
case 'e': type = 0; break;
case 'b': type = SXE_O_BINARY; break;
case 'n': type = SXE_O_NAMED; break;
default:
r = SXE_BADPROTO;
goto __clean_up_on_error;
}
modp++;
break;
case _MOD_FLAGS:
flags = 0;
if(iisx->val[0] == 'r') flags |= SXE_O_READ;
else if(iisx->val[1] == 'w') flags |= SXE_O_WRITE;
else if(iisx->val[2] == 't') flags |= SXE_O_TRUNC;
else if(iisx->val[3] == 'a') flags |= SXE_O_ASYNC;
else {
r = SXE_BADPROTO;
goto __clean_up_on_error;
}
modp++;
break;
}
value_mod = 0;
} else {
r = SXE_BADPROTO;
goto __clean_up_on_error;
}
}
/* here we go */
if(modp < 4) {
r = SXE_BADPROTO;
goto __clean_up_on_error;
} else if(!(s_desc = malloc(sizeof(struct sxstream_description)))) {
r = SXE_ENOMEM;
goto __clean_up_on_error;
}
usrtc_node_init(&s_desc->node, s_desc);
s_desc->ops = NULL;
s_desc->flags = (uint16_t)flags;
s_desc->type = (uint16_t)type;
s_desc->pcid = (uint16_t)pcid;
s_desc->stid = (uint16_t)stid;
usrtc_insert(tree, &s_desc->node, &s_desc->stid);
/* finish */
modp = 0;
}
}
__fini:
link->flags |= SXMP_MESSAGINGMODE;
link->flags &= ~SXMP_BATCHMODE;
return SXE_SUCCESS;
}
#undef _MOD_STID
#undef _MOD_TCID
#undef _MOD_TYPE
#undef _MOD_FLAGS
#undef _MOD_UNKWN
static int __set_channels_list(void *cctx, sexp_t *sx)
{
register int idx;
sxlink_t *co = (sxlink_t *)cctx;
sxhub_t *ssys = co->hub;
sexp_t *isx, *iisx;
int id, r;
/* determine how this function was called */
if(!strcmp(sx->list->val, "get-channels-list"))
co->cp_version = V2;
else co->cp_version = V2_1; /* last supported in this version */
SEXP_ITERATE_LIST(sx, isx, idx) {
if(!idx) continue;
if(isx->ty != SEXP_LIST) return SXE_BADPROTO;
if(sexp_list_length(isx) != 2) return SXE_BADPROTO;
/* get id */
sexp_list_car(isx, &iisx);
if(iisx->ty == SEXP_LIST) return SXE_BADPROTO;
if(iisx->aty != SEXP_BASIC) return SXE_BADPROTO;
if(iisx->val[0] != ':') return SXE_BADPROTO;
id = atoi(iisx->val + sizeof(char));
/* get short description */
sexp_list_cdr(isx, &iisx);
if(iisx->ty == SEXP_LIST) return SXE_BADPROTO;
if(iisx->aty != SEXP_DQUOTE) return SXE_BADPROTO;
/* ok, here we go */
if(ssys->set_typed_list_callback) {
r = ssys->set_typed_list_callback(co, id, iisx->val);
if(r != SXE_SUCCESS) return r;
}
}
if(co->cp_version == V2) {
/* we're ready for messaging mode */
co->flags |= SXMP_MESSAGINGMODE;
co->flags &= ~SXMP_BATCHMODE;
}
return SXE_SUCCESS;
}
static int __my_version_ack(void *cctx, sexp_t *sx)
{
register int idx;
sxlink_t *link = (sxlink_t *)cctx;
sexp_t *isx;
SEXP_ITERATE_LIST(sx, isx, idx) {
if(!idx) continue;
if(idx > 2) return SXE_BADPROTO;
if(isx->ty == SEXP_LIST) return SXE_BADPROTO;
if(!strcmp(isx->val, V2_1_TPROT)) link->cp_version = V2_1;
else link->cp_version = V_UNKNOWN;
}
return SXE_SUCCESS;
}
static int __my_version_set(void *cctx, sexp_t *sx)
{
register int idx;
sxlink_t *link = (sxlink_t *)cctx;
sexp_t *isx;
SEXP_ITERATE_LIST(sx, isx, idx) {
if(!idx) continue;
if(idx > 2) return SXE_BADPROTO;
if(isx->ty == SEXP_LIST) return SXE_BADPROTO;
if(!strcmp(isx->val, V2_1_TPROT)) link->cp_version = V2_1;
else return SXE_FAILED; /* failed to set another version */
}
return SXE_SUCCESS;
}
static int __init_systemrpc_tree(usrtc_t *rtree)
{
/* batch mode negotiation context functions */
if(__insert_rpc_function(rtree, "auth-set-credentials", __set_credentials)) goto __fail;
/* old version V2 */
if(__insert_rpc_function(rtree, "get-channels-list", __get_channels_list)) goto __fail; /* old V2 (v1 also) */
if(__insert_rpc_function(rtree, "set-channels-list", __set_channels_list)) goto __fail;
/* sync functions */
/* channels */
if(__insert_rpc_function(rtree, "!@c>", __get_channels_list)) goto __fail;
if(__insert_rpc_function(rtree, "!@c<", __set_channels_list)) goto __fail;
/* version */
if(__insert_rpc_function(rtree, "!@v>", __my_version_ack)) goto __fail;
if(__insert_rpc_function(rtree, "!@v<", __my_version_ack)) goto __fail;
if(__insert_rpc_function(rtree, "!@V>", __my_version_set)) goto __fail;
/* streams */
if(__insert_rpc_function(rtree, "!@s>", __get_streams)) goto __fail;
if(__insert_rpc_function(rtree, "!@s<", __set_streams)) goto __fail;
return 0;
__fail:
__destroy_rpc_list_tree(rtree);
return ENOMEM;
}
static int __init_builtinrpc_tree(usrtc_t *rtree)
{
/* streams */
if(__insert_rpc_function(rtree, _SXSTREAMOPEN_CMD, _builtin_stream_open)) goto __fail;
if(__insert_rpc_function(rtree, _SXSTREAMCLOSE_CMD, _builtin_stream_close)) goto __fail;
if(__insert_rpc_function(rtree, _SXSTREAMEREAD_CMD, _builtin_stream_eread)) goto __fail;
if(__insert_rpc_function(rtree, _SXSTREAMBREAD_CMD, _builtin_stream_bread)) goto __fail;
if(__insert_rpc_function(rtree, _SXSTREAMBWRITE_CMD, _builtin_stream_bwrite)) goto __fail;
return 0;
__fail:
__destroy_rpc_list_tree(rtree);
return ENOMEM;
}
static long __cmp_cstr(const void *a, const void *b)
{
return (long)strcmp((const char *)a, (const char *)b);
}
static int __sxhub_log_dummy(const sxlogtype_t type, const char *fmt, ...)
{
int r;
va_list arglist;
va_start(arglist, fmt);
r = vfprintf(stderr, fmt, arglist);
va_end(arglist);
return r;
}
int sxhub_init(sxhub_t *ssys)
{
int r = 0;
if(!ssys) return EINVAL;
else memset(ssys, 0, sizeof(sxhub_t));
if(!(ssys->links = malloc(sizeof(usrtc_t)))) return ENOMEM;
/* init links list */
usrtc_init(ssys->links, USRTC_REDBLACK, MAX_LINKS,
__cmp_cstr);
if((r = pthread_rwlock_init(&(ssys->rwlock), NULL)))
goto __fini;
/* init RPC list related functions */
if(!(ssys->system_rpc = malloc(sizeof(sxl_rpclist_t)))) {
r = ENOMEM;
goto __lfini;
} else {
if(!(ssys->system_rpc->rpc_tree = malloc(sizeof(usrtc_t)))) {
r = ENOMEM;
goto __lfini;
}
usrtc_init(ssys->system_rpc->rpc_tree, USRTC_SPLAY, 256, __cmp_cstr);
r = __init_systemrpc_tree(ssys->system_rpc->rpc_tree);
if(r) {
free(ssys->system_rpc->rpc_tree);
goto __lfini;
}
}
/* init builtin functions list */
if(!(ssys->stream_rpc = malloc(sizeof(sxl_rpclist_t)))) {
r = ENOMEM;
goto __lfini;
} else {
if(!(ssys->stream_rpc->rpc_tree = malloc(sizeof(usrtc_t)))) {
r = ENOMEM;
goto __lfini;
}
usrtc_init(ssys->stream_rpc->rpc_tree, USRTC_SPLAY, 256, __cmp_cstr);
r = __init_builtinrpc_tree(ssys->stream_rpc->rpc_tree);
if(r) {
free(ssys->stream_rpc->rpc_tree);
goto __lfini;
}
}
/* set function pointers to default implementation */
sxhub_set_logops(ssys, __sxhub_log_dummy);
return 0;
__lfini:
if(ssys->system_rpc) free(ssys->system_rpc);
if(ssys->stream_rpc) free(ssys->stream_rpc);
pthread_rwlock_destroy(&(ssys->rwlock));
__fini:
if(ssys->links) free(ssys->links);
return r;
}
int sxhub_free(sxhub_t *ssys)
{
usrtc_node_t *node;
__destroy_rpc_list_tree(ssys->system_rpc->rpc_tree);
free(ssys->system_rpc->rpc_tree);
free(ssys->system_rpc);
/* free streams description tree */
if(ssys->streams) {
for(node = usrtc_first(ssys->streams); node != NULL; node = usrtc_first(ssys->streams))
usrtc_delete(ssys->streams, node);
free(ssys->streams);
}
free(ssys->links);
pthread_rwlock_destroy(&(ssys->rwlock));
SSL_CTX_free(ssys->ctx);
return 0;
}
int sxhub_destroy(sxhub_t *ssys)
{
int r = sxhub_free(ssys);
free(ssys);
return r;
}
sxhub_t *sxhub_create(void)
{
int r = 0;
sxhub_t *nsys = malloc(sizeof(sxhub_t));
if(!nsys) {
errno = ENOMEM;
return NULL;
}
r = sxhub_init(nsys);
if(r) {
errno = r;
free(nsys);
return NULL;
}
return nsys;
}
int sxhub_setsslserts(sxhub_t *ssys, const char *rootca,
const char *certpem, const char *certkey)
{
if (!ssys || !rootca || !certpem || !certkey)
return EINVAL;
/* check on existence of certificate files */
if (access(rootca, R_OK) == -1) {
ssys->log(SXERROR_LOG, "Unable to read rootca file '%s': %s\n",
rootca, sys_errlist[errno]);
return ENOENT;
}
if (access(certpem, R_OK) == -1) {
ssys->log(SXERROR_LOG, "Unable to read certpem file '%s': %s\n",
certpem, sys_errlist[errno]);
return ENOENT;
}
if (access(certkey, R_OK) == -1) {
ssys->log(SXERROR_LOG, "Unable to read certkey file '%s': %s\n",
certkey, sys_errlist[errno]);
return ENOENT;
}
/* obtain full paths of certificate files */
int r;
if (NULL == (ssys->rootca = realpath(rootca, NULL))) {
ssys->log(SXERROR_LOG, "Unable to obtain the full path of '%s': %s\n",
rootca, sys_errlist[errno]);
if (errno == ENOMEM) r = errno;
goto __fail;
}
if (NULL == (ssys->certpem = realpath(certpem, NULL))) {
ssys->log(SXERROR_LOG, "Unable to obtain the full path of '%s': %s\n",
certpem, sys_errlist[errno]);
if (errno == ENOMEM) r = errno;
goto __fail;
}
if (NULL == (ssys->certkey = realpath(certkey, NULL))) {
ssys->log(SXERROR_LOG, "Unable to obtain the full path of '%s': %s\n",
certkey, sys_errlist[errno]);
if (errno == ENOMEM) r = errno;
goto __fail;
}
return 0;
__fail:
if(ssys->rootca) free(ssys->rootca);
if(ssys->certkey) free(ssys->certkey);
if(ssys->certpem) free(ssys->certpem);
return r;
}
int sxhub_stream_register(sxhub_t *hub, const struct sxstream_description *s_desc)
{
usrtc_t *tree = NULL;
usrtc_node_t *node = NULL;
if(!hub->streams) {
tree = malloc(sizeof(usrtc_t));
if(!tree) return ENOMEM;
usrtc_init(tree, USRTC_SPLAY, MAX_STREAMS_TYPES, __cmp_uint16);
hub->streams = tree;
} else {
tree = hub->streams;
node = usrtc_lookup(tree, &s_desc->stid);
if(node) return EEXIST;
}
node = (usrtc_node_t *)&s_desc->node;
usrtc_node_init(node, (void *)s_desc);
usrtc_insert(tree, node, &s_desc->stid);
return 0;
}
/* internally used functions */
static int __verify_certcall_dummy(int preverify_ok, X509_STORE_CTX *ctx)
{
return preverify_ok;
}
int ex_ssldata_index;
/* this is a callback to perform a custom SSL certs chain validation,
* as I promised here the comments, a lot of ...
* The first shit: 0 means validation failed, 1 otherwise
* The second shit: X509 API, I guess u will love it ;-)
* openssl calls this function for each certificate in chain,
* since our case is a simple (depth of chain is one, since we're
* don't care for public certificates lists or I cannot find any reasons to
* do it ...), amount of calls reduced, and in this case we're interested
* only in top of chain i.e. actual certificate used on client side,
* the validity of signing for other certificates within chain is
* guaranteed by the ssl itself.
* u know, we need to lookup in database, or elsewhere... some information
* about client certificate, and decide - is it valid, or not?, if so
* yep I mean it's valid, we can assign it's long fucking number to
* security context, to use in ongoing full scaled connection handshaking.
*/
static int __verify_certcall(int preverify_ok, X509_STORE_CTX *ctx)
{
// X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
int err = X509_STORE_CTX_get_error(ctx), depth = X509_STORE_CTX_get_error_depth(ctx);
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
sxlink_t *link = SSL_get_ex_data(ssl, ex_ssldata_index); /* this is a custom data we're set before */
sxhub_t *hub = link->hub;
/* now we need to check for certificates with a long chain,
* so since we have a short one, reject long ones */
if(depth > VERIFY_DEPTH) { /* longer than we expect */
preverify_ok = 0; /* yep, 0 means error for those function callback in openssl, fucking set */
err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
X509_STORE_CTX_set_error(ctx, err);
}
if(!preverify_ok) return 0;
/* ok, now we're on top of SSL (depth == 0) certs chain,
* and we can validate client certificate */
if(!depth) {
link->pctx->certid = ASN1_INTEGER_get((const ASN1_INTEGER *)X509_get_serialNumber(ctx->current_cert));
/* now we're need to check the ssl cert */
if(hub->validate_sslpem) {
if(hub->validate_sslpem(link)) return 0;
else return 1;
} else return 0;
}
return preverify_ok;
}
int _sxhub_settls_ctx(sxhub_t *hub, const char *crtfile)
{
if(!hub->ctx) {
/* init SSL certificates and context */
if(!(hub->ctx = SSL_CTX_new(TLSv1_2_client_method()))) return SXE_ENOMEM;
/* set verify context */
SSL_CTX_set_verify(hub->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
__verify_certcall_dummy);
/* set verify depth */
SSL_CTX_set_verify_depth(hub->ctx, VERIFY_DEPTH);
/* load certificates */
SSL_CTX_load_verify_locations(hub->ctx, hub->rootca, NULL);
}
/* set the local certificate from CertFile */
if(SSL_CTX_use_certificate_file(hub->ctx, crtfile, SSL_FILETYPE_PEM)<=0) return SXE_ESSL;
/* set the private key from KeyFile (may be the same as CertFile) */
if(SSL_CTX_use_PrivateKey_file(hub->ctx, crtfile, SSL_FILETYPE_PEM)<=0) return SXE_ESSL;
/* verify private key */
if (!SSL_CTX_check_private_key(hub->ctx)) return SXE_ESSL;
return SXE_SUCCESS;
}
int _sxhub_settls_ctx_s(sxhub_t *hub)
{
if(hub->ctx) return SXE_SUCCESS;
/* init SSL certificates and context */
if(!(hub->ctx = SSL_CTX_new(TLSv1_2_server_method()))) return SXE_ENOMEM;
/* set verify context */
SSL_CTX_set_verify(hub->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
__verify_certcall);
/* set verify depth */
SSL_CTX_set_verify_depth(hub->ctx, VERIFY_DEPTH);
/* set cache policy */
SSL_CTX_set_session_cache_mode(hub->ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_mode(hub->ctx, SSL_MODE_RELEASE_BUFFERS);
/* load certificates */
SSL_CTX_load_verify_locations(hub->ctx, hub->rootca, NULL);
/* set the local certificate from CertFile */
if(SSL_CTX_use_certificate_file(hub->ctx, hub->certpem, SSL_FILETYPE_PEM) <= 0) return SXE_ESSL;
/* set the private key from KeyFile (may be the same as CertFile) */
if(SSL_CTX_use_PrivateKey_file(hub->ctx, hub->certkey, SSL_FILETYPE_PEM) <= 0) return SXE_ESSL;
/* verify private key */
if (!SSL_CTX_check_private_key(hub->ctx)) return SXE_ESSL;
return SXE_SUCCESS;
}