/** Send a raw string to the XMPP server. * This function is a convenience function to send raw string data to the * XMPP server. It is used by Strophe to send short messages instead of * building up an XML stanza with DOM methods. This should be used with care * as it does not validate the data; invalid data may result in immediate * stream termination by the XMPP server. * * @param conn a Strophe connection object * @param fmt a printf-style format string followed by a variable list of * arguments to format */ void xmpp_send_raw_string(xmpp_conn_t * const conn, const char * const fmt, ...) { va_list ap; size_t len; char buf[1024]; /* small buffer for common case */ char *bigbuf; va_start(ap, fmt); len = xmpp_vsnprintf(buf, 1024, fmt, ap); va_end(ap); if (len >= 1024) { /* we need more space for this data, so we allocate a big * enough buffer and print to that */ len++; /* account for trailing \0 */ bigbuf = xmpp_alloc(conn->ctx, len); if (!bigbuf) { xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string"); return; } va_start(ap, fmt); xmpp_vsnprintf(bigbuf, len, fmt, ap); va_end(ap); xmpp_debug(conn->ctx, "conn", "SENT: %s", bigbuf); /* len - 1 so we don't send trailing \0 */ xmpp_send_raw(conn, bigbuf, len - 1); xmpp_free(conn->ctx, bigbuf); } else { xmpp_debug(conn->ctx, "conn", "SENT: %s", buf); xmpp_send_raw(conn, buf, len); } }
/** Create a new Strophe connection object. * * @param ctx a Strophe context object * * @return a Strophe connection object or NULL on an error * * @ingroup Connections */ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx) { xmpp_conn_t *conn = NULL; xmpp_connlist_t *tail, *item; if (ctx == NULL) return NULL; conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t)); if (conn != NULL) { conn->ctx = ctx; conn->type = XMPP_UNKNOWN; conn->state = XMPP_STATE_DISCONNECTED; conn->sock = -1; conn->tls = NULL; conn->timeout_stamp = 0; conn->error = 0; conn->stream_error = NULL; /* default send parameters */ conn->blocking_send = 0; conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX; conn->send_queue_len = 0; conn->send_queue_head = NULL; conn->send_queue_tail = NULL; /* default timeouts */ conn->connect_timeout = CONNECT_TIMEOUT; conn->lang = xmpp_strdup(conn->ctx, "en"); if (!conn->lang) { xmpp_free(conn->ctx, conn); return NULL; } conn->domain = NULL; conn->jid = NULL; conn->pass = NULL; conn->stream_id = NULL; conn->bound_jid = NULL; conn->tls_support = 0; conn->tls_disabled = 0; conn->tls_failed = 0; conn->sasl_support = 0; conn->secured = 0; conn->bind_required = 0; conn->session_required = 0; conn->parser = parser_new(conn->ctx, _handle_stream_start, _handle_stream_end, _handle_stream_stanza, conn); conn->reset_parser = 0; conn_prepare_reset(conn, auth_handle_open); conn->authenticated = 0; conn->conn_handler = NULL; conn->userdata = NULL; conn->timed_handlers = NULL; /* we own (and will free) the hash values */ conn->id_handlers = hash_new(conn->ctx, 32, NULL); conn->handlers = NULL; /* give the caller a reference to connection */ conn->ref = 1; /* add connection to ctx->connlist */ tail = conn->ctx->connlist; while (tail && tail->next) tail = tail->next; item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t)); if (!item) { xmpp_error(conn->ctx, "xmpp", "failed to allocate memory"); xmpp_free(conn->ctx, conn->lang); parser_free(conn->parser); xmpp_free(conn->ctx, conn); conn = NULL; } else { item->conn = conn; item->next = NULL; if (tail) tail->next = item; else conn->ctx->connlist = item; } } return conn; }
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) { tls_t *tls; PSecurityFunctionTable (*pInitSecurityInterface)(void); SCHANNEL_CRED scred; int ret; ALG_ID algs[1]; SecPkgCred_SupportedAlgs spc_sa; SecPkgCred_CipherStrengths spc_cs; SecPkgCred_SupportedProtocols spc_sp; OSVERSIONINFO osvi; memset(&osvi, 0, sizeof(osvi)); osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); /* no TLS support on win9x/me, despite what anyone says */ if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { return NULL; } tls = xmpp_alloc(ctx, sizeof(*tls)); if (!tls) { return NULL; } memset(tls, 0, sizeof(*tls)); tls->ctx = ctx; tls->sock = sock; if (!(tls->hsec32 = LoadLibrary ("secur32.dll"))) { tls_free(tls); return NULL; } if (!(pInitSecurityInterface = (void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) { tls_free(tls); return NULL; } tls->sft = pInitSecurityInterface(); if (!tls->sft) { tls_free(tls); return NULL; } ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi)); if (ret != SEC_E_OK) { tls_free(tls); return NULL; } xmpp_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success"); memset(&scred, 0, sizeof(scred)); scred.dwVersion = SCHANNEL_CRED_VERSION; /*scred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;*/ /* Remote server closes connection with forced RC4. The below lines are commented to leave default system configuration */ #if 0 /* Something down the line doesn't like AES, so force it to RC4 */ algs[0] = CALG_RC4; scred.cSupportedAlgs = 1; scred.palgSupportedAlgs = algs; #else (void)algs; #endif ret = tls->sft->AcquireCredentialsHandleA(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL, &(tls->hcred), NULL); if (ret != SEC_E_OK) { tls_free(tls); return NULL; } xmpp_debug(ctx, "TLSS", "AcquireCredentialsHandle() success"); tls->init = 1; /* This bunch of queries should trip up wine until someone fixes * schannel support there */ ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa); if (ret != SEC_E_OK) { tls_free(tls); return NULL; } ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs); if (ret != SEC_E_OK) { tls_free(tls); return NULL; } ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp); if (ret != SEC_E_OK) { tls_free(tls); return NULL; } return tls; }
int tls_start(tls_t *tls) { ULONG ctxtreq = 0, ctxtattr = 0; SecBufferDesc sbdin, sbdout; SecBuffer sbin[2], sbout[1]; SECURITY_STATUS ret; int sent; char *name = NULL; /* search the ctx's conns for our sock, and use the domain there as our * name */ { xmpp_connlist_t *listentry = tls->ctx->connlist; while (listentry) { xmpp_conn_t *conn = listentry->conn; if (conn->sock == tls->sock) { name = strdup(conn->domain); listentry = NULL; } else { listentry = listentry->next; } } } ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY; memset(&(sbout[0]), 0, sizeof(sbout[0])); sbout[0].BufferType = SECBUFFER_TOKEN; memset(&sbdout, 0, sizeof(sbdout)); sbdout.ulVersion = SECBUFFER_VERSION; sbdout.cBuffers = 1; sbdout.pBuffers = sbout; memset(&(sbin[0]), 0, sizeof(sbin[0])); sbin[0].BufferType = SECBUFFER_TOKEN; sbin[0].pvBuffer = xmpp_alloc(tls->ctx, tls->spi->cbMaxToken); sbin[0].cbBuffer = tls->spi->cbMaxToken; memset(&(sbin[1]), 0, sizeof(sbin[1])); sbin[1].BufferType = SECBUFFER_EMPTY; memset(&sbdin, 0, sizeof(sbdin)); sbdin.ulVersion = SECBUFFER_VERSION; sbdin.cBuffers = 2; sbdin.pBuffers = sbin; ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), NULL, name, ctxtreq, 0, 0, NULL, 0, &(tls->hctxt), &sbdout, &ctxtattr, NULL); while (ret == SEC_I_CONTINUE_NEEDED || ret == SEC_I_INCOMPLETE_CREDENTIALS) { unsigned char *p = sbin[0].pvBuffer; int len = 0, inbytes = 0; if (sbdout.pBuffers[0].cbBuffer) { unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer; unsigned int writelen = sbdout.pBuffers[0].cbBuffer; sent = sock_write(tls->sock, writebuff, writelen); if (sent == -1) { tls->lasterror = sock_error(); } else { writebuff += sent; writelen -= sent; } tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer); sbdout.pBuffers[0].pvBuffer = NULL; sbdout.pBuffers[0].cbBuffer = 0; } /* poll for a bit until the remote server stops sending data, ie it * finishes sending the token */ inbytes = 1; { fd_set fds; struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(tls->sock, &fds); select(tls->sock, &fds, NULL, NULL, &tv); } while (inbytes > 0) { fd_set fds; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; FD_ZERO(&fds); FD_SET(tls->sock, &fds); select(tls->sock, &fds, NULL, NULL, &tv); inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len); if (inbytes > 0) { len += inbytes; p += inbytes; } else { tls->lasterror = sock_error(); } } sbin[0].cbBuffer = len; ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), &(tls->hctxt), name, ctxtreq, 0, 0, &sbdin, 0, &(tls->hctxt), &sbdout, &ctxtattr, NULL); } if (ret == SEC_E_OK) { if (sbdout.pBuffers[0].cbBuffer) { unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer; unsigned int writelen = sbdout.pBuffers[0].cbBuffer; sent = sock_write(tls->sock, writebuff, writelen); if (sent == -1) { tls->lasterror = sock_error(); } else { writebuff += sent; writelen -= sent; } tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer); sbdout.pBuffers[0].pvBuffer = NULL; sbdout.pBuffers[0].cbBuffer = 0; } } xmpp_free(tls->ctx, sbin[0].pvBuffer); if (ret != SEC_E_OK) { tls->lasterror = ret; xmpp_error(tls->ctx, "TLSS", "Schannel error 0x%lx", (unsigned long)ret); return 0; } tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES, &(tls->spcss)); tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage + tls->spcss.cbTrailer; tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen); tls->recvbufferpos = 0; tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage + tls->spcss.cbTrailer; tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen); tls->sendbufferpos = 0; tls->sendbufferlen = 0; tls->readybuffer = xmpp_alloc(tls->ctx, tls->spcss.cbMaximumMessage); tls->readybufferpos = 0; tls->readybufferlen = 0; return 1; }
/* stream:error handler */ static int _handle_error(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *child; char *name; /* free old stream error if it's still there */ if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(conn->ctx, conn->stream_error->text); xmpp_free(conn->ctx, conn->stream_error); } /* create stream error structure */ conn->stream_error = (xmpp_stream_error_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_stream_error_t)); conn->stream_error->text = NULL; conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION; if (conn->stream_error) { child = xmpp_stanza_get_children(stanza); do { char *ns = NULL; if (child) { ns = xmpp_stanza_get_ns(child); } if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) { name = xmpp_stanza_get_name(child); if (strcmp(name, "text") == 0) { if (conn->stream_error->text) xmpp_free(conn->ctx, conn->stream_error->text); conn->stream_error->text = xmpp_stanza_get_text(child); } else if (strcmp(name, "bad-format") == 0) conn->stream_error->type = XMPP_SE_BAD_FORMAT; else if (strcmp(name, "bad-namespace-prefix") == 0) conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX; else if (strcmp(name, "conflict") == 0) conn->stream_error->type = XMPP_SE_CONFLICT; else if (strcmp(name, "connection-timeout") == 0) conn->stream_error->type = XMPP_SE_CONN_TIMEOUT; else if (strcmp(name, "host-gone") == 0) conn->stream_error->type = XMPP_SE_HOST_GONE; else if (strcmp(name, "host-unknown") == 0) conn->stream_error->type = XMPP_SE_HOST_UNKNOWN; else if (strcmp(name, "improper-addressing") == 0) conn->stream_error->type = XMPP_SE_IMPROPER_ADDR; else if (strcmp(name, "internal-server-error") == 0) conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR; else if (strcmp(name, "invalid-from") == 0) conn->stream_error->type = XMPP_SE_INVALID_FROM; else if (strcmp(name, "invalid-id") == 0) conn->stream_error->type = XMPP_SE_INVALID_ID; else if (strcmp(name, "invalid-namespace") == 0) conn->stream_error->type = XMPP_SE_INVALID_NS; else if (strcmp(name, "invalid-xml") == 0) conn->stream_error->type = XMPP_SE_INVALID_XML; else if (strcmp(name, "not-authorized") == 0) conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED; else if (strcmp(name, "policy-violation") == 0) conn->stream_error->type = XMPP_SE_POLICY_VIOLATION; else if (strcmp(name, "remote-connection-failed") == 0) conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED; else if (strcmp(name, "resource-constraint") == 0) conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT; else if (strcmp(name, "restricted-xml") == 0) conn->stream_error->type = XMPP_SE_RESTRICTED_XML; else if (strcmp(name, "see-other-host") == 0) conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST; else if (strcmp(name, "system-shutdown") == 0) conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN; else if (strcmp(name, "undefined-condition") == 0) conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION; else if (strcmp(name, "unsupported-encoding") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING; else if (strcmp(name, "unsupported-stanza-type") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE; else if (strcmp(name, "unsupported-version") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION; else if (strcmp(name, "xml-not-well-formed") == 0) conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED; } } while ((child = xmpp_stanza_get_next(child))); conn->stream_error->stanza = xmpp_stanza_clone(stanza); } return 1; }
/** generate auth response string for the SASL SCRAM-SHA-1 mechanism */ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge, const char *first_bare, const char *jid, const char *password) { uint8_t key[SHA1_DIGEST_SIZE]; uint8_t sign[SHA1_DIGEST_SIZE]; char *r = NULL; char *s = NULL; char *i = NULL; unsigned char *sval; size_t sval_len; long ival; char *tmp; char *ptr; char *saveptr = NULL; char *response; char *auth; char *response_b64; char *sign_b64; char *result = NULL; size_t response_len; size_t auth_len; int j; tmp = xmpp_strdup(ctx, challenge); if (!tmp) { return NULL; } ptr = strtok_r(tmp, ",", &saveptr); while (ptr) { if (strncmp(ptr, "r=", 2) == 0) { r = ptr; } else if (strncmp(ptr, "s=", 2) == 0) { s = ptr + 2; } else if (strncmp(ptr, "i=", 2) == 0) { i = ptr + 2; } ptr = strtok_r(NULL, ",", &saveptr); } if (!r || !s || !i) { goto out; } xmpp_base64_decode_bin(ctx, s, strlen(s), &sval, &sval_len); if (!sval) { goto out; } ival = strtol(i, &saveptr, 10); auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge); auth = xmpp_alloc(ctx, auth_len); if (!auth) { goto out_sval; } response_len = 39 + strlen(r); response = xmpp_alloc(ctx, response_len); if (!response) { goto out_auth; } xmpp_snprintf(response, response_len, "c=biws,%s", r); xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge, response); SCRAM_SHA1_ClientKey((uint8_t *)password, strlen(password), (uint8_t *)sval, sval_len, (uint32_t)ival, key); SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); for (j = 0; j < SHA1_DIGEST_SIZE; j++) { sign[j] ^= key[j]; } sign_b64 = xmpp_base64_encode(ctx, sign, sizeof(sign)); if (!sign_b64) { goto out_response; } if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) { xmpp_free(ctx, sign_b64); goto out_response; } strcat(response, ",p="); strcat(response, sign_b64); xmpp_free(ctx, sign_b64); response_b64 = xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response)); if (!response_b64) { goto out_response; } result = response_b64; out_response: xmpp_free(ctx, response); out_auth: xmpp_free(ctx, auth); out_sval: xmpp_free(ctx, sval); out: xmpp_free(ctx, tmp); return result; }
/** generate auth response string for the SASL DIGEST-MD5 mechanism */ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, const char *jid, const char *password) { hash_t *table; char *result = NULL; char *node, *domain, *realm; char *value; char *response; int rlen; struct MD5Context MD5; unsigned char digest[16], HA1[16], HA2[16]; char hex[32]; char cnonce[13]; /* our digest response is Hex( KD( HEX(MD5(A1)), nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2)) )) where KD(k, s) = MD5(k ':' s), A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce A2 = "AUTHENTICATE" ':' "xmpp/" domain If there is an authzid it is ':'-appended to A1 */ /* parse the challenge */ table = _parse_digest_challenge(ctx, challenge); if (table == NULL) { xmpp_error(ctx, "SASL", "couldn't parse digest challenge"); return NULL; } node = xmpp_jid_node(ctx, jid); domain = xmpp_jid_domain(ctx, jid); /* generate default realm of domain if one didn't come from the server */ realm = hash_get(table, "realm"); if (realm == NULL || strlen(realm) == 0) { hash_add(table, "realm", xmpp_strdup(ctx, domain)); realm = hash_get(table, "realm"); } /* add our response fields */ hash_add(table, "username", xmpp_strdup(ctx, node)); xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce)); hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce)); hash_add(table, "nc", xmpp_strdup(ctx, "00000001")); hash_add(table, "qop", xmpp_strdup(ctx, "auth")); value = xmpp_alloc(ctx, 5 + strlen(domain) + 1); memcpy(value, "xmpp/", 5); memcpy(value+5, domain, strlen(domain)); value[5+strlen(domain)] = '\0'; hash_add(table, "digest-uri", value); /* generate response */ /* construct MD5(node : realm : password) */ MD5Init(&MD5); MD5Update(&MD5, (unsigned char *)node, strlen(node)); MD5Update(&MD5, (unsigned char *)":", 1); MD5Update(&MD5, (unsigned char *)realm, strlen(realm)); MD5Update(&MD5, (unsigned char *)":", 1); MD5Update(&MD5, (unsigned char *)password, strlen(password)); MD5Final(digest, &MD5); /* digest now contains the first field of A1 */ MD5Init(&MD5); MD5Update(&MD5, digest, 16); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "cnonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Final(digest, &MD5); /* now digest is MD5(A1) */ memcpy(HA1, digest, 16); /* construct MD5(A2) */ MD5Init(&MD5); MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13); value = hash_get(table, "digest-uri"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); if (strcmp(hash_get(table, "qop"), "auth") != 0) { MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000", 33); } MD5Final(digest, &MD5); memcpy(HA2, digest, 16); /* construct response */ MD5Init(&MD5); _digest_to_hex((char *)HA1, hex); MD5Update(&MD5, (unsigned char *)hex, 32); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "nc"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "cnonce"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); value = hash_get(table, "qop"); MD5Update(&MD5, (unsigned char *)value, strlen(value)); MD5Update(&MD5, (unsigned char *)":", 1); _digest_to_hex((char *)HA2, hex); MD5Update(&MD5, (unsigned char *)hex, 32); MD5Final(digest, &MD5); response = xmpp_alloc(ctx, 32+1); _digest_to_hex((char *)digest, hex); memcpy(response, hex, 32); response[32] = '\0'; hash_add(table, "response", response); /* construct reply */ result = NULL; rlen = 0; result = _add_key(ctx, table, "username", result, &rlen, 1); result = _add_key(ctx, table, "realm", result, &rlen, 1); result = _add_key(ctx, table, "nonce", result, &rlen, 1); result = _add_key(ctx, table, "cnonce", result, &rlen, 1); result = _add_key(ctx, table, "nc", result, &rlen, 0); result = _add_key(ctx, table, "qop", result, &rlen, 0); result = _add_key(ctx, table, "digest-uri", result, &rlen, 1); result = _add_key(ctx, table, "response", result, &rlen, 0); result = _add_key(ctx, table, "charset", result, &rlen, 0); xmpp_free(ctx, node); xmpp_free(ctx, domain); hash_release(table); /* also frees value strings */ /* reuse response for the base64 encode of our result */ response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result)); xmpp_free(ctx, result); return response; }
/* add a stanza handler */ static void _handler_add(xmpp_conn_t * const conn, xmpp_handler handler, const char * const ns, const char * const name, const char * const type, void * const userdata, int user_handler) { xmpp_handlist_t *item, *tail; /* check if handler already in list */ for (item = conn->handlers; item; item = item->next) { if (item->handler == (void *)handler) break; } if (item) return; /* build new item */ item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); if (!item) return; item->user_handler = user_handler; item->handler = (void *)handler; item->userdata = userdata; item->enabled = 0; item->next = NULL; if (ns) { item->ns = xmpp_strdup(conn->ctx, ns); if (!item->ns) { xmpp_free(conn->ctx, item); return; } } else item->ns = NULL; if (name) { item->name = xmpp_strdup(conn->ctx, name); if (!item->name) { if (item->ns) xmpp_free(conn->ctx, item->ns); xmpp_free(conn->ctx, item); return; } } else item->name = NULL; if (type) { item->type = xmpp_strdup(conn->ctx, type); if (!item->type) { if (item->ns) xmpp_free(conn->ctx, item->ns); if (item->name) xmpp_free(conn->ctx, item->name); xmpp_free(conn->ctx, item); } } else item->type = NULL; /* append to list */ if (!conn->handlers) conn->handlers = item; else { tail = conn->handlers; while (tail->next) tail = tail->next; tail->next = item; } }
tls_t *tls_new(xmpp_conn_t *conn) { tls_t *tls = xmpp_alloc(conn->ctx, sizeof(*tls)); int mode; if (tls) { int ret; memset(tls, 0, sizeof(*tls)); tls->ctx = conn->ctx; tls->sock = conn->sock; tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (tls->ssl_ctx == NULL) goto err; /* Enable bug workarounds. */ SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_ALL); /* Disable insecure SSL/TLS versions. */ SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv2); /* DROWN */ SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv3); /* POODLE */ SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_TLSv1); /* BEAST */ SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL); SSL_CTX_set_mode(tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_default_verify_paths(tls->ssl_ctx); tls->ssl = SSL_new(tls->ssl_ctx); if (tls->ssl == NULL) goto err_free_ctx; /* Trust server's certificate when user sets the flag explicitly. */ mode = conn->tls_trust ? SSL_VERIFY_NONE : SSL_VERIFY_PEER; SSL_set_verify(tls->ssl, mode, 0); #if OPENSSL_VERSION_NUMBER >= 0x10002000L /* Hostname verification is supported in OpenSSL 1.0.2 and newer. */ X509_VERIFY_PARAM *param = SSL_get0_param(tls->ssl); /* * Allow only complete wildcards. RFC 6125 discourages wildcard usage * completely, and lists internationalized domain names as a reason * against partial wildcards. * See https://tools.ietf.org/html/rfc6125#section-7.2 for more information. */ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); X509_VERIFY_PARAM_set1_host(param, conn->domain, 0); #endif ret = SSL_set_fd(tls->ssl, conn->sock); if (ret <= 0) goto err_free_ssl; } return tls; err_free_ssl: SSL_free(tls->ssl); err_free_ctx: SSL_CTX_free(tls->ssl_ctx); err: xmpp_free(conn->ctx, tls); _tls_log_error(conn->ctx); return NULL; }
/** Create a new Strophe connection object. * * @param ctx a Strophe context object * * @return a Strophe connection object or NULL on an error * * @ingroup Connections */ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx) { xmpp_conn_t *conn; list_t *item; if (!ctx) return NULL; conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t)); if (!conn) return NULL; conn->ctx = ctx; conn->type = XMPP_UNKNOWN; conn->state = XMPP_STATE_DISCONNECTED; conn->sock = -1; conn->tls = NULL; conn->timeout_stamp = 0; conn->error = 0; conn->stream_error = NULL; /* default send parameters */ conn->blocking_send = 0; conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX; conn->send_queue = list_init(ctx); if (!conn->send_queue) goto out_free_conn; /* default timeouts */ conn->connect_timeout = CONNECT_TIMEOUT; conn->lang = xmpp_strdup(ctx, "en"); if (!conn->lang) goto out_free_send_queue; conn->domain = NULL; conn->jid = NULL; conn->pass = NULL; conn->stream_id = NULL; conn->bound_jid = NULL; conn->tls_support = 0; conn->tls_disabled = 0; conn->tls_failed = 0; conn->sasl_support = 0; conn->secured = 0; conn->bind_required = 0; conn->session_required = 0; conn->parser = parser_new(ctx, _handle_stream_start, _handle_stream_end, _handle_stream_stanza, conn); if (!conn->parser) goto out_free_lang; conn->reset_parser = 0; conn_prepare_reset(conn, auth_handle_open); conn->authenticated = 0; conn->conn_handler = NULL; conn->userdata = NULL; /* we own (and will free) the hash values */ conn->id_handlers = hash_new(ctx, 32, NULL); conn->timed_handlers = list_init(ctx); if (!conn->timed_handlers) goto out_free_parser; conn->handlers = list_init(ctx); if (!conn->handlers) goto out_free_timed_handlers; /* give the caller a reference to connection */ conn->ref = 1; /* add connection to ctx->connlist */ item = list_init_item(ctx); if (!item) goto out_free_handlers; else { item->data = (void *)conn; list_push(ctx->connlist, item); } return conn; out_free_handlers: list_destroy(conn->handlers); out_free_timed_handlers: list_destroy(conn->timed_handlers); out_free_parser: parser_free(conn->parser); out_free_lang: xmpp_free(ctx, conn->lang); out_free_send_queue: list_destroy(conn->send_queue); out_free_conn: xmpp_free(ctx, conn); return NULL; }