changeset 13723:1983a8fc8e72

[gaim-migrate @ 16132] Make the ntlm code a little cleaner (to me, anyway). Add some helpful comments (to me, anyway). And hopefully fix a bug or two (hostname, domain, and username are all sent as UCS-2LE now). committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Thu, 04 May 2006 00:33:33 +0000
parents 4997b8b66206
children 65a39e436567
files src/ntlm.c
diffstat 1 files changed, 122 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/src/ntlm.c	Thu May 04 00:12:33 2006 +0000
+++ b/src/ntlm.c	Thu May 04 00:33:33 2006 +0000
@@ -34,20 +34,16 @@
 
 struct type1_message {
 	guint8  protocol[8];     /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */
-	guint8  type;            /* 0x01 */
-	guint8  zero1[3];
-	short   flags;           /* 0xb203 */
-	guint8  zero2[2];
+	guint32 type;            /* 0x00000001 */
+	guint32 flags;           /* 0x0000b203 */
 
 	short   dom_len1;        /* domain string length */
 	short   dom_len2;        /* domain string length */
-	short   dom_off;         /* domain string offset */
-	guint8  zero3[2];
+	guint32 dom_off;         /* domain string offset */
 
 	short   host_len1;       /* host string length */
 	short   host_len2;       /* host string length */
-	short   host_off;        /* host string offset (always 0x20) */
-	guint8  zero4[2];
+	guint32 host_off;        /* host string offset (always 0x00000020) */
 
 #if 0
 	guint8  host[*];         /* host string (ASCII) */
@@ -57,54 +53,49 @@
 
 struct type2_message {
 	guint8  protocol[8];     /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/
-	guint8  type;            /* 0x02 */
-	guint8  zero1[7];
-	short   msg_len;         /* 0x28 */
-	guint8  zero2[2];
-	guint32   flags;           /* 0x8201 */
+	guint32 type;            /* 0x00000002 */
+
+	short   msg_len1;        /* target name length */
+	short   msg_len2;        /* target name length */
+	guint32 msg_off;         /* target name offset (always 0x00000048) */
+
+	guint32 flags;           /* 0x00008201 */
 
 	guint8  nonce[8];        /* nonce */
-	guint8  zero[8];
+	guint8 context[8];
 };
 
 struct type3_message {
 	guint8  protocol[8];     /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/
-	guint8  type;            /* 0x03 */
-	guint8  zero1[3];
+	guint32 type;            /* 0x00000003 */
 
 	short   lm_resp_len1;    /* LanManager response length (always 0x18)*/
 	short   lm_resp_len2;    /* LanManager response length (always 0x18)*/
-	short   lm_resp_off;     /* LanManager response offset */
-	guint8  zero2[2];
+	guint32 lm_resp_off;     /* LanManager response offset */
 
 	short   nt_resp_len1;    /* NT response length (always 0x18) */
 	short   nt_resp_len2;    /* NT response length (always 0x18) */
-	short   nt_resp_off;     /* NT response offset */
-	guint8  zero3[2];
+	guint32 nt_resp_off;     /* NT response offset */
 
 	short   dom_len1;        /* domain string length */
 	short   dom_len2;        /* domain string length */
-	short   dom_off;         /* domain string offset (always 0x40) */
-	guint8  zero4[2];
+	guint32 dom_off;         /* domain string offset (always 0x00000040) */
 
 	short   user_len1;       /* username string length */
 	short   user_len2;       /* username string length */
-	short   user_off;        /* username string offset */
-	guint8  zero5[2];
+	guint32 user_off;        /* username string offset */
 
 	short   host_len1;       /* host string length */
 	short   host_len2;       /* host string length */
-	short   host_off;        /* host string offset */
-	guint8  zero6[2];
+	guint32 host_off;        /* host string offset */
 
 	short   sess_len1;
 	short	sess_len2;
-	short   sess_off;         /* message length */
-	guint8  zero7[2];
+	guint32 sess_off;         /* message length */
 
-	guint32   flags;           /* 0x8201 */
-/*	guint32  flags2;  unknown, used in windows messenger
-	guint32  flags3; */
+	guint32 flags;            /* 0x00008201 */
+	/* guint32 flags2; */     /* unknown, used in windows messenger */
+	/* guint32  flags3; */
 
 #if 0
 	guint8  dom[*];          /* domain string (unicode UTF-16LE) */
@@ -119,9 +110,16 @@
 gchar *
 gaim_ntlm_gen_type1(const gchar *hostname, const gchar *domain)
 {
+	int hostnamelen;
+	int domainlen;
+	unsigned char *msg;
+	struct type1_message *tmsg;
 	gchar *tmp;
-	char *msg = g_malloc0(sizeof(struct type1_message) + strlen(hostname) + strlen(domain));
-	struct type1_message *tmsg = (struct type1_message*)msg;
+
+	hostnamelen = strlen(hostname);
+	domainlen = strlen(domain);
+	msg = g_malloc0(sizeof(struct type1_message) + hostnamelen + domainlen);
+	tmsg = (struct type1_message*)msg;
 	tmsg->protocol[0] = 'N';
 	tmsg->protocol[1] = 'T';
 	tmsg->protocol[2] = 'L';
@@ -130,17 +128,18 @@
 	tmsg->protocol[5] = 'S';
 	tmsg->protocol[6] = 'P';
 	tmsg->protocol[7] = '\0';
-	tmsg->type= 0x01;
-	tmsg->flags = 0xb202;
-	tmsg->dom_len1 = tmsg->dom_len2 = strlen(domain);
-	tmsg->dom_off = 32+strlen(hostname);
-	tmsg->host_len1 = tmsg->host_len2 = strlen(hostname);
-	tmsg->host_off= 32;
-	memcpy(msg+sizeof(struct type1_message),hostname,strlen(hostname));
-	memcpy(msg+sizeof(struct type1_message)+strlen(hostname),domain,strlen(domain));
+	tmsg->type      = 0x00000001;
+	tmsg->flags     = 0x0000b202;
+	tmsg->dom_len1  = tmsg->dom_len2 = domainlen;
+	tmsg->dom_off   = sizeof(struct type1_message) + hostnamelen;
+	tmsg->host_len1 = tmsg->host_len2 = hostnamelen;
+	tmsg->host_off  = sizeof(struct type1_message);
+	memcpy(msg + tmsg->host_off, hostname, hostnamelen);
+	memcpy(msg + tmsg->dom_off, domain, domainlen);
 
-	tmp = gaim_base64_encode((guchar*)msg, sizeof(struct type1_message) + strlen(hostname) + strlen(domain));
+	tmp = gaim_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen);
 	g_free(msg);
+
 	return tmp;
 }
 
@@ -148,11 +147,15 @@
 gaim_ntlm_parse_type2(const gchar *type2, guint32 *flags)
 {
 	gsize retlen;
+	struct type2_message *tmsg;
 	static guint8 nonce[8];
-	struct type2_message *tmsg = (struct type2_message*)gaim_base64_decode((char*)type2, &retlen);
+
+	tmsg = (struct type2_message*)gaim_base64_decode(type2, &retlen);
 	memcpy(nonce, tmsg->nonce, 8);
-	if(flags) *flags = tmsg->flags;
+	if (flags != NULL)
+		*flags = tmsg->flags;
 	g_free(tmsg);
+
 	return nonce;
 }
 
@@ -177,7 +180,7 @@
  * helper function for gaim cipher.c
  */
 static void
-des_ecb_encrypt(const guint8 *plaintext, char *result, const guint8 *key)
+des_ecb_encrypt(const guint8 *plaintext, guint8 *result, const guint8 *key)
 {
 	GaimCipher *cipher;
 	GaimCipherContext *context;
@@ -186,7 +189,7 @@
 	cipher = gaim_ciphers_find_cipher("des");
 	context = gaim_cipher_context_new(cipher, NULL);
 	gaim_cipher_context_set_key(context, key);
-	gaim_cipher_context_encrypt(context, (guchar*)plaintext, 8, (guchar*)result, &outlen);
+	gaim_cipher_context_encrypt(context, plaintext, 8, result, &outlen);
 	gaim_cipher_context_destroy(context);
 }
 
@@ -200,13 +203,13 @@
 {
 	guint8 key[8];
 	setup_des_key(keys, key);
-	des_ecb_encrypt(plaintext, (char*)results, key);
+	des_ecb_encrypt(plaintext, results, key);
 
-	setup_des_key(keys+7, key);
-	des_ecb_encrypt(plaintext, (char*)(results+8), key);
+	setup_des_key(keys + 7, key);
+	des_ecb_encrypt(plaintext, results + 8, key);
 
-	setup_des_key(keys+14, key);
-	des_ecb_encrypt(plaintext, (char*)(results+16), key);
+	setup_des_key(keys + 14, key);
+	des_ecb_encrypt(plaintext, results + 16, key);
 }
 
 static void
@@ -225,24 +228,33 @@
 gchar *
 gaim_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags)
 {
-	char  lm_pw[14];
+	char lm_pw[14];
 	unsigned char lm_hpw[21];
 	char sesskey[16];
 	guint8 key[8];
-	int msglen = sizeof(struct type3_message)+
-		strlen(domain) + strlen(username)+
-		strlen(hostname) + 24 +24 + ((flags) ? 16 : 0);
-	struct type3_message *tmsg = g_malloc0(msglen);
-	int   len = strlen(passw);
+	int domainlen;
+	int usernamelen;
+	int hostnamelen;
+	int msglen;
+	struct type3_message *tmsg;
+	int passwlen, lennt;
 	unsigned char lm_resp[24], nt_resp[24];
 	unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
 	unsigned char nt_hpw[21];
-	int lennt;
-	char  nt_pw[128];
+	char nt_pw[128];
 	GaimCipher *cipher;
 	GaimCipherContext *context;
-	char *tmp = 0;
-	int idx = 0;
+	char *tmp;
+	int idx;
+	gchar *ucs2le;
+
+	domainlen = strlen(domain) * 2;
+	usernamelen = strlen(username) * 2;
+	hostnamelen = strlen(hostname) * 2;
+	msglen = sizeof(struct type3_message) + domainlen +
+		usernamelen + hostnamelen + 0x18 + 0x18 + ((flags) ? 0x10 : 0);
+	tmsg = g_malloc0(msglen);
+	passwlen = strlen(passw);
 
 	/* type3 message initialization */
 	tmsg->protocol[0] = 'N';
@@ -252,79 +264,87 @@
 	tmsg->protocol[4] = 'S';
 	tmsg->protocol[5] = 'S';
 	tmsg->protocol[6] = 'P';
-	tmsg->type = 0x03;
+	tmsg->type = 0x00000003;
 	tmsg->lm_resp_len1 = tmsg->lm_resp_len2 = 0x18;
-	tmsg->lm_resp_off = sizeof(struct type3_message) + strlen(domain) + strlen(username) + strlen(hostname);
+	tmsg->lm_resp_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen;
 	tmsg->nt_resp_len1 = tmsg->nt_resp_len2 = 0x18;
-	tmsg->nt_resp_off = sizeof(struct type3_message) + strlen(domain) + strlen(username) + strlen(hostname) + 0x18;
+	tmsg->nt_resp_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18;
 
-	tmsg->dom_len1 = tmsg->dom_len2 = strlen(domain);
-	tmsg->dom_off = 0x40;
+	tmsg->dom_len1 = tmsg->dom_len2 = domainlen;
+	tmsg->dom_off = sizeof(struct type3_message);
 
-	tmsg->user_len1 = tmsg->user_len2 = strlen(username);
-	tmsg->user_off = sizeof(struct type3_message) + strlen(domain);
+	tmsg->user_len1 = tmsg->user_len2 = usernamelen;
+	tmsg->user_off = sizeof(struct type3_message) + domainlen;
 
-	tmsg->host_len1 = tmsg->host_len2 = strlen(hostname);
-	tmsg->host_off = sizeof(struct type3_message) + strlen(domain) + strlen(username);
+	tmsg->host_len1 = tmsg->host_len2 = hostnamelen;
+	tmsg->host_off = sizeof(struct type3_message) + domainlen + usernamelen;
 
 	if(flags) {
-		tmsg->sess_off = sizeof(struct type3_message) + strlen(domain) + strlen(username) + strlen(hostname) + 0x18 + 0x18;
-		tmsg->sess_len1 = tmsg->sess_len2 = 0x10;
+		tmsg->sess_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18 + 0x18;
+		tmsg->sess_len1 = tmsg->sess_len2 = 0x0010;
 	}
 
-	tmsg->flags = 0x8200;
+	tmsg->flags = 0x00008200;
+
+	tmp = (char *)tmsg + sizeof(struct type3_message);
+
+	ucs2le = g_convert(domain, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
+	memcpy(tmp, ucs2le, domainlen);
+	g_free(ucs2le);
+	tmp += domainlen;
 
-	tmp = ((char*) tmsg) + sizeof(struct type3_message);
-	strcpy(tmp, domain);
-	tmp += strlen(domain);
-	strcpy(tmp, username);
-	tmp += strlen(username);
-	strcpy(tmp, hostname);
-	tmp += strlen(hostname);
+	ucs2le = g_convert(username, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
+	memcpy(tmp, ucs2le, usernamelen);
+	g_free(ucs2le);
+	tmp += usernamelen;
+
+	ucs2le = g_convert(hostname, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL);
+	memcpy(tmp, ucs2le, hostnamelen);
+	g_free(ucs2le);
+	tmp += hostnamelen;
 
 	/* LM */
-	if (len > 14)  len = 14;
+	if (passwlen > 14)
+		passwlen = 14;
 
-	for (idx=0; idx<len; idx++)
+	for (idx = 0; idx < passwlen; idx++)
 		lm_pw[idx] = g_ascii_toupper(passw[idx]);
-	for (; idx<14; idx++)
+	for (; idx < 14; idx++)
 		lm_pw[idx] = 0;
 
 	setup_des_key((unsigned char*)lm_pw, key);
-	des_ecb_encrypt(magic, (char*)lm_hpw, key);
+	des_ecb_encrypt(magic, lm_hpw, key);
+
+	setup_des_key((unsigned char*)(lm_pw + 7), key);
+	des_ecb_encrypt(magic, lm_hpw + 8, key);
 
-	setup_des_key((unsigned char*)(lm_pw+7), key);
-	des_ecb_encrypt(magic, (char*)lm_hpw+8, key);
-
-	memset(lm_hpw+16, 0, 5);
+	memset(lm_hpw + 16, 0, 5);
 	calc_resp(lm_hpw, nonce, lm_resp);
+	memcpy(tmp, lm_resp, 0x18);
+	tmp += 0x18;
 
 	/* NTLM */
+	/* Convert the password to UCS-2LE */
 	lennt = strlen(passw);
-	for (idx=0; idx<lennt; idx++)
+	for (idx = 0; idx < lennt; idx++)
 	{
-		nt_pw[2*idx]   = passw[idx];
-		nt_pw[2*idx+1] = 0;
+		nt_pw[2 * idx]   = passw[idx];
+		nt_pw[2 * idx + 1] = 0;
 	}
 
 	cipher = gaim_ciphers_find_cipher("md4");
 	context = gaim_cipher_context_new(cipher, NULL);
-	gaim_cipher_context_append(context, (guchar*)nt_pw, 2*lennt);
-	gaim_cipher_context_digest(context, 21, (guchar*)nt_hpw, NULL);
+	gaim_cipher_context_append(context, (guint8 *)nt_pw, 2 * lennt);
+	gaim_cipher_context_digest(context, 21, nt_hpw, NULL);
 	gaim_cipher_context_destroy(context);
 
-	memset(nt_hpw+16, 0, 5);
-
-
+	memset(nt_hpw + 16, 0, 5);
 	calc_resp(nt_hpw, nonce, nt_resp);
-	memcpy(tmp, lm_resp, 0x18);
-	tmp += 0x18;
 	memcpy(tmp, nt_resp, 0x18);
 	tmp += 0x18;
 
-
 	/* LCS Stuff */
-	if(flags) {
+	if (flags) {
 		tmsg->flags = 0x409082d4;
 		gensesskey(sesskey, NULL);
 		memcpy(tmp, sesskey, 0x10);
@@ -333,7 +353,8 @@
 	/*tmsg->flags2 = 0x0a280105;
 	tmsg->flags3 = 0x0f000000;*/
 
-	tmp = gaim_base64_encode((guchar*) tmsg, msglen);
+	tmp = gaim_base64_encode((guchar *)tmsg, msglen);
 	g_free(tmsg);
+
 	return tmp;
 }