📄 auth.c
字号:
js->auth_type = JABBER_AUTH_DIGEST_MD5; auth = xmlnode_new("auth"); xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5"); jabber_send(js, auth); xmlnode_free(auth); } else if(plain) { js->auth_type = JABBER_AUTH_PLAIN; if(js->gsc == NULL && !purple_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE)) { char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), js->gc->account->username); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, 2, purple_connection_get_account(js->gc), NULL, NULL, purple_connection_get_account(js->gc), allow_plaintext_auth, disallow_plaintext_auth); g_free(msg); return; } finish_plaintext_authentication(js); } else { purple_connection_error(js->gc, _("Server does not use any supported authentication method")); }#endif}static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data){ const char *type = xmlnode_get_attrib(packet, "type"); if(type && !strcmp(type, "result")) { jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); } else { char *msg = jabber_parse_error(js, packet); xmlnode *error; const char *err_code; if((error = xmlnode_get_child(packet, "error")) && (err_code = xmlnode_get_attrib(error, "code")) && !strcmp(err_code, "401")) { js->gc->wants_to_die = TRUE; } purple_connection_error(js->gc, msg); g_free(msg); }}static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data){ JabberIq *iq; xmlnode *query, *x; const char *type = xmlnode_get_attrib(packet, "type"); const char *pw = purple_connection_get_password(js->gc); if(!type) { purple_connection_error(js->gc, _("Invalid response from server.")); return; } else if(!strcmp(type, "error")) { char *msg = jabber_parse_error(js, packet); purple_connection_error(js->gc, msg); g_free(msg); } else if(!strcmp(type, "result")) { query = xmlnode_get_child(packet, "query"); if(js->stream_id && xmlnode_get_child(query, "digest")) { unsigned char hashval[20]; char *s, h[41], *p; int i; iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = xmlnode_get_child(iq->node, "query"); x = xmlnode_new_child(query, "username"); xmlnode_insert_data(x, js->user->node, -1); x = xmlnode_new_child(query, "resource"); xmlnode_insert_data(x, js->user->resource, -1); x = xmlnode_new_child(query, "digest"); s = g_strdup_printf("%s%s", js->stream_id, pw); purple_cipher_digest_region("sha1", (guchar *)s, strlen(s), sizeof(hashval), hashval, NULL); p = h; for(i=0; i<20; i++, p+=2) snprintf(p, 3, "%02x", hashval[i]); xmlnode_insert_data(x, h, -1); g_free(s); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if(xmlnode_get_child(query, "password")) { if(js->gsc == NULL && !purple_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE)) { purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), 2, purple_connection_get_account(js->gc), NULL, NULL, purple_connection_get_account(js->gc), allow_plaintext_auth, disallow_plaintext_auth); return; } finish_plaintext_authentication(js); } else { purple_connection_error(js->gc, _("Server does not use any supported authentication method")); return; } }}void jabber_auth_start_old(JabberStream *js){ JabberIq *iq; xmlnode *query, *username;#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_account_get_password(js->gc->account)) { purple_account_request_password(js->gc->account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js); return; }#endif iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth"); query = xmlnode_get_child(iq->node, "query"); username = xmlnode_new_child(query, "username"); xmlnode_insert_data(username, js->user->node, -1); jabber_iq_set_callback(iq, auth_old_cb, NULL); jabber_iq_send(iq);}/* Parts of this algorithm are inspired by stuff in libgsasl */static GHashTable* parse_challenge(const char *challenge){ const char *token_start, *val_start, *val_end, *cur; GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); cur = challenge; while(*cur != '\0') { /* Find the end of the token */ gboolean in_quotes = FALSE; char *name, *value = NULL; token_start = cur; while(*cur != '\0' && (in_quotes || (!in_quotes && *cur != ','))) { if (*cur == '"') in_quotes = !in_quotes; cur++; } /* Find start of value. */ val_start = strchr(token_start, '='); if (val_start == NULL || val_start > cur) val_start = cur; if (token_start != val_start) { name = g_strndup(token_start, val_start - token_start); if (val_start != cur) { val_start++; while (val_start != cur && (*val_start == ' ' || *val_start == '\t' || *val_start == '\r' || *val_start == '\n' || *val_start == '"')) val_start++; val_end = cur; while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t' || *val_end == '\r' || *val_start == '\n' || *val_end == '"')) val_end--; if (val_start != val_end) value = g_strndup(val_start, val_end - val_start + 1); } g_hash_table_replace(ret, name, value); } /* Find the start of the next token, if there is one */ if (*cur != '\0') { cur++; while (*cur == ' ' || *cur == ',' || *cur == '\t' || *cur == '\r' || *cur == '\n') cur++; } } return ret;}static char *generate_response_value(JabberID *jid, const char *passwd, const char *nonce, const char *cnonce, const char *a2, const char *realm){ PurpleCipher *cipher; PurpleCipherContext *context; guchar result[16]; size_t a1len; gchar *a1, *convnode=NULL, *convpasswd = NULL, *ha1, *ha2, *kd, *x, *z; if((convnode = g_convert(jid->node, strlen(jid->node), "iso-8859-1", "utf-8", NULL, NULL, NULL)) == NULL) { convnode = g_strdup(jid->node); } if(passwd && ((convpasswd = g_convert(passwd, strlen(passwd), "iso-8859-1", "utf-8", NULL, NULL, NULL)) == NULL)) { convpasswd = g_strdup(passwd); } cipher = purple_ciphers_find_cipher("md5"); context = purple_cipher_context_new(cipher, NULL); x = g_strdup_printf("%s:%s:%s", convnode, realm, convpasswd ? convpasswd : ""); purple_cipher_context_append(context, (const guchar *)x, strlen(x)); purple_cipher_context_digest(context, sizeof(result), result, NULL); a1 = g_strdup_printf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce); a1len = strlen(a1); g_memmove(a1, result, 16); purple_cipher_context_reset(context, NULL); purple_cipher_context_append(context, (const guchar *)a1, a1len); purple_cipher_context_digest(context, sizeof(result), result, NULL); ha1 = purple_base16_encode(result, 16); purple_cipher_context_reset(context, NULL); purple_cipher_context_append(context, (const guchar *)a2, strlen(a2)); purple_cipher_context_digest(context, sizeof(result), result, NULL); ha2 = purple_base16_encode(result, 16); kd = g_strdup_printf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2); purple_cipher_context_reset(context, NULL); purple_cipher_context_append(context, (const guchar *)kd, strlen(kd)); purple_cipher_context_digest(context, sizeof(result), result, NULL); purple_cipher_context_destroy(context); z = purple_base16_encode(result, 16); g_free(convnode); g_free(convpasswd); g_free(x); g_free(a1); g_free(ha1); g_free(ha2); g_free(kd); return z;}voidjabber_auth_handle_challenge(JabberStream *js, xmlnode *packet){ if(js->auth_type == JABBER_AUTH_DIGEST_MD5) { char *enc_in = xmlnode_get_data(packet); char *dec_in; char *enc_out; GHashTable *parts; if(!enc_in) { purple_connection_error(js->gc, _("Invalid response from server.")); return; } dec_in = (char *)purple_base64_decode(enc_in, NULL); purple_debug(PURPLE_DEBUG_MISC, "jabber", "decoded challenge (%d): %s\n", strlen(dec_in), dec_in); parts = parse_challenge(dec_in); if (g_hash_table_lookup(parts, "rspauth")) { char *rspauth = g_hash_table_lookup(parts, "rspauth"); if(rspauth && js->expected_rspauth && !strcmp(rspauth, js->expected_rspauth)) { jabber_send_raw(js, "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />", -1); } else { purple_connection_error(js->gc, _("Invalid challenge from server")); } g_free(js->expected_rspauth); } 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) purple_connection_error(js->gc, _("Invalid challenge from server")); else { GString *response = g_string_new(""); char *a2; char *auth_resp; char *buf; 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->expected_rspauth = 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(PURPLE_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); buf = g_strdup_printf("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>", enc_out); jabber_send_raw(js, buf, -1); g_free(buf); g_free(enc_out); g_string_free(response, TRUE); } } g_free(enc_in); g_free(dec_in); g_hash_table_destroy(parts); }#ifdef HAVE_CYRUS_SASL else if (js->auth_type == JABBER_AUTH_CYRUS) { char *enc_in = xmlnode_get_data(packet); unsigned char *dec_in; char *enc_out; const char *c_out; unsigned int clen; gsize declen; xmlnode *response; dec_in = purple_base64_decode(enc_in, &declen); js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, NULL, &c_out, &clen); g_free(enc_in); g_free(dec_in); if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) { purple_debug_error("jabber", "Error is %d : %s\n",js->sasl_state,sasl_errdetail(js->sasl)); purple_connection_error(js->gc, _("SASL error")); return; } else { response = xmlnode_new("response"); xmlnode_set_namespace(response, "urn:ietf:params:xml:ns:xmpp-sasl"); if (clen > 0) { enc_out = purple_base64_encode((unsigned char*)c_out, clen); xmlnode_insert_data(response, enc_out, -1); g_free(enc_out); } jabber_send(js, response); xmlnode_free(response); } }#endif}void jabber_auth_handle_success(JabberStream *js, xmlnode *packet){ const char *ns = xmlnode_get_namespace(packet);#ifdef HAVE_CYRUS_SASL const void *x;#endif if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) { purple_connection_error(js->gc, _("Invalid response from server.")); return; }#ifdef HAVE_CYRUS_SASL /* The SASL docs say that if the client hasn't returned OK yet, we * should try one more round against it */ if (js->sasl_state != SASL_OK) { char *enc_in = xmlnode_get_data(packet); unsigned char *dec_in = NULL; const char *c_out; unsigned int clen; gsize declen = 0; if(enc_in != NULL) dec_in = purple_base64_decode(enc_in, &declen); js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, NULL, &c_out, &clen); g_free(enc_in); g_free(dec_in); if (js->sasl_state != SASL_OK) { /* This should never happen! */ purple_connection_error(js->gc, _("Invalid response from server.")); } } /* If we've negotiated a security layer, we need to enable it */ sasl_getprop(js->sasl, SASL_SSF, &x); if (*(int *)x > 0) { sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x); js->sasl_maxbuf = *(int *)x; }#endif jabber_stream_set_state(js, JABBER_STREAM_REINITIALIZING);}void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet){ char *msg = jabber_parse_error(js, packet); if(!msg) { purple_connection_error(js->gc, _("Invalid response from server.")); } else { purple_connection_error(js->gc, msg); g_free(msg); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -