📄 auth.c
字号:
/* * purple - Jabber Protocol Plugin * * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include "internal.h"#include "account.h"#include "debug.h"#include "cipher.h"#include "conversation.h"#include "request.h"#include "sslconn.h"#include "util.h"#include "xmlnode.h"#include "jutil.h"#include "auth.h"#include "jabber.h"#include "iq.h"#include "notify.h"static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data);gbooleanjabber_process_starttls(JabberStream *js, xmlnode *packet){ xmlnode *starttls; if((starttls = xmlnode_get_child(packet, "starttls"))) { if(purple_ssl_is_supported()) { jabber_send_raw(js, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1); return TRUE; } else if(xmlnode_get_child(starttls, "required")) { purple_connection_error(js->gc, _("Server requires TLS/SSL for login. No TLS/SSL support found.")); return TRUE; } } return FALSE;}static void finish_plaintext_authentication(JabberStream *js){ if(js->auth_type == JABBER_AUTH_PLAIN) { xmlnode *auth; GString *response; gchar *enc_out; auth = xmlnode_new("auth"); xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth"); xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true"); response = g_string_new(""); response = g_string_append_len(response, "\0", 1); response = g_string_append(response, js->user->node); response = g_string_append_len(response, "\0", 1); response = g_string_append(response, purple_connection_get_password(js->gc)); enc_out = purple_base64_encode((guchar *)response->str, response->len); xmlnode_set_attrib(auth, "mechanism", "PLAIN"); xmlnode_insert_data(auth, enc_out, -1); g_free(enc_out); g_string_free(response, TRUE); jabber_send(js, auth); xmlnode_free(auth); } else if(js->auth_type == JABBER_AUTH_IQ_AUTH) { JabberIq *iq; xmlnode *query, *x; 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, "password"); 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); }}static void allow_plaintext_auth(PurpleAccount *account){ purple_account_set_bool(account, "auth_plain_in_clear", TRUE); finish_plaintext_authentication(account->gc->proto_data);}static void disallow_plaintext_auth(PurpleAccount *account){ purple_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream"));}#ifdef HAVE_CYRUS_SASLstatic void jabber_auth_start_cyrus(JabberStream *);static void jabber_sasl_build_callbacks(JabberStream *);/* Callbacks for Cyrus SASL */static int jabber_sasl_cb_realm(void *ctx, int id, const char **avail, const char **result){ JabberStream *js = (JabberStream *)ctx; if (id != SASL_CB_GETREALM || !result) return SASL_BADPARAM; *result = js->user->domain; return SASL_OK;}static int jabber_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len){ JabberStream *js = (JabberStream *)ctx; switch(id) { case SASL_CB_AUTHNAME: *res = js->user->node; break; case SASL_CB_USER: *res = ""; break; default: return SASL_BADPARAM; } if (len) *len = strlen((char *)*res); return SASL_OK;}static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret){ JabberStream *js = (JabberStream *)ctx; const char *pw = purple_account_get_password(js->gc->account); size_t len; static sasl_secret_t *x = NULL; if (!conn || !secret || id != SASL_CB_PASS) return SASL_BADPARAM; len = strlen(pw); x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len); if (!x) return SASL_NOMEM; x->len = len; strcpy((char*)x->data, pw); *secret = x; return SASL_OK;}static void allow_cyrus_plaintext_auth(PurpleAccount *account){ purple_account_set_bool(account, "auth_plain_in_clear", TRUE); jabber_auth_start_cyrus(account->gc->proto_data);}static gboolean auth_pass_generic(JabberStream *js, PurpleRequestFields *fields){ const char *entry; gboolean remember; entry = purple_request_fields_get_string(fields, "password"); remember = purple_request_fields_get_bool(fields, "remember"); if (!entry || !*entry) { purple_notify_error(js->gc->account, NULL, _("Password is required to sign on."), NULL); return FALSE; } if (remember) purple_account_set_remember_password(js->gc->account, TRUE); purple_account_set_password(js->gc->account, entry); return TRUE;} static void auth_pass_cb(JabberStream *js, PurpleRequestFields *fields){ if (!auth_pass_generic(js, fields)) return; /* Rebuild our callbacks as we now have a password to offer */ jabber_sasl_build_callbacks(js); /* Restart our connection */ jabber_auth_start_cyrus(js);}static voidauth_old_pass_cb(JabberStream *js, PurpleRequestFields *fields){ if (!auth_pass_generic(js, fields)) return; /* Restart our connection */ jabber_auth_start_old(js);}static voidauth_no_pass_cb(JabberStream *js, PurpleRequestFields *fields){ purple_connection_error(js->gc, _("Password is required to sign on."));}static void jabber_auth_start_cyrus(JabberStream *js){ const char *clientout = NULL, *mech = NULL; char *enc_out; unsigned coutlen = 0; xmlnode *auth; sasl_security_properties_t secprops; gboolean again; gboolean plaintext = TRUE; /* Set up security properties and options */ secprops.min_ssf = 0; secprops.security_flags = SASL_SEC_NOANONYMOUS; if (!js->gsc) { secprops.max_ssf = -1; secprops.maxbufsize = 4096; plaintext = purple_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE); if (!plaintext) secprops.security_flags |= SASL_SEC_NOPLAINTEXT; } else { secprops.max_ssf = 0; secprops.maxbufsize = 0; plaintext = TRUE; } secprops.property_names = 0; secprops.property_values = 0; do { again = FALSE; js->sasl_state = sasl_client_new("xmpp", js->serverFQDN, NULL, NULL, js->sasl_cb, 0, &js->sasl); if (js->sasl_state==SASL_OK) { sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops); purple_debug_info("sasl", "Mechs found: %s\n", js->sasl_mechs->str); js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &mech); } switch (js->sasl_state) { /* Success */ case SASL_OK: case SASL_CONTINUE: break; case SASL_NOMECH: /* No mechanisms have offered to help */ /* Firstly, if we don't have a password try * to get one */ if (!purple_account_get_password(js->gc->account)) { purple_account_request_password(js->gc->account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js); return; /* If we've got a password, but aren't sending * it in plaintext, see if we can turn on * plaintext auth */ } else if (!plaintext) { 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, js->gc->account, NULL, NULL, NULL, allow_cyrus_plaintext_auth, disallow_plaintext_auth); g_free(msg); return; /* Everything else has failed, so fail the * connection. Should probably have a better * error here. */ } else { purple_connection_error(js->gc, _("Server does not use any supported authentication method")); return; } /* not reached */ break; /* Fatal errors. Give up and go home */ case SASL_BADPARAM: case SASL_NOMEM: break; /* For everything else, fail the mechanism and try again */ default: purple_debug_info("sasl", "sasl_state is %d, failing the mech and trying again\n", js->sasl_state); /* * DAA: is this right? * The manpage says that "mech" will contain the chosen mechanism on success. * Presumably, if we get here that isn't the case and we shouldn't try again? * I suspect that this never happens. */ /* * SXW: Yes, this is right. What this handles is the situation where a * mechanism, say GSSAPI, is tried. If that mechanism fails, it may be * due to mechanism specific issues, so we want to try one of the other * supported mechanisms. This code handles that case */ if (mech && strlen(mech) > 0) { char *pos; if ((pos = strstr(js->sasl_mechs->str, mech))) { g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str, strlen(mech)); } again = TRUE; } sasl_dispose(&js->sasl); } } while (again); if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) { auth = xmlnode_new("auth"); xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl"); xmlnode_set_attrib(auth, "mechanism", mech); if (clientout) { if (coutlen == 0) { xmlnode_insert_data(auth, "=", -1); } else { enc_out = purple_base64_encode((unsigned char*)clientout, coutlen); xmlnode_insert_data(auth, enc_out, -1); g_free(enc_out); } } jabber_send(js, auth); xmlnode_free(auth); } else { purple_connection_error(js->gc, "SASL authentication failed\n"); }}static intjabber_sasl_cb_log(void *context, int level, const char *message){ if(level <= SASL_LOG_TRACE) purple_debug_info("sasl", "%s\n", message); return SASL_OK;}voidjabber_sasl_build_callbacks(JabberStream *js){ int id; /* Set up our callbacks structure */ if (js->sasl_cb == NULL) js->sasl_cb = g_new0(sasl_callback_t,6); id = 0; js->sasl_cb[id].id = SASL_CB_GETREALM; js->sasl_cb[id].proc = jabber_sasl_cb_realm; js->sasl_cb[id].context = (void *)js; id++; js->sasl_cb[id].id = SASL_CB_AUTHNAME; js->sasl_cb[id].proc = jabber_sasl_cb_simple; js->sasl_cb[id].context = (void *)js; id++; js->sasl_cb[id].id = SASL_CB_USER; js->sasl_cb[id].proc = jabber_sasl_cb_simple; js->sasl_cb[id].context = (void *)js; id++; if (purple_account_get_password(js->gc->account) != NULL ) { js->sasl_cb[id].id = SASL_CB_PASS; js->sasl_cb[id].proc = jabber_sasl_cb_secret; js->sasl_cb[id].context = (void *)js; id++; } js->sasl_cb[id].id = SASL_CB_LOG; js->sasl_cb[id].proc = jabber_sasl_cb_log; js->sasl_cb[id].context = (void*)js; id++; js->sasl_cb[id].id = SASL_CB_LIST_END;}#endifvoidjabber_auth_start(JabberStream *js, xmlnode *packet){#ifndef HAVE_CYRUS_SASL gboolean digest_md5 = FALSE, plain=FALSE;#endif xmlnode *mechs, *mechnode; if(js->registration) { jabber_register_start(js); return; } mechs = xmlnode_get_child(packet, "mechanisms"); if(!mechs) { purple_connection_error(js->gc, _("Invalid response from server.")); return; }#ifdef HAVE_CYRUS_SASL js->sasl_mechs = g_string_new("");#endif for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode; mechnode = xmlnode_get_next_twin(mechnode)) { char *mech_name = xmlnode_get_data(mechnode);#ifdef HAVE_CYRUS_SASL g_string_append(js->sasl_mechs, mech_name); g_string_append_c(js->sasl_mechs, ' ');#else if(mech_name && !strcmp(mech_name, "DIGEST-MD5")) digest_md5 = TRUE; else if(mech_name && !strcmp(mech_name, "PLAIN")) plain = TRUE;#endif g_free(mech_name); }#ifdef HAVE_CYRUS_SASL js->auth_type = JABBER_AUTH_CYRUS; jabber_sasl_build_callbacks(js); jabber_auth_start_cyrus(js);#else if(digest_md5) { xmlnode *auth;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -