src/protocols/jabber/auth.c

changeset 12508
32f6f8bf3a57
parent 11183
be87fe695c93
child 12520
3ec49915efd8
equal deleted inserted replaced
12507:9c4e2db872fd 12508:32f6f8bf3a57
112 static void disallow_plaintext_auth(GaimAccount *account) 112 static void disallow_plaintext_auth(GaimAccount *account)
113 { 113 {
114 gaim_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream")); 114 gaim_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream"));
115 } 115 }
116 116
117 #ifdef HAVE_CYRUS_SASL
118
119 static void jabber_auth_start_cyrus(JabberStream *);
120
121 /* Callbacks for Cyrus SASL */
122
123 static int jabber_sasl_cb_realm(void *ctx, int id, const char **avail, const char **result)
124 {
125 JabberStream *js = (JabberStream *)ctx;
126
127 if (id != SASL_CB_GETREALM || !result) return SASL_BADPARAM;
128
129 *result = js->user->domain;
130
131 return SASL_OK;
132 }
133
134 static int jabber_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len)
135 {
136 JabberStream *js = (JabberStream *)ctx;
137
138 switch(id) {
139 case SASL_CB_AUTHNAME:
140 *res = js->user->node;
141 break;
142 case SASL_CB_USER:
143 *res = js->user->node;
144 break;
145 default:
146 return SASL_BADPARAM;
147 }
148 if (len) *len = strlen((char *)*res);
149 return SASL_OK;
150 }
151
152 static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret)
153 {
154 JabberStream *js = (JabberStream *)ctx;
155 const char *pw = gaim_account_get_password(js->gc->account);
156 size_t len;
157 static sasl_secret_t *x = NULL;
158
159 if (!conn || !secret || id != SASL_CB_PASS)
160 return SASL_BADPARAM;
161
162 len = strlen(pw);
163 x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len);
164
165 if (!x)
166 return SASL_NOMEM;
167
168 x->len = len;
169 strcpy((char*)x->data, pw);
170
171 *secret = x;
172 return SASL_OK;
173 }
174
175 static void allow_cyrus_plaintext_auth(GaimAccount *account)
176 {
177 gaim_account_set_bool(account, "auth_plain_in_clear", TRUE);
178
179 jabber_auth_start_cyrus(account->gc->proto_data);
180 }
181
182 static void jabber_auth_start_cyrus(JabberStream *js)
183 {
184 const char *clientout, *mech;
185 char *enc_out;
186 unsigned coutlen;
187 xmlnode *auth;
188 sasl_security_properties_t secprops;
189 gboolean again;
190 gboolean plaintext = TRUE;
191
192 /* Set up security properties and options */
193 secprops.min_ssf = 0;
194 secprops.security_flags = SASL_SEC_NOANONYMOUS;
195
196 if (!js->gsc) {
197 plaintext = gaim_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE);
198 if (!plaintext)
199 secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
200 secprops.max_ssf = -1;
201 secprops.maxbufsize = 4096;
202 } else {
203 plaintext = FALSE;
204 secprops.max_ssf = 0;
205 secprops.maxbufsize = 0;
206 }
207 secprops.property_names = 0;
208 secprops.property_values = 0;
209
210 do {
211 again = FALSE;
212 /* Use the user's domain for compatibility with the old
213 * DIGESTMD5 code. Note that this may cause problems where
214 * the user's domain doesn't match the FQDN of the jabber
215 * service
216 */
217
218 js->sasl_state = sasl_client_new("xmpp", js->user->domain, NULL, NULL, js->sasl_cb, 0, &js->sasl);
219 if (js->sasl_state==SASL_OK) {
220 sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops);
221 js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &mech);
222 }
223 switch (js->sasl_state) {
224 /* Success */
225 case SASL_CONTINUE:
226 break;
227 case SASL_NOMECH:
228 /* No mechanisms do what we want. See if we can add
229 * plaintext ones to the list. */
230
231 if (!gaim_account_get_password(js->gc->account)) {
232 gaim_connection_error(js->gc, _("Server couldn't authenticate you without a password"));
233 return;
234 } else if (!plaintext) {
235 gaim_request_yes_no(js->gc, _("Plaintext Authentication"),
236 _("Plaintext Authentication"),
237 _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"),
238 2, js->gc->account,
239 allow_cyrus_plaintext_auth,
240 disallow_plaintext_auth);
241 return;
242 } else {
243 gaim_connection_error(js->gc, _("Server does not use any supported authentication method"));
244 return;
245 }
246 /* not reached */
247 break;
248
249 /* Fatal errors. Give up and go home */
250 case SASL_BADPARAM:
251 case SASL_NOMEM:
252 break;
253
254 /* For everything else, fail the mechanism and try again */
255 default:
256 if (strlen(mech)>0) {
257 char *pos;
258 pos = strstr(js->sasl_mechs->str,mech);
259 g_assert(pos!=NULL);
260 g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str,strlen(mech));
261 }
262 sasl_dispose(&js->sasl);
263 again=TRUE;
264 }
265 } while (again);
266
267 if (js->sasl_state == SASL_CONTINUE) {
268 auth = xmlnode_new("auth");
269 xmlnode_set_attrib(auth, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
270 xmlnode_set_attrib(auth,"mechanism", mech);
271 if (clientout) {
272 if (coutlen == 0) {
273 xmlnode_insert_data(auth, "=", -1);
274 } else {
275 enc_out = gaim_base64_encode((unsigned char*)clientout, coutlen);
276 xmlnode_insert_data(auth, enc_out, -1);
277 g_free(enc_out);
278 }
279 }
280 jabber_send(js, auth);
281 xmlnode_free(auth);
282 } else {
283 gaim_connection_error(js->gc, "SASL authentication failed\n");
284 }
285 }
286
287 #endif
288
117 void 289 void
118 jabber_auth_start(JabberStream *js, xmlnode *packet) 290 jabber_auth_start(JabberStream *js, xmlnode *packet)
119 { 291 {
292 #ifdef HAVE_CYRUS_SASL
293 int id;
294 #else
295 gboolean digest_md5 = FALSE, plain=FALSE;
296 #endif
297
120 xmlnode *mechs, *mechnode; 298 xmlnode *mechs, *mechnode;
121
122 gboolean digest_md5 = FALSE, plain=FALSE;
123 299
124 300
125 if(js->registration) { 301 if(js->registration) {
126 jabber_register_start(js); 302 jabber_register_start(js);
127 return; 303 return;
131 307
132 if(!mechs) { 308 if(!mechs) {
133 gaim_connection_error(js->gc, _("Invalid response from server.")); 309 gaim_connection_error(js->gc, _("Invalid response from server."));
134 return; 310 return;
135 } 311 }
312
313 #ifdef HAVE_CYRUS_SASL
314 js->sasl_mechs = g_string_new("");
315 #endif
136 316
137 for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode; 317 for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode;
138 mechnode = xmlnode_get_next_twin(mechnode)) 318 mechnode = xmlnode_get_next_twin(mechnode))
139 { 319 {
140 char *mech_name = xmlnode_get_data(mechnode); 320 char *mech_name = xmlnode_get_data(mechnode);
321 #ifdef HAVE_CYRUS_SASL
322 g_string_append(js->sasl_mechs, mech_name);
323 g_string_append_c(js->sasl_mechs,' ');
324 #else
141 if(mech_name && !strcmp(mech_name, "DIGEST-MD5")) 325 if(mech_name && !strcmp(mech_name, "DIGEST-MD5"))
142 digest_md5 = TRUE; 326 digest_md5 = TRUE;
143 else if(mech_name && !strcmp(mech_name, "PLAIN")) 327 else if(mech_name && !strcmp(mech_name, "PLAIN"))
144 plain = TRUE; 328 plain = TRUE;
329 #endif
145 g_free(mech_name); 330 g_free(mech_name);
146 } 331 }
147 332
333 #ifdef HAVE_CYRUS_SASL
334 js->auth_type = JABBER_AUTH_CYRUS;
335
336 /* Set up our callbacks structure */
337 js->sasl_cb = g_new0(sasl_callback_t,5);
338
339 id = 0;
340 js->sasl_cb[id].id = SASL_CB_GETREALM;
341 js->sasl_cb[id].proc = jabber_sasl_cb_realm;
342 js->sasl_cb[id].context = (void *)js;
343 id++;
344
345 js->sasl_cb[id].id = SASL_CB_AUTHNAME;
346 js->sasl_cb[id].proc = jabber_sasl_cb_simple;
347 js->sasl_cb[id].context = (void *)js;
348 id++;
349
350 js->sasl_cb[id].id = SASL_CB_USER;
351 js->sasl_cb[id].proc = jabber_sasl_cb_simple;
352 js->sasl_cb[id].context = (void *)js;
353 id++;
354
355 if (gaim_account_get_password(js->gc->account)) {
356 js->sasl_cb[id].id = SASL_CB_PASS;
357 js->sasl_cb[id].proc = jabber_sasl_cb_secret;
358 js->sasl_cb[id].context = (void *)js;
359 id++;
360 }
361
362 js->sasl_cb[id].id = SASL_CB_LIST_END;
363
364 jabber_auth_start_cyrus(js);
365 #else
148 366
149 if(digest_md5) { 367 if(digest_md5) {
150 xmlnode *auth; 368 xmlnode *auth;
151 369
152 js->auth_type = JABBER_AUTH_DIGEST_MD5; 370 js->auth_type = JABBER_AUTH_DIGEST_MD5;
170 finish_plaintext_authentication(js); 388 finish_plaintext_authentication(js);
171 } else { 389 } else {
172 gaim_connection_error(js->gc, 390 gaim_connection_error(js->gc,
173 _("Server does not use any supported authentication method")); 391 _("Server does not use any supported authentication method"));
174 } 392 }
393 #endif
175 } 394 }
176 395
177 static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data) 396 static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
178 { 397 {
179 const char *type = xmlnode_get_attrib(packet, "type"); 398 const char *type = xmlnode_get_attrib(packet, "type");
461 680
462 g_free(enc_in); 681 g_free(enc_in);
463 g_free(dec_in); 682 g_free(dec_in);
464 g_hash_table_destroy(parts); 683 g_hash_table_destroy(parts);
465 } 684 }
685 #ifdef HAVE_CYRUS_SASL
686 else if (js->auth_type == JABBER_AUTH_CYRUS) {
687 char *enc_in = xmlnode_get_data(packet);
688 unsigned char *dec_in;
689 char *enc_out;
690 const char *c_out;
691 unsigned int clen,declen;
692 xmlnode *response;
693
694 dec_in = gaim_base64_decode(enc_in, &declen);
695
696 js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen,
697 NULL, &c_out, &clen);
698 g_free(dec_in);
699 if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) {
700 gaim_debug_error("jabber", "Error is %d : %s\n",js->sasl_state,sasl_errdetail(js->sasl));
701 gaim_connection_error(js->gc, _("SASL error"));
702 return;
703 } else {
704 response = xmlnode_new("response");
705 xmlnode_set_attrib(response, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
706 if (c_out) {
707 enc_out = gaim_base64_encode((unsigned char*)c_out, clen);
708 xmlnode_insert_data(response, enc_out, -1);
709 g_free(enc_out);
710 }
711 jabber_send(js, response);
712 xmlnode_free(response);
713 }
714 }
715 #endif
466 } 716 }
467 717
468 void jabber_auth_handle_success(JabberStream *js, xmlnode *packet) 718 void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
469 { 719 {
470 const char *ns = xmlnode_get_attrib(packet, "xmlns"); 720 const char *ns = xmlnode_get_attrib(packet, "xmlns");
721 #ifdef HAVE_CYRUS_SASL
722 int *x;
723 #endif
471 724
472 if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) { 725 if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) {
473 gaim_connection_error(js->gc, _("Invalid response from server.")); 726 gaim_connection_error(js->gc, _("Invalid response from server."));
474 return; 727 return;
475 } 728 }
729
730 #if HAVE_CYRUS_SASL
731 /* The SASL docs say that if the client hasn't returned OK yet, we
732 * should try one more round against it
733 */
734 if (js->sasl_state != SASL_OK) {
735 js->sasl_state = sasl_client_step(js->sasl, NULL, 0, NULL, NULL, NULL);
736 if (js->sasl_state != SASL_OK) {
737 /* This should never happen! */
738 gaim_connection_error(js->gc, _("Invalid response from server."));
739 }
740 }
741 /* If we've negotiated a security layer, we need to enable it */
742 sasl_getprop(js->sasl, SASL_SSF, (const void **)&x);
743 if (*x>0) {
744 sasl_getprop(js->sasl, SASL_MAXOUTBUF, (const void **)&x);
745 js->sasl_maxbuf = *x;
746 }
747 #endif
476 748
477 jabber_stream_set_state(js, JABBER_STREAM_REINITIALIZING); 749 jabber_stream_set_state(js, JABBER_STREAM_REINITIALIZING);
478 } 750 }
479 751
480 void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet) 752 void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet)

mercurial