static int add_pem_cert(SSL_CTX *sslCtx, char const* pem_cert) { X509 *cert; BIO *mem = BIO_new(BIO_s_mem()); if (NULL == mem) { PUBNUB_LOG_ERROR("SSL_CTX=%p: Failed BIO_new for PEM certificate\n", sslCtx); return -1; } BIO_puts(mem, pem_cert); cert = PEM_read_bio_X509(mem, NULL, 0, NULL); BIO_free(mem); if (NULL == cert) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("SSL_CTX=%p: Failed to read PEM certificate\n", sslCtx); return -1; } if (0 == X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert)) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("SSL_CTX=%p: Failed to add PEM certificate\n", sslCtx); X509_free(cert); return -1; } X509_free(cert); return 0; }
static int do_decrypt(EVP_CIPHER_CTX* aes256, pubnub_bymebl_t data, uint8_t const* key, uint8_t const* iv, pubnub_bymebl_t *msg) { int len = 0; if (!EVP_DecryptInit_ex(aes256, EVP_aes_256_cbc(), NULL, key, iv)) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("Failed to initialize AES-256 decryption\n"); return -1; } if (!EVP_DecryptUpdate(aes256, msg->ptr, &len, data.ptr, data.size)) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("Failed to AES-256 decrypt the mesage\n"); return -1; } msg->size = len; if (!EVP_DecryptFinal_ex(aes256, msg->ptr + len, &len)) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("Failed to finalize AES-256 decryption\n"); return -1; } msg->size += len; return 0; }
pubnub_bymebl_t pbaes256_decrypt_alloc(pubnub_bymebl_t data, uint8_t const* key, uint8_t const* iv) { int decrypt_result; EVP_CIPHER_CTX *aes256; pubnub_bymebl_t result; result.size = data.size + EVP_CIPHER_block_size(EVP_aes_256_cbc()) + 1; result.ptr = (uint8_t*)malloc(result.size); if (NULL == result.ptr) { return result; } aes256 = EVP_CIPHER_CTX_new(); if (NULL == aes256) { PUBNUB_LOG_ERROR("Failed to allocate AES-256 decryption context\n"); free(result.ptr); result.ptr = NULL; return result;; } decrypt_result = do_decrypt(aes256, data, key, iv, &result); EVP_CIPHER_CTX_free(aes256); if (decrypt_result != 0) { PUBNUB_LOG_ERROR("Failed AES-256 decryption\n"); free(result.ptr); result.ptr = NULL; } return result; }
enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) { fd_set read_set, write_set; int socket; int rslt; struct timeval timev = { 0, 300000 }; if (-1 == BIO_get_fd(pb->pal.socket, &socket)) { PUBNUB_LOG_ERROR("pbpal_connected(): Uninitialized BIO!\n"); return pbpal_connect_resource_failure; } FD_ZERO(&read_set); FD_ZERO(&write_set); FD_SET(socket, &read_set); FD_SET(socket, &write_set); rslt = select(socket + 1, &read_set, &write_set, NULL, &timev); if (SOCKET_ERROR == rslt) { PUBNUB_LOG_ERROR("pbpal_connected(): select() Error!\n"); return pbpal_connect_resource_failure; } else if (rslt > 0) { PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n"); return pbpal_resolv_and_connect(pb); } PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n"); return pbpal_connect_wouldblock; }
pubnub_bymebl_t pbaes256_encrypt_alloc(pubnub_bymebl_t msg, uint8_t const* key, uint8_t const* iv) { int encrypt_result; pubnub_bymebl_t result = { NULL, 0 }; EVP_CIPHER_CTX* aes256 = EVP_CIPHER_CTX_new(); if (NULL == aes256) { PUBNUB_LOG_ERROR("Failed to allocate AES-256 encryption context\n"); return result; } result.ptr = (uint8_t*)malloc(msg.size + EVP_CIPHER_block_size(EVP_aes_256_cbc())); if (NULL == result.ptr) { EVP_CIPHER_CTX_free(aes256); PUBNUB_LOG_ERROR("Failed to allocate memory for AES-256 encryption\n"); return result; } encrypt_result = do_encrypt(aes256, msg, key, iv, &result); if (-1 == encrypt_result) { free(result.ptr); result.ptr = NULL; } EVP_CIPHER_CTX_free(aes256); return result; }
static enum pbpal_resolv_n_connect_result resolv_and_connect_wout_SSL(pubnub_t *pb) { PUBNUB_LOG_TRACE("resolv_and_connect_wout_SSL\n"); if (NULL == pb->pal.socket) { char const*origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb); pb->pal.socket = BIO_new_connect((char*)origin); } if (NULL == pb->pal.socket) { return pbpal_resolv_resource_failure; } BIO_set_conn_port(pb->pal.socket, "http"); BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io); WATCH_ENUM(pb->options.use_blocking_io); if (BIO_do_connect(pb->pal.socket) <= 0) { if (BIO_should_retry(pb->pal.socket)) { return pbpal_connect_wouldblock; } ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("BIO_do_connect failed\n"); return pbpal_connect_failed; } PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb); { int fd = BIO_get_fd(pb->pal.socket, NULL); socket_set_rcv_timeout(fd, pb->transaction_timeout_ms); } return pbpal_connect_success; }
static void sublup_context_callback(pubnub_t* pb, enum pubnub_trans trans, enum pubnub_res result, void* user_data) { pubnub_subloop_t* pbsld = (pubnub_subloop_t*)user_data; PUBNUB_ASSERT_OPT(pbsld != NULL); pubnub_mutex_lock(pbsld->monitor); if (PBTT_SUBSCRIBE == trans) { if (PNR_OK == result) { char const* msg; for (msg = pubnub_get(pb); msg != NULL; msg = pubnub_get(pb)) { pbsld->cb(pb, msg, PNR_OK); } } else { pbsld->cb(pb, NULL, result); } result = pubnub_subscribe_ex(pbsld->pbp, pbsld->channel, pbsld->options); if (result != PNR_STARTED) { PUBNUB_LOG_ERROR("Failed to re-subscribe in the subscribe loop, " "error code = %d\n", result); } } pubnub_mutex_unlock(pbsld->monitor); }
static int print_to_pubnub_log(const char *s, size_t len, void *p) { PUBNUB_UNUSED(len); PUBNUB_UNUSED(p); PUBNUB_LOG_ERROR("%s", s); return 0; }
int send_dns_query(int skt, struct sockaddr const* dest, char const* host) { uint8_t buf[4096]; int to_send; int sent_to; if (-1 == pubnub_prepare_dns_request(buf, sizeof buf, host, &to_send)) { PUBNUB_LOG_ERROR("Couldn't prepare dns request! : #prepared bytes=%d\n", to_send); return -1; } TRACE_SOCKADDR("Sending DNS query to: ", dest); sent_to = sendto(skt, (char*)buf, to_send, 0, dest, sizeof *dest); if (sent_to <= 0) { return socket_would_block() ? +1 : -1; } else if (to_send != sent_to) { PUBNUB_LOG_ERROR("sendto() sent %d out of %d bytes!\n", sent_to, to_send); return -1; } return 0; }
int pbaes256_decrypt(pubnub_bymebl_t data, uint8_t const* key, uint8_t const* iv, pubnub_bymebl_t *msg) { int result; EVP_CIPHER_CTX *aes256; if (msg->size < data.size + EVP_CIPHER_block_size(EVP_aes_256_cbc()) + 1) { PUBNUB_LOG_ERROR("Not enough room to save AES-256 decrypted data\n"); return -1; } aes256 = EVP_CIPHER_CTX_new(); if (NULL == aes256) { PUBNUB_LOG_ERROR("Failed to allocate AES-256 decryption context\n"); return -1; } result = do_decrypt(aes256, data, key, iv, msg); EVP_CIPHER_CTX_free(aes256); return result; }
int pbpal_send(pubnub_t *pb, void const *data, size_t n) { if (n == 0) { return 0; } if (pb->sock_state != STATE_NONE) { PUBNUB_LOG_ERROR("pbpal_send(): pb->sock_state != STATE_NONE (=%d)\n", pb->sock_state); return -1; } pb->sendptr = (uint8_t*)data; pb->sendlen = (uint16_t)n; pb->sock_state = STATE_NONE; return pbpal_send_status(pb); }
int pbaes256_encrypt(pubnub_bymebl_t msg, uint8_t const* key, uint8_t const* iv, pubnub_bymebl_t *encrypted) { int result; EVP_CIPHER_CTX* aes256 = EVP_CIPHER_CTX_new(); if (NULL == aes256) { PUBNUB_LOG_ERROR("Failed to allocate AES-256 encryption context\n"); return -1; } result = do_encrypt(aes256, msg, key, iv, encrypted); EVP_CIPHER_CTX_free(aes256); return result; }
int pubnub_free_with_timeout(pubnub_t* pbp, unsigned millisec) { const clock_t t0 = clock(); const clock_t clocks_till_timeout = (millisec * CLOCKS_PER_SEC) / 1000; PUBNUB_ASSERT_OPT(pbp != NULL); while (pubnub_free(pbp) != 0) { const clock_t elapsed = clock() - t0; if (elapsed > clocks_till_timeout) { PUBNUB_LOG_ERROR("Failed to free the context in %u milli seconds\n", millisec); return -1; } } PUBNUB_LOG_TRACE("Freed the context in %lf seconds\n", ((float)clock() - t0) / CLOCKS_PER_SEC); return 0; }
enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) { fd_set write_set; int rslt; struct timeval timev = { 0, 300000 }; FD_ZERO(&write_set); FD_SET(pb->pal.socket, &write_set); rslt = select(pb->pal.socket + 1, NULL, &write_set, NULL, &timev); if (SOCKET_ERROR == rslt) { PUBNUB_LOG_ERROR("pbpal_connected(): select() Error!\n"); return pbpal_connect_resource_failure; } else if (rslt > 0) { PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n"); return pbpal_connect_success; } PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n"); return pbpal_connect_wouldblock; }
void pbproxy_handle_http_header(pubnub_t *p, char const* header) { char scheme_basic[] = "Basic"; char scheme_NTLM[] = "NTLM"; char proxy_auth[] = "Proxy-Authenticate: "; char const* contents; PUBNUB_ASSERT_OPT(p != NULL); PUBNUB_ASSERT_OPT(header != NULL); if (strncmp(p->core.http_buf, proxy_auth, sizeof proxy_auth - 1) != 0) { return; } contents = p->core.http_buf + sizeof proxy_auth - 1; PUBNUB_LOG_TRACE("pbproxy_handle_http_header(header='%s', contents='%s')\n", header, contents); if (0 == strncmp(contents, scheme_basic, sizeof scheme_basic -1)) { /* We ignore the "realm" for now */ PUBNUB_LOG_TRACE("pbproxy_handle_http_header() Basic authentication\n"); p->proxy_auth_scheme = pbhtauBasic; p->proxy_authorization_sent = false; } else if (0 == strncmp(contents, scheme_NTLM, sizeof scheme_NTLM -1)) { if (pbhtauNTLM != p->proxy_auth_scheme) { pbntlm_core_init(p); p->proxy_auth_scheme = pbhtauNTLM; p->proxy_authorization_sent = false; } else { char const *base64_msg = contents + sizeof scheme_NTLM; pbntlm_core_handle(p, base64_msg, strcspn(base64_msg, " \r\n")); } } else { PUBNUB_LOG_ERROR("Proxy Authentication '%s' not supported\n", contents); p->proxy_auth_scheme = pbhtauNone; } }
int pbpal_start_read(pubnub_t *pb, size_t n) { if (pb->sock_state != STATE_NONE) { PUBNUB_LOG_ERROR("pbpal_start_read(): pb->sock_state != STATE_NONE: "); WATCH_ENUM(pb->sock_state); return -1; } if (pb->ptr > (uint8_t*)pb->core.http_buf) { unsigned distance = pb->ptr - (uint8_t*)pb->core.http_buf; memmove(pb->core.http_buf, pb->ptr, pb->readlen); pb->ptr -= distance; pb->left += distance; } else { if (pb->left == 0) { /* Obviously, our buffer is not big enough, maybe some error should be reported */ buf_setup(pb); } } pb->sock_state = STATE_READ; pb->len = n; return +1; }
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb) { SSL *ssl = NULL; int rslt; char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_CONNECT)); if (!pb->options.useSSL) { return resolv_and_connect_wout_SSL(pb); } if (NULL == pb->pal.ctx) { PUBNUB_LOG_TRACE("pb=%p: Don't have SSL_CTX\n", pb); pb->pal.ctx = SSL_CTX_new(SSLv23_client_method()); if (NULL == pb->pal.ctx) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("pb=%p SSL_CTX_new failed\n", pb); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Got SSL_CTX\n", pb); add_pubnub_cert(pb->pal.ctx); } if (NULL == pb->pal.socket) { PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb); pb->pal.socket = BIO_new_ssl_connect(pb->pal.ctx); if (PUBNUB_TIMERS_API) { pb->pal.connect_timeout = time(NULL) + pb->transaction_timeout_ms/1000; } } else { BIO_get_ssl(pb->pal.socket, &ssl); if (NULL == ssl) { return resolv_and_connect_wout_SSL(pb); } ssl = NULL; } if (NULL == pb->pal.socket) { ERR_print_errors_cb(print_to_pubnub_log, NULL); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Using BIO == %p\n", pb, pb->pal.socket); BIO_get_ssl(pb->pal.socket, &ssl); PUBNUB_ASSERT(NULL != ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* maybe not auto_retry? */ if (pb->pal.session != NULL) { SSL_set_session(ssl, pb->pal.session); } BIO_set_conn_hostname(pb->pal.socket, origin); BIO_set_conn_port(pb->pal.socket, "https"); if (pb->pal.ip_timeout != 0) { if (pb->pal.ip_timeout < time(NULL)) { pb->pal.ip_timeout = 0; } else { PUBNUB_LOG_TRACE("SSL re-connect to: %d.%d.%d.%d\n", pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); BIO_set_conn_ip(pb->pal.socket, pb->pal.ip); } } BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io); WATCH_ENUM(pb->options.use_blocking_io); if (BIO_do_connect(pb->pal.socket) <= 0) { if (BIO_should_retry(pb->pal.socket) && PUBNUB_TIMERS_API && (pb->pal.connect_timeout > time(NULL))) { PUBNUB_LOG_TRACE("pb=%p: BIO_should_retry\n", pb); return pbpal_connect_wouldblock; } /* Expire the IP for the next connect */ pb->pal.ip_timeout = 0; ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); pb->pal.session = NULL; } PUBNUB_LOG_ERROR("pb=%p: BIO_do_connect failed\n", pb); return pbpal_connect_failed; } PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb); { int fd = BIO_get_fd(pb->pal.socket, NULL); socket_set_rcv_timeout(fd, pb->transaction_timeout_ms); } rslt = SSL_get_verify_result(ssl); if (rslt != X509_V_OK) { PUBNUB_LOG_WARNING("pb=%p: SSL_get_verify_result() failed == %d(%s)\n", pb, rslt, X509_verify_cert_error_string(rslt)); ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->options.fallbackSSL) { BIO_free_all(pb->pal.socket); pb->pal.socket = NULL; return resolv_and_connect_wout_SSL(pb); } return pbpal_connect_failed; } PUBNUB_LOG_INFO("pb=%p: SSL session reused: %s\n", pb, SSL_session_reused(ssl) ? "yes" : "no"); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); } pb->pal.session = SSL_get1_session(ssl); if (0 == pb->pal.ip_timeout) { pb->pal.ip_timeout = SSL_SESSION_get_time(pb->pal.session) + SSL_SESSION_get_timeout(pb->pal.session); memcpy(pb->pal.ip, BIO_get_conn_ip(pb->pal.socket), 4); } PUBNUB_LOG_TRACE("pb=%p: SSL connected to IP: %d.%d.%d.%d\n", pb, pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); return pbpal_connect_success; }
void pbproxy_handle_http_header(pubnub_t* p, char const* header) { char scheme_basic[] = "Basic"; char scheme_digest[] = "Digest"; char scheme_NTLM[] = "NTLM"; char proxy_auth[] = "Proxy-Authenticate: "; char const* contents; PUBNUB_ASSERT_OPT(p != NULL); PUBNUB_ASSERT_OPT(header != NULL); switch (header[0]) { case ' ': case '\t': /* Though this is not very nice, we only support multi-line headers for Digest proxy. In practice, Basic and NTLM never use multi-line headers. */ if (p->proxy_auth_scheme != pbhtauDigest) { return; } pbhttp_digest_parse_header(&p->digest_context, header + 1); return; default: if (strncmp(header, proxy_auth, sizeof proxy_auth - 1) != 0) { return; } break; } contents = header + sizeof proxy_auth - 1; PUBNUB_LOG_TRACE("pbproxy_handle_http_header(header='%s', contents='%s')\n", header, contents); if (0 == strncmp(contents, scheme_basic, sizeof scheme_basic - 1)) { /* We ignore the "realm" for now */ PUBNUB_LOG_TRACE("pbproxy_handle_http_header() Basic authentication\n"); p->proxy_auth_scheme = pbhtauBasic; p->proxy_authorization_sent = false; } else if (0 == strncmp(contents, scheme_digest, sizeof scheme_digest - 1)) { /* We ignore the "realm" for now */ PUBNUB_LOG_TRACE( "pbproxy_handle_http_header() Digest authentication\n"); p->proxy_auth_scheme = pbhtauDigest; pbhttp_digest_init(&p->digest_context); pbhttp_digest_parse_header(&p->digest_context, contents + sizeof scheme_digest); p->proxy_authorization_sent = false; } else if (0 == strncmp(contents, scheme_NTLM, sizeof scheme_NTLM - 1)) { if (pbhtauNTLM != p->proxy_auth_scheme) { pbntlm_core_init(p); p->proxy_auth_scheme = pbhtauNTLM; p->proxy_authorization_sent = false; } else { char const* base64_msg = contents + sizeof scheme_NTLM; pbntlm_core_handle(p, base64_msg, strcspn(base64_msg, " \r\n")); } } else { PUBNUB_LOG_ERROR("Proxy Authentication '%s' not supported\n", contents); p->proxy_auth_scheme = pbhtauNone; } }
int pbproxy_http_header_to_send(pubnub_t *p, char* header, size_t n) { PUBNUB_ASSERT_OPT(p != NULL); PUBNUB_ASSERT_OPT(header != NULL); switch (p->proxy_auth_scheme) { case pbhtauBasic: { int i; char prefix[] = "Proxy-Authorization: Basic "; char response[128]; pubnub_bymebl_t data = { (uint8_t*)response, 0 }; PUBNUB_ASSERT_OPT(n > sizeof prefix); memcpy(header, prefix, sizeof prefix); n -= sizeof prefix; data.size = snprintf(response, sizeof response, "%s:%s", figure_out_username(p), figure_out_password(p)); PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): Basic header (before Base64): '%s'\n", response); i = pbbase64_encode_std(data, header + sizeof prefix - 1, &n); if (0 == i) { PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): Basic header (after Base64): '%s'\n", header); p->proxy_authorization_sent = true; } else { PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): Basic Failed Base64 encoding of header\n"); } return i; } case pbhtauNTLM: { char prefix[] = "Proxy-Authorization: NTLM "; uint8_t response[PUBNUB_NTLM_MAX_TOKEN]; pubnub_bymebl_t data = { response, sizeof response }; int i = pbntlm_core_prep_msg_to_send(p, &data); if (i != 0) { PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): NTLM failed preparing message to send'\n"); return -1; } if (0 == data.size) { return -1; } PUBNUB_ASSERT_OPT(n > sizeof prefix); memcpy(header, prefix, sizeof prefix); n -= sizeof prefix; i = pbbase64_encode_std(data, header + sizeof prefix - 1, &n); if (0 == i) { PUBNUB_LOG_TRACE("pbproxy_http_header_to_send(): NTLM header (after Base64): '%s'\n", header); } else { PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): NTLM Failed Base64 encoding of header\n"); } return i; } case pbhtauNone: return -1; default: PUBNUB_LOG_ERROR("pbproxy_http_header_to_send(): Proxy auth scheme %d not supported\n", p->proxy_auth_scheme); return -1; } }