static void _log_open_tag(xmpp_conn_t *conn, char **attrs) { char buf[4096]; size_t pos; int len; int i; if (!attrs) return; pos = 0; len = xmpp_snprintf(buf, 4096, "<stream:stream"); if (len < 0) return; pos += len; for (i = 0; attrs[i]; i += 2) { len = xmpp_snprintf(&buf[pos], 4096 - pos, " %s='%s'", attrs[i], attrs[i+1]); if (len < 0) return; pos += len; } len = xmpp_snprintf(&buf[pos], 4096 - pos, ">"); if (len < 0) return; xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf); }
/* Will compute SHA1 and authenticate the component to the server */ int _handle_component_auth(xmpp_conn_t * const conn) { uint8_t md_value[SHA1_DIGEST_SIZE]; SHA1_CTX mdctx; char *digest; size_t i; if (conn->stream_id == NULL) { xmpp_error(conn->ctx, "auth", "Received no stream id from the server."); return XMPP_EINT; } /* Feed the session id and passphrase to the algorithm. * We need to compute SHA1(session_id + passphrase) */ crypto_SHA1_Init(&mdctx); crypto_SHA1_Update(&mdctx, (uint8_t*)conn->stream_id, strlen(conn->stream_id)); crypto_SHA1_Update(&mdctx, (uint8_t*)conn->pass, strlen(conn->pass)); crypto_SHA1_Final(&mdctx, md_value); digest = xmpp_alloc(conn->ctx, 2*sizeof(md_value)+1); if (digest) { /* convert the digest into string representation */ for (i = 0; i < sizeof(md_value); i++) xmpp_snprintf(digest+i*2, 3, "%02x", md_value[i]); digest[2*sizeof(md_value)] = '\0'; xmpp_debug(conn->ctx, "auth", "Digest: %s, len: %d", digest, strlen(digest)); /* Send the digest to the server */ xmpp_send_raw_string(conn, "<handshake xmlns='%s'>%s</handshake>", XMPP_NS_COMPONENT, digest); xmpp_debug(conn->ctx, "auth", "Sent component handshake to the server."); xmpp_free(conn->ctx, digest); } else { xmpp_debug(conn->ctx, "auth", "Couldn't allocate memory for component "\ "handshake digest."); return XMPP_EMEM; } return 0; }
static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn) { size_t message_len; char *node; char *message; char nonce[32]; node = xmpp_jid_node(conn->ctx, conn->jid); if (!node) { return NULL; } xmpp_rand_nonce(conn->ctx, nonce, sizeof(nonce)); message_len = strlen(node) + strlen(nonce) + 8 + 1; message = xmpp_alloc(conn->ctx, message_len); if (message) { xmpp_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce); } xmpp_free(conn->ctx, node); return message; }
/* always returns number of bytes written or that would have been * written if the buffer was large enough * return values < 0 indicate some error occurred, * and return values > buflen indicate buffer was not large enough */ static int _render_stanza_recursive(xmpp_stanza_t *stanza, char * const buf, size_t const buflen) { char *ptr = buf; size_t left = buflen; int ret, written; xmpp_stanza_t *child; hash_iterator_t *iter; const char *key; char *tmp; written = 0; if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT) { if (!stanza->data) return XMPP_EINVOP; tmp = _escape_xml(stanza->ctx, stanza->data); if (tmp == NULL) return XMPP_EMEM; ret = xmpp_snprintf(ptr, left, "%s", tmp); xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ if (!stanza->data) return XMPP_EINVOP; /* write beginning of tag and attributes */ ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter))) { if (!strcmp(key, "xmlns")) { /* don't output namespace if parent stanza is the same */ if (stanza->parent && stanza->parent->attributes && hash_get(stanza->parent->attributes, key) && !strcmp((char*)hash_get(stanza->attributes, key), (char*)hash_get(stanza->parent->attributes, key))) continue; /* or if this is the stream namespace */ if (!stanza->parent && !strcmp((char*)hash_get(stanza->attributes, key), XMPP_NS_CLIENT)) continue; } tmp = _escape_xml(stanza->ctx, (char *)hash_get(stanza->attributes, key)); if (tmp == NULL) return XMPP_EMEM; ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } hash_iter_release(iter); } if (!stanza->children) { /* write end if singleton tag */ ret = xmpp_snprintf(ptr, left, "/>"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* this stanza has child stanzas */ /* write end of start tag */ ret = xmpp_snprintf(ptr, left, ">"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); /* iterate and recurse over child stanzas */ child = stanza->children; while (child) { ret = _render_stanza_recursive(child, ptr, left); if (ret < 0) return ret; _render_update(&written, buflen, ret, &left, &ptr); child = child->next; } /* write end tag */ ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } } return written; }
/* always returns number of bytes written or that would have been * written if the buffer was large enough * return values < 0 indicate some error occured, * and return values > buflen indicate buffer was not large enough */ static int _render_stanza_recursive(xmpp_stanza_t *stanza, char * const buf, size_t const buflen) { char *ptr = buf; size_t left = buflen; int ret, written; xmpp_stanza_t *child; hash_iterator_t *iter; const char *key; assert(stanza); written = 0; if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT) { if (!stanza->data) return XMPP_EINVOP; assert(stanza->data); ret = xmpp_snprintf(ptr, left, "%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ if (!stanza->data) return XMPP_EINVOP; /* write begining of tag and attributes */ assert(stanza->data); ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter))) { assert(hash_get(stanza->attributes, key)); ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, (char *)hash_get(stanza->attributes, key)); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } hash_iter_release(iter); } if (!stanza->children) { /* write end if singleton tag */ ret = xmpp_snprintf(ptr, left, "/>"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* this stanza has child stanzas */ /* write end of start tag */ ret = xmpp_snprintf(ptr, left, ">"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); /* iterate and recurse over child stanzas */ child = stanza->children; while (child) { ret = _render_stanza_recursive(child, ptr, left); if (ret < 0) return ret; _render_update(&written, buflen, ret, &left, &ptr); child = child->next; } /* write end tag */ assert(stanza->data); ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } } return written; }
/** 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; }