Mercurial > pidgin
annotate src/ntlm.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 1983a8fc8e72 |
children |
rev | line source |
---|---|
11375 | 1 /** |
2 * @file ntlm.c | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> | |
7 * | |
8 * hashing done according to description of NTLM on | |
9 * http://www.innovation.ch/java/ntlm.html | |
10 * | |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
24 */ | |
25 | |
26 #include <glib.h> | |
27 #include <stdlib.h> | |
28 #include "util.h" | |
29 #include "ntlm.h" | |
30 #include "cipher.h" | |
31 #include <string.h> | |
32 | |
13084 | 33 #define NTLM_NEGOTIATE_NTLM2_KEY 0x00080000 |
34 | |
11375 | 35 struct type1_message { |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
36 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */ |
13723 | 37 guint32 type; /* 0x00000001 */ |
38 guint32 flags; /* 0x0000b203 */ | |
11375 | 39 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
40 short dom_len1; /* domain string length */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
41 short dom_len2; /* domain string length */ |
13723 | 42 guint32 dom_off; /* domain string offset */ |
11375 | 43 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
44 short host_len1; /* host string length */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
45 short host_len2; /* host string length */ |
13723 | 46 guint32 host_off; /* host string offset (always 0x00000020) */ |
11375 | 47 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
48 #if 0 |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
49 guint8 host[*]; /* host string (ASCII) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
50 guint8 dom[*]; /* domain string (ASCII) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
51 #endif |
11375 | 52 }; |
53 | |
54 struct type2_message { | |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
55 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ |
13723 | 56 guint32 type; /* 0x00000002 */ |
57 | |
58 short msg_len1; /* target name length */ | |
59 short msg_len2; /* target name length */ | |
60 guint32 msg_off; /* target name offset (always 0x00000048) */ | |
61 | |
62 guint32 flags; /* 0x00008201 */ | |
11375 | 63 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
64 guint8 nonce[8]; /* nonce */ |
13723 | 65 guint8 context[8]; |
11375 | 66 }; |
67 | |
68 struct type3_message { | |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
69 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ |
13723 | 70 guint32 type; /* 0x00000003 */ |
11375 | 71 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
72 short lm_resp_len1; /* LanManager response length (always 0x18)*/ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
73 short lm_resp_len2; /* LanManager response length (always 0x18)*/ |
13723 | 74 guint32 lm_resp_off; /* LanManager response offset */ |
11375 | 75 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
76 short nt_resp_len1; /* NT response length (always 0x18) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
77 short nt_resp_len2; /* NT response length (always 0x18) */ |
13723 | 78 guint32 nt_resp_off; /* NT response offset */ |
11375 | 79 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
80 short dom_len1; /* domain string length */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
81 short dom_len2; /* domain string length */ |
13723 | 82 guint32 dom_off; /* domain string offset (always 0x00000040) */ |
11375 | 83 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
84 short user_len1; /* username string length */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
85 short user_len2; /* username string length */ |
13723 | 86 guint32 user_off; /* username string offset */ |
11375 | 87 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
88 short host_len1; /* host string length */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
89 short host_len2; /* host string length */ |
13723 | 90 guint32 host_off; /* host string offset */ |
11375 | 91 |
13084 | 92 short sess_len1; |
93 short sess_len2; | |
13723 | 94 guint32 sess_off; /* message length */ |
11375 | 95 |
13723 | 96 guint32 flags; /* 0x00008201 */ |
97 /* guint32 flags2; */ /* unknown, used in windows messenger */ | |
98 /* guint32 flags3; */ | |
11375 | 99 |
11829
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
100 #if 0 |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
101 guint8 dom[*]; /* domain string (unicode UTF-16LE) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
102 guint8 user[*]; /* username string (unicode UTF-16LE) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
103 guint8 host[*]; /* host string (unicode UTF-16LE) */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
104 guint8 lm_resp[*]; /* LanManager response */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
105 guint8 nt_resp[*]; /* NT response */ |
4669e7461968
[gaim-migrate @ 14120]
Richard Laager <rlaager@wiktel.com>
parents:
11586
diff
changeset
|
106 #endif |
11375 | 107 }; |
108 | |
13699 | 109 /* TODO: Will this work on both little-endian and big-endian machines? */ |
13677 | 110 gchar * |
111 gaim_ntlm_gen_type1(const gchar *hostname, const gchar *domain) | |
112 { | |
13723 | 113 int hostnamelen; |
114 int domainlen; | |
115 unsigned char *msg; | |
116 struct type1_message *tmsg; | |
13704 | 117 gchar *tmp; |
13723 | 118 |
119 hostnamelen = strlen(hostname); | |
120 domainlen = strlen(domain); | |
121 msg = g_malloc0(sizeof(struct type1_message) + hostnamelen + domainlen); | |
122 tmsg = (struct type1_message*)msg; | |
11375 | 123 tmsg->protocol[0] = 'N'; |
124 tmsg->protocol[1] = 'T'; | |
125 tmsg->protocol[2] = 'L'; | |
126 tmsg->protocol[3] = 'M'; | |
127 tmsg->protocol[4] = 'S'; | |
128 tmsg->protocol[5] = 'S'; | |
129 tmsg->protocol[6] = 'P'; | |
130 tmsg->protocol[7] = '\0'; | |
13723 | 131 tmsg->type = 0x00000001; |
132 tmsg->flags = 0x0000b202; | |
133 tmsg->dom_len1 = tmsg->dom_len2 = domainlen; | |
134 tmsg->dom_off = sizeof(struct type1_message) + hostnamelen; | |
135 tmsg->host_len1 = tmsg->host_len2 = hostnamelen; | |
136 tmsg->host_off = sizeof(struct type1_message); | |
137 memcpy(msg + tmsg->host_off, hostname, hostnamelen); | |
138 memcpy(msg + tmsg->dom_off, domain, domainlen); | |
11375 | 139 |
13723 | 140 tmp = gaim_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen); |
13704 | 141 g_free(msg); |
13723 | 142 |
13704 | 143 return tmp; |
11375 | 144 } |
145 | |
13698 | 146 guint8 * |
13677 | 147 gaim_ntlm_parse_type2(const gchar *type2, guint32 *flags) |
148 { | |
11586 | 149 gsize retlen; |
13723 | 150 struct type2_message *tmsg; |
13698 | 151 static guint8 nonce[8]; |
13723 | 152 |
153 tmsg = (struct type2_message*)gaim_base64_decode(type2, &retlen); | |
11375 | 154 memcpy(nonce, tmsg->nonce, 8); |
13723 | 155 if (flags != NULL) |
156 *flags = tmsg->flags; | |
11375 | 157 g_free(tmsg); |
13723 | 158 |
11375 | 159 return nonce; |
160 } | |
161 | |
13699 | 162 /** |
163 * Create a 64bit DES key by taking a 56bit key and adding | |
164 * a parity bit after every 7th bit. | |
165 */ | |
13677 | 166 static void |
13699 | 167 setup_des_key(const guint8 key_56[], guint8 *key) |
11375 | 168 { |
169 key[0] = key_56[0]; | |
170 key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); | |
171 key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); | |
172 key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); | |
173 key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); | |
174 key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); | |
175 key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); | |
176 key[7] = (key_56[6] << 1) & 0xFF; | |
177 } | |
178 | |
179 /* | |
180 * helper function for gaim cipher.c | |
181 */ | |
13677 | 182 static void |
13723 | 183 des_ecb_encrypt(const guint8 *plaintext, guint8 *result, const guint8 *key) |
13677 | 184 { |
11375 | 185 GaimCipher *cipher; |
186 GaimCipherContext *context; | |
11586 | 187 gsize outlen; |
13677 | 188 |
11375 | 189 cipher = gaim_ciphers_find_cipher("des"); |
190 context = gaim_cipher_context_new(cipher, NULL); | |
13699 | 191 gaim_cipher_context_set_key(context, key); |
13723 | 192 gaim_cipher_context_encrypt(context, plaintext, 8, result, &outlen); |
11375 | 193 gaim_cipher_context_destroy(context); |
194 } | |
195 | |
196 /* | |
197 * takes a 21 byte array and treats it as 3 56-bit DES keys. The | |
198 * 8 byte plaintext is encrypted with each key and the resulting 24 | |
199 * bytes are stored in the results array. | |
200 */ | |
13677 | 201 static void |
13699 | 202 calc_resp(guint8 *keys, const guint8 *plaintext, unsigned char *results) |
11375 | 203 { |
13699 | 204 guint8 key[8]; |
205 setup_des_key(keys, key); | |
13723 | 206 des_ecb_encrypt(plaintext, results, key); |
11375 | 207 |
13723 | 208 setup_des_key(keys + 7, key); |
209 des_ecb_encrypt(plaintext, results + 8, key); | |
11375 | 210 |
13723 | 211 setup_des_key(keys + 14, key); |
212 des_ecb_encrypt(plaintext, results + 16, key); | |
11375 | 213 } |
214 | |
13677 | 215 static void |
216 gensesskey(char *buffer, const char *oldkey) | |
217 { | |
13087 | 218 int i = 0; |
219 if(oldkey == NULL) { | |
220 for(i=0; i<16; i++) { | |
221 buffer[i] = (char)(rand() & 0xff); | |
222 } | |
223 } else { | |
224 memcpy(buffer, oldkey, 16); | |
225 } | |
226 } | |
13084 | 227 |
13088 | 228 gchar * |
13698 | 229 gaim_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags) |
13088 | 230 { |
13723 | 231 char lm_pw[14]; |
11375 | 232 unsigned char lm_hpw[21]; |
13087 | 233 char sesskey[16]; |
13699 | 234 guint8 key[8]; |
13723 | 235 int domainlen; |
236 int usernamelen; | |
237 int hostnamelen; | |
238 int msglen; | |
239 struct type3_message *tmsg; | |
240 int passwlen, lennt; | |
11375 | 241 unsigned char lm_resp[24], nt_resp[24]; |
242 unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; | |
243 unsigned char nt_hpw[21]; | |
13723 | 244 char nt_pw[128]; |
11375 | 245 GaimCipher *cipher; |
246 GaimCipherContext *context; | |
13723 | 247 char *tmp; |
248 int idx; | |
249 gchar *ucs2le; | |
250 | |
251 domainlen = strlen(domain) * 2; | |
252 usernamelen = strlen(username) * 2; | |
253 hostnamelen = strlen(hostname) * 2; | |
254 msglen = sizeof(struct type3_message) + domainlen + | |
255 usernamelen + hostnamelen + 0x18 + 0x18 + ((flags) ? 0x10 : 0); | |
256 tmsg = g_malloc0(msglen); | |
257 passwlen = strlen(passw); | |
11375 | 258 |
13088 | 259 /* type3 message initialization */ |
11375 | 260 tmsg->protocol[0] = 'N'; |
261 tmsg->protocol[1] = 'T'; | |
262 tmsg->protocol[2] = 'L'; | |
263 tmsg->protocol[3] = 'M'; | |
264 tmsg->protocol[4] = 'S'; | |
265 tmsg->protocol[5] = 'S'; | |
266 tmsg->protocol[6] = 'P'; | |
13723 | 267 tmsg->type = 0x00000003; |
11375 | 268 tmsg->lm_resp_len1 = tmsg->lm_resp_len2 = 0x18; |
13723 | 269 tmsg->lm_resp_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen; |
11375 | 270 tmsg->nt_resp_len1 = tmsg->nt_resp_len2 = 0x18; |
13723 | 271 tmsg->nt_resp_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18; |
11375 | 272 |
13723 | 273 tmsg->dom_len1 = tmsg->dom_len2 = domainlen; |
274 tmsg->dom_off = sizeof(struct type3_message); | |
11375 | 275 |
13723 | 276 tmsg->user_len1 = tmsg->user_len2 = usernamelen; |
277 tmsg->user_off = sizeof(struct type3_message) + domainlen; | |
11375 | 278 |
13723 | 279 tmsg->host_len1 = tmsg->host_len2 = hostnamelen; |
280 tmsg->host_off = sizeof(struct type3_message) + domainlen + usernamelen; | |
11375 | 281 |
13084 | 282 if(flags) { |
13723 | 283 tmsg->sess_off = sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18 + 0x18; |
284 tmsg->sess_len1 = tmsg->sess_len2 = 0x0010; | |
13084 | 285 } |
286 | |
13723 | 287 tmsg->flags = 0x00008200; |
288 | |
289 tmp = (char *)tmsg + sizeof(struct type3_message); | |
290 | |
291 ucs2le = g_convert(domain, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL); | |
292 memcpy(tmp, ucs2le, domainlen); | |
293 g_free(ucs2le); | |
294 tmp += domainlen; | |
11375 | 295 |
13723 | 296 ucs2le = g_convert(username, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL); |
297 memcpy(tmp, ucs2le, usernamelen); | |
298 g_free(ucs2le); | |
299 tmp += usernamelen; | |
300 | |
301 ucs2le = g_convert(hostname, -1, "UCS-2LE", "UTF-8", NULL, NULL, NULL); | |
302 memcpy(tmp, ucs2le, hostnamelen); | |
303 g_free(ucs2le); | |
304 tmp += hostnamelen; | |
13088 | 305 |
13084 | 306 /* LM */ |
13723 | 307 if (passwlen > 14) |
308 passwlen = 14; | |
13084 | 309 |
13723 | 310 for (idx = 0; idx < passwlen; idx++) |
11375 | 311 lm_pw[idx] = g_ascii_toupper(passw[idx]); |
13723 | 312 for (; idx < 14; idx++) |
11375 | 313 lm_pw[idx] = 0; |
314 | |
13699 | 315 setup_des_key((unsigned char*)lm_pw, key); |
13723 | 316 des_ecb_encrypt(magic, lm_hpw, key); |
317 | |
318 setup_des_key((unsigned char*)(lm_pw + 7), key); | |
319 des_ecb_encrypt(magic, lm_hpw + 8, key); | |
11375 | 320 |
13723 | 321 memset(lm_hpw + 16, 0, 5); |
13698 | 322 calc_resp(lm_hpw, nonce, lm_resp); |
13723 | 323 memcpy(tmp, lm_resp, 0x18); |
324 tmp += 0x18; | |
11375 | 325 |
13084 | 326 /* NTLM */ |
13723 | 327 /* Convert the password to UCS-2LE */ |
11375 | 328 lennt = strlen(passw); |
13723 | 329 for (idx = 0; idx < lennt; idx++) |
11375 | 330 { |
13723 | 331 nt_pw[2 * idx] = passw[idx]; |
332 nt_pw[2 * idx + 1] = 0; | |
11375 | 333 } |
334 | |
335 cipher = gaim_ciphers_find_cipher("md4"); | |
336 context = gaim_cipher_context_new(cipher, NULL); | |
13723 | 337 gaim_cipher_context_append(context, (guint8 *)nt_pw, 2 * lennt); |
338 gaim_cipher_context_digest(context, 21, nt_hpw, NULL); | |
11375 | 339 gaim_cipher_context_destroy(context); |
340 | |
13723 | 341 memset(nt_hpw + 16, 0, 5); |
13698 | 342 calc_resp(nt_hpw, nonce, nt_resp); |
13084 | 343 memcpy(tmp, nt_resp, 0x18); |
344 tmp += 0x18; | |
345 | |
346 /* LCS Stuff */ | |
13723 | 347 if (flags) { |
13084 | 348 tmsg->flags = 0x409082d4; |
13087 | 349 gensesskey(sesskey, NULL); |
13084 | 350 memcpy(tmp, sesskey, 0x10); |
351 } | |
352 | |
353 /*tmsg->flags2 = 0x0a280105; | |
354 tmsg->flags3 = 0x0f000000;*/ | |
13088 | 355 |
13723 | 356 tmp = gaim_base64_encode((guchar *)tmsg, msglen); |
11375 | 357 g_free(tmsg); |
13723 | 358 |
11375 | 359 return tmp; |
360 } |