/* * Secure Network Transport Layer Library v2 implementation. * (sntllv2) it superseed all versions before due to the: * - memory consumption * - new features such as pulse emitting * - performance optimization * * This is a proprietary software. See COPYING for further details. * * (c) Askele Group 2013-2015 * */ #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #define EBADE 1 #define NETDB_SUCCESS 0 #else #include #include #include #include #endif #include #include #include #include #include static int ex_ssldata_index; /** < index used to work with additional data * provided to the special call during SSL handshake */ /* this function is an ugly implementation to get C string with uuid */ extern char *__generate_uuid(void); /* 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()); conn_t *co = SSL_get_ex_data(ssl, ex_ssldata_index); /* this is a custom data we're set before */ conn_sys_t *ssys = co->ssys; /* 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) { co->pctx->certid = ASN1_INTEGER_get((const ASN1_INTEGER *)X509_get_serialNumber(ctx->current_cert)); /* now we're need to check the ssl cert */ if(ssys->validate_sslpem) { if(ssys->validate_sslpem(co)) return 0; else return 1; } else return 0; } return preverify_ok; } /* dummy just to check the server side */ static int __verify_certcall_dummy(int preverify_ok, X509_STORE_CTX *ctx) { return preverify_ok; } int sntl_init(void) { /* init SSL library */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ex_ssldata_index = SSL_get_ex_new_index(0, "__ssldata index", NULL, NULL, NULL); return 0; } conn_t *__connection_minimal_alloc(struct in_addr *addr) { conn_t *co = malloc(sizeof(conn_t)); int r; if(!co) { r = ENOMEM; goto __fail; } else memset(co, 0, sizeof(conn_t)); if(!(co->messages = malloc(sizeof(uintptr_t)*1024))) { r = ENOMEM; goto __fail; } else memset(co->messages, 0, sizeof(uintptr_t)*1024); if(!(co->pctx = malloc(sizeof(perm_ctx_t)))) { r = ENOMEM; goto __fail; } else memset(co->pctx, 0, sizeof(perm_ctx_t)); if(addr) { if(!(co->pctx->addr = malloc(sizeof(struct in_addr)))) { r = ENOMEM; goto __fail; } memcpy(co->pctx->addr, addr, sizeof(struct in_addr)); } if(!(co->uuid = __generate_uuid())) { r = ENOMEM; goto __fail; } return co; __fail: if(co) { if(co->pctx) { if(co->pctx->addr) free(co->pctx->addr); free(co->pctx); } if(co->messages) free(co->messages); free(co); } errno = r; return NULL; } static void __connection_minimal_free(conn_t *co) { if(co) { if(co->pctx) { if(co->pctx->addr) free(co->pctx->addr); free(co->pctx); } if(co->messages) free(co->messages); free(co->uuid); free(co); } return; } static int __eval_syssexp(conn_t *co, sexp_t *sx) { cx_rpc_list_t *rpc_list = co->ssys->system_rpc; usrtc_node_t *node; cx_rpc_t *rentry; char *rpcf; if(sx->ty == SEXP_LIST) rpcf = sx->list->val; else return SNE_BADPROTO; /* find an appropriate function */ node = usrtc_lookup(rpc_list->rpc_tree, rpcf); if(!node) return SNE_ENORPC; else rentry = (cx_rpc_t *)usrtc_node_getdata(node); /* call it */ return rentry->rpcf((void *)co, sx); } int connection_create_fapi_m(conn_sys_t *ssys, conn_t *co, int sck, struct in_addr *addr) { void *buf = NULL; char *bbuf; conn_t *coc = __connection_minimal_alloc(addr); sx_msg_t *msg = NULL; sntllv2_head_t *head; size_t rd; if(!coc) return SNE_ENOMEM; /* ok, now we need to init ssl stuff */ co->ssys = ssys; /* init SSL certificates and context */ co->ctx = SSL_CTX_new(TLSv1_2_server_method()); if(!co->ctx) { goto __fail; } else { /* set verify context */ SSL_CTX_set_verify(co->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, __verify_certcall); /* set verify depth */ SSL_CTX_set_verify_depth(co->ctx, VERIFY_DEPTH); } /* load certificates */ SSL_CTX_load_verify_locations(co->ctx, ssys->rootca, NULL); /* set the local certificate from CertFile */ if(SSL_CTX_use_certificate_file(co->ctx, ssys->certpem, SSL_FILETYPE_PEM)<=0) { ERR_print_errors_fp(stderr); goto __fail; } /* set the private key from KeyFile (may be the same as CertFile) */ if(SSL_CTX_use_PrivateKey_file(co->ctx, ssys->certkey, SSL_FILETYPE_PEM)<=0) { goto __fail; } /* verify private key */ if (!SSL_CTX_check_private_key(co->ctx)) { goto __fail; } /* now we will create an SSL connection */ co->ssl = SSL_new(co->ctx); if(!co->ssl) goto __fail; else SSL_set_fd(co->ssl, sck); /* attach connected socket */ SSL_set_accept_state(co->ssl); /* set the context to verify ssl connection */ SSL_set_ex_data(co->ssl, ex_ssldata_index, (void *)co); SSL_set_accept_state(co->ssl); if(SSL_accept(co->ssl) == -1) goto __fail; /* ok, now we are able to allocate and so on */ /* set connection to the batch mode */ co->flags |= SNSX_BATCHMODE; /* allocate our first buffer */ buf = mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if(buf == MAP_FAILED) goto __fail2; /* allocate first message */ if(!(msg = malloc(sizeof(sx_msg_t)))) goto __fail2; else { memset(msg, 0, sizeof(sx_msg_t)); coc->messages[0] = msg; } bbuf = (char *)buf; bbuf += sizeof(sntllv2_head_t); sexp_t *sx; while(co->flags & SNSX_BATCHMODE) { rd = __conn_read(co, buf, sizeof(sntllv2_head_t)); if(rd == sizeof(sntllv2_head_t)) { head = (sntllv2_head_t *)buf; /* check for returns */ if(head->opcode != SNE_SUCCESS) goto __fail3; else { /* opcode is fine */ /* if we're ready for messaging mode, turn off batch mode */ if(co->flags & SNSX_MESSAGINGMODE) co->flags &= ~SNSX_BATCHMODE; } if(!head->payload_length) continue; /* pass the following check up */ rd = __conn_read(co, bbuf, head->payload_length); if(rd != head->payload_length) goto __fail3; bbuf[rd] = '\0'; sx = parse_sexp(bbuf, rd); if(!sx) goto __fail3; /* initialize message */ msg->payload = bbuf; msg->payload_length = 0; /* deal with it */ r = __eval_syssexp(co, sx); head->opcode = r; if(r != SNE_SUCCESS) { /* we finish */ head->payload_length = 0; __conn_write(co, buf, sizeof(sntllv2_head_t)); destroy_sexp(sx); goto __fail3; } rd = __conn_write(co, buf, sizeof(sntllv2_head_t) + msg->payload_length); if(rd != sizeof(sntllv2_head_t) + msg->payload_length) { destroy_sexp(sx); goto __fail3; } destroy_sexp(sx); } else goto __fail3; } /* if we're there - negotiation is done, going to init messaging mode */ r = __connection_second_alloc(co); if(r != SNE_SUCCESS) goto __fail3; /* and now we're need to create a thread poll */ return SNE_SUCCESS; __fail4: __connection_second_free(co); __fail3: if(ssys->on_destroy) ssys->on_destroy(co); __fail2: if(msg) free(msg); if(buf != MAP_FAILED) munmap(buf, 65536); SSL_shutdown(co->ssl); __fail: if(coc) { if(co->ssl) SSL_free(co->ssl); if(co->ctx) SSL_CTX_free(co->ctx); __connection_minimal_free(coc); } close(sck); return SNE_FAILED; }