/pidgin/main: 0105b155a3d1: oscar: Add kerberos authentication m...
Youness Alaoui
kakaroto at kakaroto.homelinux.net
Fri Jun 17 19:11:41 EDT 2016
Changeset: 0105b155a3d1f0f7393e1931e01d45513abcf031
Author: Youness Alaoui <kakaroto at kakaroto.homelinux.net>
Date: 2016-05-06 16:22 -0400
Branch: oscar_auth
URL: https://hg.pidgin.im/pidgin/main/rev/0105b155a3d1
Description:
oscar: Add kerberos authentication method support for AIM
This doesn't actually use kerberos to authenticate to the chat server,
but it uses the kerberos KDC server to authenticate and it receives
the authentication cookie for the IM service at the same time.
We simply don't use the KDC ticket to authenticate for the website.
diffstat:
libpurple/protocols/oscar/Makefile.am | 1 +
libpurple/protocols/oscar/kerberos.c | 468 ++++++++++++++++++++++++++++++++
libpurple/protocols/oscar/oscar.c | 66 ++++-
libpurple/protocols/oscar/oscar.h | 6 +
libpurple/protocols/oscar/oscarcommon.h | 4 +
5 files changed, 540 insertions(+), 5 deletions(-)
diffs (truncated from 645 to 300 lines):
diff --git a/libpurple/protocols/oscar/Makefile.am b/libpurple/protocols/oscar/Makefile.am
--- a/libpurple/protocols/oscar/Makefile.am
+++ b/libpurple/protocols/oscar/Makefile.am
@@ -11,6 +11,7 @@ OSCARSOURCES = \
aim.h \
bstream.c \
clientlogin.c \
+ kerberos.c \
encoding.c \
encoding.h \
family_admin.c \
diff --git a/libpurple/protocols/oscar/kerberos.c b/libpurple/protocols/oscar/kerberos.c
new file mode 100644
--- /dev/null
+++ b/libpurple/protocols/oscar/kerberos.c
@@ -0,0 +1,468 @@
+/*
+ * Purple's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+*/
+
+/**
+ * This file implements AIM's kerberos procedure for authenticating
+ * users. This replaces the older MD5-based and XOR-based
+ * authentication methods that use SNAC family 0x0017.
+ *
+ * This doesn't use SNACs or FLAPs at all. It makes http and https
+ * POSTs to AOL to validate the user based on the password they
+ * provided to us. Upon successful authentication we request a
+ * connection to the BOS server by calling startOSCARsession. The
+ * AOL server gives us the hostname and port number to use, as well
+ * as the cookie to use to authenticate to the BOS server. And then
+ * everything else is the same as with BUCP.
+ *
+ * For details, see:
+ * http://dev.aol.com/aim/oscar/#AUTH
+ * http://dev.aol.com/authentication_for_clients
+ */
+
+#include "oscar.h"
+#include "oscarcommon.h"
+#include "core.h"
+
+#include "ciphers/hmaccipher.h"
+#include "ciphers/sha256hash.h"
+
+
+#define MAXAIMPASSLEN 16
+
+/*
+ * Incomplete X-SNAC format taken from reverse engineering doen by digsby:
+ * https://github.com/ifwe/digsby/blob/master/digsby/src/oscar/login2.py
+ */
+typedef struct {
+ aim_tlv_t *main_tlv;
+ gchar *principal1;
+ gchar *service;
+ gchar *principal1_again;
+ gchar *principal2;
+ gchar unknown;
+ guint8 *footer;
+ struct {
+ guint32 unknown1;
+ guint32 unknown2;
+ guint32 epoch_now;
+ guint32 epoch_valid;
+ guint32 epoch_renew;
+ guint32 epoch_expire;
+ guint32 unknown3;
+ guint32 unknown4;
+ guint32 unknown5;
+ } dates;
+ GSList *tlvlist;
+} aim_xsnac_token_t;
+
+typedef struct {
+ guint16 family;
+ guint16 subtype;
+ guint8 flags[8];
+ guint16 request_id;
+ guint32 epoch;
+ guint32 unknown;
+ gchar *principal1;
+ gchar *principal2;
+ guint16 num_tokens;
+ aim_xsnac_token_t *tokens;
+ GSList *tlvlist;
+} aim_xsnac_t;
+
+static gchar *get_kdc_url(OscarData *od)
+{
+ PurpleAccount *account = purple_connection_get_account(od->gc);
+ const gchar *server;
+ gchar *url;
+ gchar *port_str = NULL;
+ gint port;
+
+ server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
+ port = purple_account_get_int(account, "port", AIM_DEFAULT_KDC_PORT);
+ if (port != 443)
+ port_str = g_strdup_printf (":%d", port);
+ url = g_strdup_printf ("https://%s%s/", server, port_str ? port_str : "");
+ if (port_str)
+ g_free (port_str);
+
+ return url;
+}
+
+/*
+ * Using clientLogin requires a developer ID. This key is for libpurple.
+ * It is the default key for all libpurple-based clients. AOL encourages
+ * UIs (especially ones with lots of users) to override this with their
+ * own key. This key is owned by the AIM account "markdoliner"
+ *
+ * Keys can be managed at http://developer.aim.com/manageKeys.jsp
+ */
+#define DEFAULT_CLIENT_KEY "ma15d7JTxbmVG-RP"
+
+static const char *get_client_key(OscarData *od)
+{
+ return oscar_get_ui_info_string(
+ od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey",
+ DEFAULT_CLIENT_KEY);
+}
+
+static void
+aim_encode_password(const char *password, guint8 *encoded)
+{
+ guint8 encoding_table[] = {
+ 0x76, 0x91, 0xc5, 0xe7,
+ 0xd0, 0xd9, 0x95, 0xdd,
+ 0x9e, 0x2F, 0xea, 0xd8,
+ 0x6B, 0x21, 0xc2, 0xbc,
+
+ };
+ guint i;
+
+ /*
+ * We truncate AIM passwords to 16 characters since that's what
+ * the official client does as well.
+ */
+ for (i = 0; i < strlen(password) && i < MAXAIMPASSLEN; i++)
+ encoded[i] = (password[i] ^ encoding_table[i]);
+}
+
+static void
+aim_xsnac_free(aim_xsnac_t *xsnac)
+{
+ gint i;
+
+ if (xsnac->principal1)
+ g_free (xsnac->principal1);
+ if (xsnac->principal2)
+ g_free (xsnac->principal2);
+ aim_tlvlist_free (xsnac->tlvlist);
+
+ for (i = 0; i < xsnac->num_tokens; i++) {
+ g_free(xsnac->tokens[i].main_tlv->value);
+ g_free(xsnac->tokens[i].main_tlv);
+ if (xsnac->tokens[i].principal1)
+ g_free (xsnac->tokens[i].principal1);
+ if (xsnac->tokens[i].principal1_again)
+ if (xsnac->tokens[i].service)
+ g_free (xsnac->tokens[i].service);
+ g_free (xsnac->tokens[i].principal1_again);
+ if (xsnac->tokens[i].principal2)
+ g_free (xsnac->tokens[i].principal2);
+ if (xsnac->tokens[i].footer)
+ g_free (xsnac->tokens[i].footer);
+ aim_tlvlist_free (xsnac->tokens[i].tlvlist);
+ }
+ g_free (xsnac->tokens);
+}
+
+static void
+kerberos_login_cb(PurpleHttpConnection *http_conn,
+ PurpleHttpResponse *response, gpointer _od)
+{
+ OscarData *od = _od;
+ PurpleConnection *gc;
+ const gchar *got_data;
+ size_t got_len;
+ ByteStream bs;
+ aim_xsnac_t xsnac = {0};
+ guint16 len;
+ gchar *bosip = NULL;
+ gchar *tlsCertName = NULL;
+ guint8 *cookie = NULL;
+ guint32 cookie_len = 0;
+ char *host; int port;
+ gsize i;
+
+ gc = od->gc;
+
+ od->hc = NULL;
+
+ if (!purple_http_response_is_successful(response)) {
+ gchar *tmp;
+ gchar *url;
+
+ url = get_kdc_url(od);
+ tmp = g_strdup_printf(_("Error requesting %s: %s"),
+ url,
+ purple_http_response_get_error(response));
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ g_free(tmp);
+ g_free(url);
+ return;
+ }
+
+ got_data = purple_http_response_get_data(response, &got_len);
+ purple_debug_info("oscar", "Received kerberos login HTTP response %lu : ", got_len);
+
+ byte_stream_init (&bs, (guint8 *)got_data, got_len);
+
+ xsnac.family = byte_stream_get16 (&bs);
+ xsnac.subtype = byte_stream_get16(&bs);
+ byte_stream_getrawbuf(&bs, (guint8 *) xsnac.flags, 8);
+
+ if (xsnac.family == 0x50C && xsnac.subtype == 0x0005) {
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Incorrect password"));
+ return;
+ }
+ if (xsnac.family != 0x50C || xsnac.subtype != 0x0003) {
+ purple_connection_error(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Error parsing response from authentication server"));
+ return;
+ }
+ xsnac.request_id = byte_stream_get16(&bs);
+ xsnac.epoch = byte_stream_get32(&bs);
+ xsnac.unknown = byte_stream_get32(&bs);
+ len = byte_stream_get16(&bs);
+ xsnac.principal1 = byte_stream_getstr(&bs, len);
+ len = byte_stream_get16(&bs);
+ xsnac.principal2 = byte_stream_getstr(&bs, len);
+ xsnac.num_tokens = byte_stream_get16(&bs);
+
+ purple_debug_info("oscar", "KDC: %d tokens between '%s' and '%s'\n",
+ xsnac.num_tokens, xsnac.principal1, xsnac.principal2);
+ xsnac.tokens = g_new0 (aim_xsnac_token_t, xsnac.num_tokens);
+ for (i = 0; i < xsnac.num_tokens; i++) {
+ GSList *tlv;
+
+ tlv = aim_tlvlist_readnum(&bs, 1);
+ if (tlv)
+ xsnac.tokens[i].main_tlv = tlv->data;
+ g_slist_free (tlv);
+
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].principal1 = byte_stream_getstr(&bs, len);
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].service = byte_stream_getstr(&bs, len);
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].principal1_again = byte_stream_getstr(&bs, len);
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].principal2 = byte_stream_getstr(&bs, len);
+ xsnac.tokens[i].unknown = byte_stream_get8(&bs);
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].footer = byte_stream_getraw(&bs, len);
+
+ xsnac.tokens[i].dates.unknown1 = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.unknown2 = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.epoch_now = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.epoch_valid = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.epoch_renew = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.epoch_expire = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.unknown3 = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.unknown4 = byte_stream_get32(&bs);
+ xsnac.tokens[i].dates.unknown5 = byte_stream_get32(&bs);
+
+ len = byte_stream_get16(&bs);
+ xsnac.tokens[i].tlvlist = aim_tlvlist_readnum(&bs, len);
+
+ purple_debug_info("oscar", "Token %lu has %d TLVs for service '%s'\n",
+ i, len, xsnac.tokens[i].service);
+ }
+ len = byte_stream_get16(&bs);
+ xsnac.tlvlist = aim_tlvlist_readnum(&bs, len);
+
+ for (i = 0; i < xsnac.num_tokens; i++) {
+ if (strcmp (xsnac.tokens[i].service, "im/boss") == 0) {
More information about the Commits
mailing list