static PurpleXmlNode * pounce_to_xmlnode(PurplePounce *pounce) { PurpleXmlNode *node, *child; PurpleAccount *pouncer; PurplePounceEvent events; PurplePounceOption options; pouncer = purple_pounce_get_pouncer(pounce); events = purple_pounce_get_events(pounce); options = purple_pounce_get_options(pounce); node = purple_xmlnode_new("pounce"); purple_xmlnode_set_attrib(node, "ui", pounce->ui_type); child = purple_xmlnode_new_child(node, "account"); purple_xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(pouncer)); purple_xmlnode_insert_data(child, purple_normalize(pouncer, purple_account_get_username(pouncer)), -1); child = purple_xmlnode_new_child(node, "pouncee"); purple_xmlnode_insert_data(child, purple_pounce_get_pouncee(pounce), -1); /* Write pounce options */ child = purple_xmlnode_new_child(node, "options"); if (options & PURPLE_POUNCE_OPTION_AWAY) add_option_to_xmlnode(child, "on-away"); /* Write pounce events */ child = purple_xmlnode_new_child(node, "events"); if (events & PURPLE_POUNCE_SIGNON) add_event_to_xmlnode(child, "sign-on"); if (events & PURPLE_POUNCE_SIGNOFF) add_event_to_xmlnode(child, "sign-off"); if (events & PURPLE_POUNCE_AWAY) add_event_to_xmlnode(child, "away"); if (events & PURPLE_POUNCE_AWAY_RETURN) add_event_to_xmlnode(child, "return-from-away"); if (events & PURPLE_POUNCE_IDLE) add_event_to_xmlnode(child, "idle"); if (events & PURPLE_POUNCE_IDLE_RETURN) add_event_to_xmlnode(child, "return-from-idle"); if (events & PURPLE_POUNCE_TYPING) add_event_to_xmlnode(child, "start-typing"); if (events & PURPLE_POUNCE_TYPED) add_event_to_xmlnode(child, "typed"); if (events & PURPLE_POUNCE_TYPING_STOPPED) add_event_to_xmlnode(child, "stop-typing"); if (events & PURPLE_POUNCE_MESSAGE_RECEIVED) add_event_to_xmlnode(child, "message-received"); /* Write pounce actions */ child = purple_xmlnode_new_child(node, "actions"); g_hash_table_foreach(pounce->actions, action_parameter_list_to_xmlnode, child); if (purple_pounce_get_save(pounce)) purple_xmlnode_new_child(node, "save"); return node; }
static void jabber_iq_version_parse(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet) { JabberIq *iq; PurpleXmlNode *query; if(type == JABBER_IQ_GET) { GHashTable *ui_info; const char *ui_name = NULL, *ui_version = NULL; #if 0 char *os = NULL; if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) { struct utsname osinfo; uname(&osinfo); os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); } #endif iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version"); if (from) purple_xmlnode_set_attrib(iq->node, "to", from); jabber_iq_set_id(iq, id); query = purple_xmlnode_get_child(iq->node, "query"); ui_info = purple_core_get_ui_info(); if(NULL != ui_info) { ui_name = g_hash_table_lookup(ui_info, "name"); ui_version = g_hash_table_lookup(ui_info, "version"); } if(NULL != ui_name && NULL != ui_version) { char *version_complete = g_strdup_printf("%s (libpurple " VERSION ")", ui_version); purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), ui_name, -1); purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), version_complete, -1); g_free(version_complete); } else { purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), "libpurple", -1); purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), VERSION, -1); } #if 0 if(os) { purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "os"), os, -1); g_free(os); } #endif jabber_iq_send(iq); } }
static gboolean msn_oim_request_helper(MsnOimRequestData *data) { MsnSession *session = data->oim->session; if (data->send) { /* The Sending of OIM's uses a different token for some reason. */ PurpleXmlNode *ticket; ticket = purple_xmlnode_get_child(data->body, "Header/Ticket"); purple_xmlnode_set_attrib(ticket, "passport", msn_nexus_get_token_str(session->nexus, MSN_AUTH_LIVE_SECURE)); } else { PurpleXmlNode *passport; PurpleXmlNode *xml_t; PurpleXmlNode *xml_p; GHashTable *token; const char *msn_t; const char *msn_p; token = msn_nexus_get_token(session->nexus, MSN_AUTH_MESSENGER_WEB); g_return_val_if_fail(token != NULL, FALSE); msn_t = g_hash_table_lookup(token, "t"); msn_p = g_hash_table_lookup(token, "p"); g_return_val_if_fail(msn_t != NULL, FALSE); g_return_val_if_fail(msn_p != NULL, FALSE); passport = purple_xmlnode_get_child(data->body, "Header/PassportCookie"); xml_t = purple_xmlnode_get_child(passport, "t"); xml_p = purple_xmlnode_get_child(passport, "p"); /* frees old token text, or the 'EMPTY' text if first time */ purple_xmlnode_free(xml_t->child); purple_xmlnode_free(xml_p->child); purple_xmlnode_insert_data(xml_t, msn_t, -1); purple_xmlnode_insert_data(xml_p, msn_p, -1); } msn_soap_service_send_message(session->soap, msn_soap_message_new(data->action, purple_xmlnode_copy(data->body)), data->host, data->url, FALSE, msn_oim_request_cb, data); return FALSE; }
static void finish_plaintext_authentication(JabberStream *js) { JabberIq *iq; PurpleXmlNode *query, *x; iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "password"); purple_xmlnode_insert_data(x, purple_connection_get_password(js->gc), -1); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); }
void jabber_auth_start_old(JabberStream *js) { PurpleAccount *account; JabberIq *iq; PurpleXmlNode *query, *username; account = purple_connection_get_account(js->gc); /* * We can end up here without encryption if the server doesn't support * <stream:features/> and we're not using old-style SSL. If the user * is requiring SSL/TLS, we need to enforce it. */ if (!jabber_stream_is_ssl(js) && g_str_equal("require_tls", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) { purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, _("You require encryption, but it is not available on this server.")); return; } if (js->registration) { jabber_register_start(js); return; } /* * IQ Auth doesn't have support for resource binding, so we need to pick a * default resource so it will work properly. jabberd14 throws an error and * iChat server just fails silently. */ if (!js->user->resource || *js->user->resource == '\0') { g_free(js->user->resource); js->user->resource = g_strdup("Home"); } #ifdef HAVE_CYRUS_SASL /* If we have Cyrus SASL, then passwords will have been set * to OPTIONAL for this protocol. So, we need to do our own * password prompting here */ if (!purple_connection_get_password(js->gc)) { purple_account_request_password(account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return; } #endif iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); username = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(username, js->user->node, -1); jabber_iq_set_callback(iq, auth_old_cb, NULL); jabber_iq_send(iq); }
static void jabber_time_parse(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *child) { JabberIq *iq; time_t now_t; struct tm *tm; time(&now_t); if(type == JABBER_IQ_GET) { PurpleXmlNode *tzo, *utc; const char *date, *tz; iq = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(iq, id); if (from) purple_xmlnode_set_attrib(iq->node, "to", from); child = purple_xmlnode_new_child(iq->node, child->name); purple_xmlnode_set_namespace(child, NS_ENTITY_TIME); /* <tzo>-06:00</tzo> */ tm = localtime(&now_t); tz = purple_get_tzoff_str(tm, TRUE); tzo = purple_xmlnode_new_child(child, "tzo"); purple_xmlnode_insert_data(tzo, tz, -1); /* <utc>2006-12-19T17:58:35Z</utc> */ tm = gmtime(&now_t); date = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm); utc = purple_xmlnode_new_child(child, "utc"); purple_xmlnode_insert_data(utc, date, -1); jabber_iq_send(iq); } else { /* TODO: Errors */ } }
static void purple_xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) { struct _xmlnode_parser_data *xpd = user_data; if(!xpd->current || xpd->error) return; if(!text || !text_len) return; purple_xmlnode_insert_data(xpd->current, (const char*) text, text_len); }
static void action_parameter_to_xmlnode(gpointer key, gpointer value, gpointer user_data) { const char *name, *param_value; PurpleXmlNode *node, *child; name = (const char *)key; param_value = (const char *)value; node = (PurpleXmlNode *)user_data; child = purple_xmlnode_new_child(node, "param"); purple_xmlnode_set_attrib(child, "name", name); purple_xmlnode_insert_data(child, param_value, -1); }
static void do_nick_set(JabberStream *js, const char *nick) { PurpleXmlNode *publish, *nicknode; publish = purple_xmlnode_new("publish"); purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick"); nicknode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "nick"); purple_xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick"); if(nick && nick[0] != '\0') purple_xmlnode_insert_data(nicknode, nick, -1); jabber_pep_publish(js, publish); /* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free (yay for well-defined memory management rules) */ }
void jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data, gsize size) { JabberIBBSessionState state = jabber_ibb_session_get_state(sess); purple_debug_info("jabber", "sending data block of %" G_GSIZE_FORMAT " bytes on IBB stream\n", size); if (state != JABBER_IBB_SESSION_OPENED) { purple_debug_error("jabber", "trying to send data on a non-open IBB session\n"); } else if (size > jabber_ibb_session_get_max_data_size(sess)) { purple_debug_error("jabber", "trying to send a too large packet in the IBB session\n"); } else { JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), JABBER_IQ_SET); PurpleXmlNode *data_element = purple_xmlnode_new("data"); char *base64 = purple_base64_encode(data, size); char seq[10]; g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess)); purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); purple_xmlnode_set_namespace(data_element, NS_IBB); purple_xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess)); purple_xmlnode_set_attrib(data_element, "seq", seq); purple_xmlnode_insert_data(data_element, base64, -1); purple_xmlnode_insert_child(set->node, data_element); purple_debug_info("jabber", "IBB: setting send <iq/> callback for session %p %s\n", sess, sess->sid); jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess); sess->last_iq_id = g_strdup(purple_xmlnode_get_attrib(set->node, "id")); purple_debug_info("jabber", "IBB: set sess->last_iq_id: %s\n", sess->last_iq_id); jabber_iq_send(set); g_free(base64); (sess->send_seq)++; } }
JabberIq * jingle_session_redirect_packet(JingleSession *session, const gchar *sid) { JabberIq *iq = jingle_session_terminate_packet(session, "alternative-session"); PurpleXmlNode *alt_session; if (sid == NULL) return iq; alt_session = purple_xmlnode_get_child(iq->node, "jingle/reason/alternative-session"); if (alt_session != NULL) { PurpleXmlNode *sid_node = purple_xmlnode_new_child(alt_session, "sid"); purple_xmlnode_insert_data(sid_node, sid, -1); } return iq; }
static void xep_ft_si_result(PurpleXfer *xfer, const char *to) { PurpleXmlNode *si_node, *feature, *field, *value, *x; XepIq *iq; XepXfer *xf; BonjourData *bd; if(!to || !xfer) return; xf = purple_xfer_get_protocol_data(xfer); if(!xf) return; bd = xf->data; purple_debug_info("bonjour", "xep file transfer stream initialization result.\n"); iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id); if(iq == NULL) return; si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/ feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "submit"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); value = purple_xmlnode_new_child(field, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); xep_iq_send_and_free(iq); }
static void xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type) { PurpleXmlNode *error_node; XepIq *iq; g_return_if_fail(error_code != NULL); g_return_if_fail(error_type != NULL); if(!to || !id) { purple_debug_info("bonjour", "xep file transfer stream initialization error.\n"); return; } iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id); if(iq == NULL) return; error_node = purple_xmlnode_new_child(iq->node, "error"); purple_xmlnode_set_attrib(error_node, "code", error_code); purple_xmlnode_set_attrib(error_node, "type", error_type); /* TODO: Make this better */ if (!strcmp(error_code, "403")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "forbidden"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); tmp_node = purple_xmlnode_new_child(error_node, "text"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); purple_xmlnode_insert_data(tmp_node, "Offer Declined", -1); } else if (!strcmp(error_code, "404")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "item-not-found"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); } xep_iq_send_and_free(iq); }
gboolean jabber_mood_set(JabberStream *js, const char *mood, const char *text) { const PurpleMood *target_mood = NULL; PurpleXmlNode *publish, *moodnode; if (mood && *mood) { target_mood = find_mood_by_name(mood); /* Mood specified, but is invalid -- * fail so that the command can handle this. */ if (!target_mood) return FALSE; } publish = purple_xmlnode_new("publish"); purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood"); moodnode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "mood"); purple_xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood"); if (target_mood) { /* If target_mood is not NULL, then * target_mood->mood == mood, and is a valid element name. */ purple_xmlnode_new_child(moodnode, mood); /* Only set text when setting a mood */ if (text && *text) { PurpleXmlNode *textnode = purple_xmlnode_new_child(moodnode, "text"); purple_xmlnode_insert_data(textnode, text, -1); } } jabber_pep_publish(js, publish); return TRUE; }
static void xep_ft_si_offer(PurpleXfer *xfer, const gchar *to) { PurpleXmlNode *si_node, *feature, *field, *file, *x; XepIq *iq; XepXfer *xf = purple_xfer_get_protocol_data(xfer); BonjourData *bd = NULL; char buf[32]; if(!xf) return; bd = xf->data; if(!bd) return; purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id); /* Assign stream id. */ g_free(xf->iq_id); xf->iq_id = g_strdup_printf("%u", next_id++); iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id); if(iq == NULL) return; /*Construct Stream initialization offer message.*/ si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); g_free(xf->sid); xf->sid = g_strdup(xf->iq_id); purple_xmlnode_set_attrib(si_node, "id", xf->sid); file = purple_xmlnode_new_child(si_node, "file"); purple_xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer)); g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer)); purple_xmlnode_set_attrib(file, "size", buf); feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "form"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); purple_xmlnode_set_attrib(field, "type", "list-single"); if (xf->mode & XEP_BYTESTREAMS) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); } if (xf->mode & XEP_IBB) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); } xep_iq_send_and_free(iq); }
static JabberSaslState digest_md5_handle_challenge(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **response, char **msg) { PurpleXmlNode *reply = NULL; char *enc_in = purple_xmlnode_get_data(packet); char *dec_in; char *enc_out; GHashTable *parts; JabberSaslState state = JABBER_SASL_STATE_CONTINUE; if (!enc_in) { *msg = g_strdup(_("Invalid response from server")); return JABBER_SASL_STATE_FAIL; } dec_in = (char *)purple_base64_decode(enc_in, NULL); purple_debug_misc("jabber", "decoded challenge (%" G_GSIZE_FORMAT "): %s\n", strlen(dec_in), dec_in); parts = jabber_auth_digest_md5_parse(dec_in); if (g_hash_table_lookup(parts, "rspauth")) { char *rspauth = g_hash_table_lookup(parts, "rspauth"); char *expected_rspauth = js->auth_mech_data; if (rspauth && purple_strequal(rspauth, expected_rspauth)) { reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); } else { *msg = g_strdup(_("Invalid challenge from server")); state = JABBER_SASL_STATE_FAIL; } g_free(js->auth_mech_data); js->auth_mech_data = NULL; } else { /* assemble a response, and send it */ /* see RFC 2831 */ char *realm; char *nonce; /* Make sure the auth string contains everything that should be there. This isn't everything in RFC2831, but it is what we need. */ nonce = g_hash_table_lookup(parts, "nonce"); /* we're actually supposed to prompt the user for a realm if * the server doesn't send one, but that really complicates things, * so i'm not gonna worry about it until is poses a problem to * someone, or I get really bored */ realm = g_hash_table_lookup(parts, "realm"); if(!realm) realm = js->user->domain; if (nonce == NULL || realm == NULL) { *msg = g_strdup(_("Invalid challenge from server")); state = JABBER_SASL_STATE_FAIL; } else { GString *response = g_string_new(""); char *a2; char *auth_resp; char *cnonce; cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), g_random_int()); a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); auth_resp = generate_response_value(js->user, purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); g_free(a2); a2 = g_strdup_printf(":xmpp/%s", realm); js->auth_mech_data = generate_response_value(js->user, purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); g_free(a2); g_string_append_printf(response, "username=\"%s\"", js->user->node); g_string_append_printf(response, ",realm=\"%s\"", realm); g_string_append_printf(response, ",nonce=\"%s\"", nonce); g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); g_string_append_printf(response, ",nc=00000001"); g_string_append_printf(response, ",qop=auth"); g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); g_string_append_printf(response, ",response=%s", auth_resp); g_string_append_printf(response, ",charset=utf-8"); g_free(auth_resp); g_free(cnonce); enc_out = purple_base64_encode((guchar *)response->str, response->len); purple_debug_misc("jabber", "decoded response (%" G_GSIZE_FORMAT "): %s\n", response->len, response->str); reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_xmlnode_insert_data(reply, enc_out, -1); g_free(enc_out); g_string_free(response, TRUE); } } g_free(enc_in); g_free(dec_in); g_hash_table_destroy(parts); *response = reply; return state; }
static JabberSaslState scram_start(JabberStream *js, PurpleXmlNode *mechanisms, PurpleXmlNode **out, char **error) { PurpleXmlNode *reply; JabberScramData *data; guint64 cnonce; #ifdef CHANNEL_BINDING gboolean binding_supported = TRUE; #endif gchar *dec_out, *enc_out; gchar *prepped_node, *tmp; gchar *prepped_pass; prepped_node = jabber_saslprep(js->user->node); if (!prepped_node) { *error = g_strdup(_("Unable to canonicalize username")); return JABBER_SASL_STATE_FAIL; } tmp = escape_username(prepped_node); g_free(prepped_node); prepped_node = tmp; prepped_pass = jabber_saslprep(purple_connection_get_password(js->gc)); if (!prepped_pass) { g_free(prepped_node); *error = g_strdup(_("Unable to canonicalize password")); return JABBER_SASL_STATE_FAIL; } data = js->auth_mech_data = g_new0(JabberScramData, 1); data->hash = mech_to_hash(js->auth_mech->name); data->password = prepped_pass; #ifdef CHANNEL_BINDING if (strstr(js->auth_mech_name, "-PLUS")) data->channel_binding = TRUE; #endif cnonce = ((guint64)g_random_int() << 32) | g_random_int(); data->cnonce = purple_base64_encode((guchar *)&cnonce, sizeof(cnonce)); data->auth_message = g_string_new(NULL); g_string_printf(data->auth_message, "n=%s,r=%s", prepped_node, data->cnonce); g_free(prepped_node); data->step = 1; reply = purple_xmlnode_new("auth"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name); /* TODO: Channel binding */ dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str); enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out)); purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out); purple_xmlnode_insert_data(reply, enc_out, -1); g_free(enc_out); g_free(dec_out); *out = reply; return JABBER_SASL_STATE_CONTINUE; }
static void auth_old_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data) { JabberIq *iq; PurpleXmlNode *query, *x; const char *pw = purple_connection_get_password(js->gc); if (type == JABBER_IQ_ERROR) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); purple_connection_error(js->gc, reason, msg); g_free(msg); } else if (type == JABBER_IQ_RESULT) { query = purple_xmlnode_get_child(packet, "query"); if (js->stream_id && *js->stream_id && purple_xmlnode_get_child(query, "digest")) { char *s, *hash; iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "digest"); s = g_strdup_printf("%s%s", js->stream_id, pw); hash = jabber_calculate_data_hash(s, strlen(s), "sha1"); purple_xmlnode_insert_data(x, hash, -1); g_free(hash); g_free(s); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if ((x = purple_xmlnode_get_child(query, "crammd5"))) { /* For future reference, this appears to be a custom OS X extension * to non-SASL authentication. */ const char *challenge; gchar digest[33]; PurpleCipher *hmac; PurpleHash *md5; gssize diglen; /* Calculate the MHAC-MD5 digest */ md5 = purple_md5_hash_new(); hmac = purple_hmac_cipher_new(md5); challenge = purple_xmlnode_get_attrib(x, "challenge"); purple_cipher_set_key(hmac, (guchar *)pw, strlen(pw)); purple_cipher_append(hmac, (guchar *)challenge, strlen(challenge)); diglen = purple_cipher_digest_to_str(hmac, digest, 33); g_object_unref(hmac); g_object_unref(md5); g_return_if_fail(diglen > 0); /* Create the response query */ iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "crammd5"); purple_xmlnode_insert_data(x, digest, 32); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if(purple_xmlnode_get_child(query, "password")) { PurpleAccount *account = purple_connection_get_account(js->gc); if(!jabber_stream_is_ssl(js) && !purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) { char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, 1, purple_request_cpar_from_account(account), account, allow_plaintext_auth, disallow_plaintext_auth); g_free(msg); return; } finish_plaintext_authentication(js); } else { purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, _("Server does not use any supported authentication method")); return; } } }
static JabberSaslState scram_handle_challenge(JabberStream *js, PurpleXmlNode *challenge, PurpleXmlNode **out, char **error) { JabberScramData *data = js->auth_mech_data; PurpleXmlNode *reply; gchar *enc_in, *dec_in = NULL; gchar *enc_out = NULL, *dec_out = NULL; gsize len; JabberSaslState state = JABBER_SASL_STATE_FAIL; enc_in = purple_xmlnode_get_data(challenge); if (!enc_in || *enc_in == '\0') { reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Invalid challenge from server")); goto out; } dec_in = (gchar *)purple_base64_decode(enc_in, &len); if (!dec_in || len != strlen(dec_in)) { /* Danger afoot; SCRAM shouldn't contain NUL bytes */ reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Malicious challenge from server")); goto out; } purple_debug_misc("jabber", "decoded challenge: %s\n", dec_in); if (!jabber_scram_feed_parser(data, dec_in, &dec_out)) { reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Invalid challenge from server")); goto out; } data->step += 1; reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)"); if (dec_out) { enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out)); purple_xmlnode_insert_data(reply, enc_out, -1); } state = JABBER_SASL_STATE_CONTINUE; out: g_free(enc_in); g_free(dec_in); g_free(enc_out); g_free(dec_out); *out = reply; return state; }