xmpp_user_t* xmpp_user_new(xmpp_conn_t * const conn, const char *jid, const char *name) { xmpp_user_t *user = xmpp_alloc(conn->ctx, sizeof(xmpp_user_t)); user->conn = conn; user->jid = xmpp_strdup(conn->ctx, jid); user->name = xmpp_strdup(conn->ctx, name); return user; }
/** Copy a stanza and its children. * This function copies a stanza along with all its children and returns * the new stanza and children with a reference count of 1. The returned * stanza will have no parent and no siblings. This function is useful * for extracting a child stanza for inclusion in another tree. * * @param stanza a Strophe stanza object * * @return a new Strophe stanza object * * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza) { xmpp_stanza_t *copy, *child, *copychild, *tail; hash_iterator_t *iter; const char *key; void *val; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; copy->type = stanza->type; if (stanza->data) { copy->data = xmpp_strdup(stanza->ctx, stanza->data); if (!copy->data) goto copy_error; } if (stanza->attributes) { copy->attributes = hash_new(stanza->ctx, 8, xmpp_free); if (!copy->attributes) goto copy_error; iter = hash_iter_new(stanza->attributes); if (!iter) { printf("DEBUG HERE\n"); goto copy_error; } while ((key = hash_iter_next(iter))) { val = xmpp_strdup(stanza->ctx, (char *)hash_get(stanza->attributes, key)); if (!val) goto copy_error; if (hash_add(copy->attributes, key, val)) goto copy_error; } hash_iter_release(iter); } tail = copy->children; for (child = stanza->children; child; child = child->next) { copychild = xmpp_stanza_copy(child); if (!copychild) goto copy_error; copychild->parent = copy; if (tail) { copychild->prev = tail; tail->next = copychild; } else copy->children = copychild; tail = copychild; } return copy; copy_error: /* release all the hitherto allocated memory */ if (copy) xmpp_stanza_release(copy); return NULL; }
/** Create a stanza object in reply to another. * This function makes a copy of a stanza object with the attribute “to” set * its original “from”. * The stanza will have a reference count of one, so the caller does not * need to clone it. * * @param stanza a Strophe stanza object * * @return a new Strophe stanza object * * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t * const stanza) { xmpp_stanza_t *copy = NULL; const char *from; int rc; from = xmpp_stanza_get_from(stanza); if (!from) goto copy_error; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; copy->type = stanza->type; if (stanza->data) { copy->data = xmpp_strdup(stanza->ctx, stanza->data); if (!copy->data) goto copy_error; } if (stanza->attributes) { if (_stanza_copy_attributes(copy, stanza) < 0) goto copy_error; } xmpp_stanza_del_attribute(copy, "to"); xmpp_stanza_del_attribute(copy, "from"); rc = xmpp_stanza_set_to(copy, from); if (rc != XMPP_EOK) goto copy_error; return copy; copy_error: if (copy) xmpp_stanza_release(copy); return NULL; }
/** Set an attribute for a stanza object. * * @param stanza a Strophe stanza object * @param key a string with the attribute name * @param value a string with the attribute value * * @return XMPP_EOK (0) on success or a number less than 0 on failure * * @ingroup Stanza */ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, const char * const key, const char * const value) { char *val; int rc; if (stanza->type != XMPP_STANZA_TAG) return XMPP_EINVOP; if (!stanza->attributes) { stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free); if (!stanza->attributes) return XMPP_EMEM; } val = xmpp_strdup(stanza->ctx, value); if (!val) { hash_release(stanza->attributes); return XMPP_EMEM; } rc = hash_add(stanza->attributes, key, val); if (rc < 0) { xmpp_free(stanza->ctx, val); return XMPP_EMEM; } return XMPP_EOK; }
/** Get the text data for a text stanza. * This function copies the text data from a stanza and returns the new * allocated string. The caller is responsible for freeing this string * with xmpp_free(). * * @param stanza a Strophe stanza object * * @return an allocated string with the text data * * @ingroup Stanza */ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza) { size_t len, clen; xmpp_stanza_t *child; char *text; if (stanza->type == XMPP_STANZA_TEXT) { if (stanza->data) return xmpp_strdup(stanza->ctx, stanza->data); else return NULL; } len = 0; for (child = stanza->children; child; child = child->next) if (child->type == XMPP_STANZA_TEXT) len += strlen(child->data); if (len == 0) return NULL; text = (char *)xmpp_alloc(stanza->ctx, len + 1); if (!text) return NULL; len = 0; for (child = stanza->children; child; child = child->next) if (child->type == XMPP_STANZA_TEXT) { clen = strlen(child->data); memcpy(&text[len], child->data, clen); len += clen; } text[len] = 0; return text; }
/** add a key, value pair to a hash table. * each key can appear only once; the value of any * identical key will be replaced */ int hash_add(hash_t *table, const char * const key, void *data) { xmpp_ctx_t *ctx = table->ctx; hashentry_t *entry = NULL; int table_index = _hash_key(table, key); /* drop existing entry, if any */ hash_drop(table, key); /* allocate and fill a new entry */ entry = xmpp_alloc(ctx, sizeof(hashentry_t)); if (!entry) return -1; entry->key = xmpp_strdup(ctx, key); if (!entry->key) { xmpp_free(ctx, entry); return -1; } entry->value = data; /* insert ourselves in the linked list */ /* TODO: this leaks duplicate keys */ entry->next = table->entries[table_index]; table->entries[table_index] = entry; table->num_keys++; return 0; }
/* * Copy the attributes of stanza src into stanza dst. Return -1 on error. */ static int _stanza_copy_attributes(xmpp_stanza_t * dst, const xmpp_stanza_t * const src) { hash_iterator_t *iter = NULL; const char *key; void *val; dst->attributes = hash_new(src->ctx, 8, xmpp_free); if (!dst->attributes) return -1; iter = hash_iter_new(src->attributes); if (!iter) goto error; while ((key = hash_iter_next(iter))) { val = xmpp_strdup(src->ctx, (char *)hash_get(src->attributes, key)); if (!val) goto error; if (hash_add(dst->attributes, key, val)) { xmpp_free(src->ctx, val); goto error; } } hash_iter_release(iter); return 0; error: if (iter != NULL) hash_iter_release(iter); hash_release(dst->attributes); return -1; }
/** Create a stanza object in reply to another. * This function makes a copy of a stanza object with the attribute “to” set * its original “from”. * The stanza will have a reference count of one, so the caller does not * need to clone it. * * @param stanza a Strophe stanza object * * @return a new Strophe stanza object * * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t * const stanza) { xmpp_stanza_t *copy; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; copy->type = stanza->type; if (stanza->data) { copy->data = xmpp_strdup(stanza->ctx, stanza->data); if (!copy->data) goto copy_error; } if (stanza->attributes) { if (_stanza_copy_attributes(copy, stanza) == -1) goto copy_error; } xmpp_stanza_set_to(copy, xmpp_stanza_get_from(stanza)); xmpp_stanza_del_attribute(copy, "from"); return copy; copy_error: if (copy) xmpp_stanza_release(copy); return NULL; }
static void _handle_stream_start(char *name, char **attrs, void * const userdata) { xmpp_conn_t *conn = (xmpp_conn_t *)userdata; char *id; if (strcmp(name, "stream:stream") != 0) { printf("name = %s\n", name); xmpp_error(conn->ctx, "conn", "Server did not open valid stream."); conn_disconnect(conn); } else { _log_open_tag(conn, attrs); if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id); id = _get_stream_attribute(attrs, "id"); if (id) conn->stream_id = xmpp_strdup(conn->ctx, id); if (!conn->stream_id) { xmpp_error(conn->ctx, "conn", "Memory allocation failed."); conn_disconnect(conn); } } /* call stream open handler */ conn->open_handler(conn); }
static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs, char ***attributes, size_t *attributes_len) { char **array = NULL; size_t nr = 0; size_t i; if (attrs) { for (; attrs[nr]; ++nr); array = xmpp_alloc(conn->ctx, sizeof(*array) * nr); for (i = 0; array && i < nr; ++i) { array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i]) : xmpp_strdup(conn->ctx, attrs[i]); if (array[i] == NULL) break; } if (!array || i < nr) { xmpp_error(conn->ctx, "xmpp", "Memory allocation error."); _conn_attributes_destroy(conn, array, i); array = NULL; nr = 0; } } *attributes = array; *attributes_len = nr; }
/** Set the name of a stanza. * * @param stanza a Strophe stanza object * @param name a string with the name of the stanza * * @return XMPP_EOK on success, a number less than 0 on failure (XMPP_EMEM, * XMPP_EINVOP) * * @ingroup Stanza */ int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char * const name) { if (stanza->type == XMPP_STANZA_TEXT) return XMPP_EINVOP; if (stanza->data) xmpp_free(stanza->ctx, stanza->data); stanza->type = XMPP_STANZA_TAG; stanza->data = xmpp_strdup(stanza->ctx, name); return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK; }
/** Set the text data for a text stanza. * This function copies the text given and sets the stanza object's text to * it. Attempting to use this function on a stanza that has a name will * fail with XMPP_EINVOP. This function takes the text as a null-terminated * string. * * @param stanza a Strophe stanza object * @param text a string with the text * * @return XMPP_EOK (0) on success or a number less than zero on failure * * @ingroup Stanza */ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char * const text) { if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; stanza->type = XMPP_STANZA_TEXT; if (stanza->data) xmpp_free(stanza->ctx, stanza->data); stanza->data = xmpp_strdup(stanza->ctx, text); return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK; }
/** Initiate a component connection to server. * This function returns immediately after starting the connection * process to the XMPP server, and notifiations of connection state changes * will be sent to the internal callback function that will set up handler * for the component handshake as defined in XEP-0114. * The domain and port to connect to must be provided in this case as the JID * provided to the call serves as component identifier to the server and is * not subject to DNS resolution. * * @param conn a Strophe connection object * @param server a string with domain to use directly as the domain can't be * extracted from the component name/JID. If this is not set, the call * will fail. * @param port an integer port number to use to connect to server expecting * an external component. If this is 0, the port 5347 will be assumed. * @param callback a xmpp_conn_handler callback function that will receive * notifications of connection status * @param userdata an opaque data pointer that will be passed to the callback * * @return 0 on success and -1 on an error * * @ingroup Connections */ int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, unsigned short port, xmpp_conn_handler callback, void * const userdata) { int connectport; if (conn->state != XMPP_STATE_DISCONNECTED) return -1; if (conn->domain != NULL) xmpp_free(conn->ctx, conn->domain); conn->type = XMPP_COMPONENT; conn->secured = 0; conn->tls_failed = 0; /* JID serves as an identificator here and will be used as "to" attribute of the stream */ conn->domain = xmpp_strdup(conn->ctx, conn->jid); /* The server domain, jid and password MUST be specified. */ if (!(server && conn->jid && conn->pass)) return -1; connectport = port ? port : _conn_default_port(conn); xmpp_debug(conn->ctx, "xmpp", "Connecting via %s", server); conn->sock = sock_connect(server, connectport); xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", server, connectport, conn->sock); if (conn->sock == -1) return -1; /* XEP-0114 does not support TLS */ conn->tls_disabled = 1; /* setup handler */ conn->conn_handler = callback; conn->userdata = userdata; conn_prepare_reset(conn, auth_handle_component_open); /* FIXME: it could happen that the connect returns immediately as * successful, though this is pretty unlikely. This would be a little * hard to fix, since we'd have to detect and fire off the callback * from within the event loop */ conn->state = XMPP_STATE_CONNECTING; conn->timeout_stamp = time_stamp(); xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", server); return 0; }
/** Create a bare JID from a JID. * * @param ctx the Strophe context object * @param jid the JID * * @return an allocated string with the bare JID or NULL on an error */ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid) { char *result; const char *c; c = strchr(jid, '/'); if (c == NULL) return xmpp_strdup(ctx, jid); result = xmpp_alloc(ctx, c-jid+1); if (result != NULL) { memcpy(result, jid, c-jid); result[c-jid] = '\0'; } return result; }
static int _conn_connect(xmpp_conn_t * const conn, const char * const domain, const char * const host, unsigned short port, xmpp_conn_type_t type, xmpp_conn_handler callback, void * const userdata) { xmpp_open_handler open_handler; if (conn->state != XMPP_STATE_DISCONNECTED) return XMPP_EINVOP; if (type != XMPP_CLIENT && type != XMPP_COMPONENT) return XMPP_EINVOP; if (host == NULL || port == 0) return XMPP_EINT; _conn_reset(conn); conn->type = type; conn->domain = xmpp_strdup(conn->ctx, domain); if (!conn->domain) return XMPP_EMEM; conn->sock = sock_connect(host, port); xmpp_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d", host, port, conn->sock); if (conn->sock == -1) return XMPP_EINT; if (conn->ka_timeout || conn->ka_interval) sock_set_keepalive(conn->sock, conn->ka_timeout, conn->ka_interval); /* setup handler */ conn->conn_handler = callback; conn->userdata = userdata; open_handler = conn->is_raw ? auth_handle_open_stub : type == XMPP_CLIENT ? auth_handle_open : auth_handle_component_open; conn_prepare_reset(conn, open_handler); /* FIXME: it could happen that the connect returns immediately as * successful, though this is pretty unlikely. This would be a little * hard to fix, since we'd have to detect and fire off the callback * from within the event loop */ conn->state = XMPP_STATE_CONNECTING; conn->timeout_stamp = time_stamp(); xmpp_debug(conn->ctx, "xmpp", "Attempting to connect to %s", host); return 0; }
/* return allocated string with the name from a delimited * namespace/name string */ static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname) { char *result = NULL; const char *c; int len; c = strchr(nsname, NAMESPACE_SEP); if (c == NULL) return xmpp_strdup(ctx, nsname); c++; len = strlen(c); result = xmpp_alloc(ctx, len + 1); if (result != NULL) { memcpy(result, c, len); result[len] = '\0'; } return result; }
static void _id_handler_add(xmpp_conn_t * const conn, xmpp_handler handler, const char * const id, void * const userdata, int user_handler) { xmpp_handlist_t *item, *tail; /* check if handler is already in the list */ item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); while (item) { if (item->handler == (void *)handler) break; item = item->next; } if (item) return; /* build new item */ item = 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; item->id = xmpp_strdup(conn->ctx, id); if (!item->id) { xmpp_free(conn->ctx, item); return; } /* put on list in hash table */ tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); if (!tail) hash_add(conn->id_handlers, id, item); else { while (tail->next) tail = tail->next; tail->next = item; } }
/** Copy a stanza and its children. * This function copies a stanza along with all its children and returns * the new stanza and children with a reference count of 1. The returned * stanza will have no parent and no siblings. This function is useful * for extracting a child stanza for inclusion in another tree. * * @param stanza a Strophe stanza object * * @return a new Strophe stanza object * * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza) { xmpp_stanza_t *copy, *child, *copychild, *tail; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; copy->type = stanza->type; if (stanza->data) { copy->data = xmpp_strdup(stanza->ctx, stanza->data); if (!copy->data) goto copy_error; } if (stanza->attributes) { if (_stanza_copy_attributes(copy, stanza) == -1) goto copy_error; } tail = copy->children; for (child = stanza->children; child; child = child->next) { copychild = xmpp_stanza_copy(child); if (!copychild) goto copy_error; copychild->parent = copy; if (tail) { copychild->prev = tail; tail->next = copychild; } else copy->children = copychild; tail = copychild; } return copy; copy_error: /* release all the hitherto allocated memory */ if (copy) xmpp_stanza_release(copy); return NULL; }
/** Set or replace attributes on a stanza. * This function replaces all previous attributes (if any) with the * attributes given. It is used primarily by the XML parser during * stanza creation. All strings in the array are copied before placing them * inside the stanza object. * * @param stanza a Strophe stanza object * @param attr an array of strings with the attributes in the following * format: attr[i] = attribute name, attr[i+1] = attribute value * * @return XMPP_EOK on success, a number less than 0 on failure (XMPP_EMEM, * XMPP_EINVOP) * * @ingroup Stanza */ int xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza, const char * const * const attr) { int i; char *value; if (stanza->attributes != NULL) hash_release(stanza->attributes); stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free); if (!stanza->attributes) return XMPP_EMEM; for (i = 0; attr[i]; i += 2) { value = xmpp_strdup(stanza->ctx, attr[i + 1]); if (!value) { /* FIXME: memory allocation error */ continue; } hash_add(stanza->attributes, attr[i], value); } return XMPP_EOK; }
/** Initiate a component connection to the XMPP server. * This function returns immediately after starting the connection * process to the XMPP server, and notifiations of connection state changes * will be sent to the callback function. The domain and port specify the * server to connect to. Strophe will then attempt to bind to the domain specified * previously with xmpp_conn_set_jid * * @param conn a Strophe connection object * @param server a string with domain of the server to connect to, commonly "localhost" * @param port an integer port number to connect to * @param callback a xmpp_conn_handler callback function that will receive * notifications of connection status * @param userdata an opaque data pointer that will be passed to the callback * * @return 0 on success and -1 on an error * * @ingroup Connections */ int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, unsigned short port, xmpp_conn_handler callback, void * const userdata) { char connectdomain[2048]; int connectport; char *domain; conn->type = XMPP_COMPONENT; conn->domain = xmpp_strdup(conn->ctx, server); if (!conn->domain) return -1; domain = conn->domain; strcpy(connectdomain, domain); connectport = port ? port : 5222; conn->sock = sock_connect(connectdomain, connectport); if (conn->sock == -1) return -1; /* setup handler */ conn->conn_handler = callback; conn->userdata = userdata; /* FIXME: it could happen that the connect returns immediately as * successful, though this is pretty unlikely. This would be a little * hard to fix, since we'd have to detect and fire off the callback * from within the event loop */ conn->state = XMPP_STATE_CONNECTING; conn->timeout_stamp = time_stamp(); xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", connectdomain); return 0; }
/** 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; }
int tls_start(tls_t *tls) { xmpp_ctx_t *ctx = tls->ctx; 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 */ { list_t *listentry = list_get_first(ctx->connlist); while (listentry) { xmpp_conn_t *conn = (xmpp_conn_t *)listentry->data; if (conn->sock == tls->sock) { name = xmpp_strdup(ctx, conn->domain); listentry = NULL; } else { listentry = list_get_next(tls->ctx->connlist, listentry); } } } 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(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; } } /* free resources */ xmpp_free(ctx, sbin[0].pvBuffer); if (name != NULL) { xmpp_free(ctx, name); } if (ret != SEC_E_OK) { tls->lasterror = 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(ctx, tls->recvbuffermaxlen); tls->recvbufferpos = 0; tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage + tls->spcss.cbTrailer; tls->sendbuffer = xmpp_alloc(ctx, tls->sendbuffermaxlen); tls->sendbufferpos = 0; tls->sendbufferlen = 0; tls->readybuffer = xmpp_alloc(ctx, tls->spcss.cbMaximumMessage); tls->readybufferpos = 0; tls->readybufferlen = 0; return 1; }
/** 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; }
/** Set the password used to authenticate the connection. * If any password was previously set, it will be discarded. The function * will make a copy of the password string. * * @param conn a Strophe connection object * @param pass the password * * @ingroup Connections */ void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass) { if (conn->pass) xmpp_free(conn->ctx, conn->pass); conn->pass = xmpp_strdup(conn->ctx, pass); }
/** Set the JID of the user that will be bound to the connection. * If any JID was previously set, it will be discarded. This should not be * be used after a connection is created. The function will make a copy of * the JID string. If the supllied JID is missing the node, SASL * ANONYMOUS authentication will be used. * * @param conn a Strophe connection object * @param jid a full or bare JID * * @ingroup Connections */ void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid) { if (conn->jid) xmpp_free(conn->ctx, conn->jid); conn->jid = xmpp_strdup(conn->ctx, jid); }
/* 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; } }
char* parser_attr_name(xmpp_ctx_t *ctx, char *nsname) { return xmpp_strdup(ctx, nsname); }
/** 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; }
/** 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; }