3 # patch "libpurple/protocols/jabber/auth.c"
4 # from [12caf62ded6c3314a811a59f8730fc801420cd8c]
5 # to [c7157e2b2625e9be09dd8b2c201b1d3dcc72fd19]
7 # patch "libpurple/protocols/jabber/jabber.c"
8 # from [eea6e7d81fa04f0cc37dae64741e2b910f56b45c]
9 # to [2330bff326c57b0af35a8e92d91ad78287655cec]
11 ============================================================
12 --- libpurple/protocols/jabber/auth.c 12caf62ded6c3314a811a59f8730fc801420cd8c
13 +++ libpurple/protocols/jabber/auth.c c7157e2b2625e9be09dd8b2c201b1d3dcc72fd19
14 @@ -330,14 +330,22 @@ static void jabber_auth_start_cyrus(Jabb
15 disallow_plaintext_auth);
18 - /* Everything else has failed, so fail the
19 - * connection. Should probably have a better
24 - purple_connection_error_reason (js->gc,
25 - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
26 - _("Server does not use any supported authentication method"));
27 + /* We have no mechs which can work.
28 + * Try falling back on the old jabber:iq:auth method. We get here if the server supports
29 + * one or more sasl mechs, we are compiled with cyrus-sasl support, but we support or can connect with none of
30 + * the offerred mechs. jabberd 2.0 w/ SASL and Apple's iChat Server 10.5 both handle and expect
31 + * jabber:iq:auth in this situation. iChat Server in particular offers SASL GSSAPI by default, which is often
32 + * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
34 + * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
35 + * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms.
36 + * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers
37 + * which would connect without issue otherwise. -evands
39 + js->auth_type = JABBER_AUTH_IQ_AUTH;
40 + jabber_auth_start_old(js);
44 @@ -563,6 +570,75 @@ static void auth_old_result_cb(JabberStr
49 + * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest
51 + * This is the crammd5 response. Inspired by cyrus-sasl's _sasl_hmac_md5()
54 +auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest)
56 + PurpleCipher *cipher;
57 + PurpleCipherContext *context;
59 + /* inner padding - key XORd with ipad */
60 + unsigned char k_ipad[65];
61 + /* outer padding - key XORd with opad */
62 + unsigned char k_opad[65];
64 + cipher = purple_ciphers_find_cipher("md5");
66 + /* if key is longer than 64 bytes reset it to key=MD5(key) */
67 + if (strlen(key) > 64) {
68 + guchar keydigest[16];
70 + context = purple_cipher_context_new(cipher, NULL);
71 + purple_cipher_context_append(context, (const guchar *)key, strlen(key));
72 + purple_cipher_context_digest(context, 16, keydigest, NULL);
73 + purple_cipher_context_destroy(context);
75 + key = (char *)keydigest;
80 + * the HMAC_MD5 transform looks like:
82 + * MD5(K XOR opad, MD5(K XOR ipad, text))
84 + * where K is an n byte key
85 + * ipad is the byte 0x36 repeated 64 times
86 + * opad is the byte 0x5c repeated 64 times
87 + * and text is the data being protected
90 + /* start out by storing key in pads */
91 + memset(k_ipad, '\0', sizeof k_ipad);
92 + memset(k_opad, '\0', sizeof k_opad);
93 + memcpy(k_ipad, (void *)key, key_len);
94 + memcpy(k_opad, (void *)key, key_len);
96 + /* XOR key with ipad and opad values */
97 + for (i=0; i<64; i++) {
102 + /* perform inner MD5 */
103 + context = purple_cipher_context_new(cipher, NULL);
104 + purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */
105 + purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */
106 + purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */
107 + purple_cipher_context_destroy(context);
109 + /* perform outer MD5 */
110 + context = purple_cipher_context_new(cipher, NULL);
111 + purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */
112 + purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */
113 + purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */
114 + purple_cipher_context_destroy(context);
117 static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
120 @@ -608,6 +685,35 @@ static void auth_old_cb(JabberStream *js
121 jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
124 + } else if(js->stream_id && xmlnode_get_child(query, "crammd5")) {
125 + const char *challenge;
130 + challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
131 + auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), digest);
133 + /* Create the response query */
134 + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
135 + query = xmlnode_get_child(iq->node, "query");
137 + x = xmlnode_new_child(query, "username");
138 + xmlnode_insert_data(x, js->user->node, -1);
139 + x = xmlnode_new_child(query, "resource");
140 + xmlnode_insert_data(x, js->user->resource, -1);
142 + x = xmlnode_new_child(query, "crammd5");
144 + /* Translate the digest to a hexadecimal notation */
146 + for(i=0; i<16; i++, p+=2)
147 + snprintf(p, 3, "%02x", digest[i]);
148 + xmlnode_insert_data(x, h, -1);
150 + jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
151 + jabber_iq_send(iq);
153 } else if(xmlnode_get_child(query, "password")) {
154 if(js->gsc == NULL && !purple_account_get_bool(js->gc->account,
155 "auth_plain_in_clear", FALSE)) {