⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 auth.c

📁 Linux下的多协议即时通讯程序源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -