/src/pidgin/libpurple/ntlm.c
Line | Count | Source |
1 | | /** |
2 | | * @file ntlm.c |
3 | | */ |
4 | | |
5 | | /* purple |
6 | | * |
7 | | * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> |
8 | | * |
9 | | * hashing done according to description of NTLM on |
10 | | * http://www.innovation.ch/java/ntlm.html |
11 | | * |
12 | | * This program is free software; you can redistribute it and/or modify |
13 | | * it under the terms of the GNU General Public License as published by |
14 | | * the Free Software Foundation; either version 2 of the License, or |
15 | | * (at your option) any later version. |
16 | | * |
17 | | * This program is distributed in the hope that it will be useful, |
18 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | | * GNU General Public License for more details. |
21 | | * |
22 | | * You should have received a copy of the GNU General Public License |
23 | | * along with this program; if not, write to the Free Software |
24 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
25 | | */ |
26 | | |
27 | | #include "internal.h" |
28 | | |
29 | | #include "util.h" |
30 | | #include "ntlm.h" |
31 | | #include "cipher.h" |
32 | | #include "debug.h" |
33 | | #include <string.h> |
34 | | |
35 | | #define NTLM_NEGOTIATE_NTLM2_KEY 0x00080000 |
36 | | |
37 | | struct type1_message { |
38 | | guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */ |
39 | | guint32 type; /* 0x00000001 */ |
40 | | guint32 flags; /* 0x0000b203 */ |
41 | | |
42 | | guint16 dom_len1; /* domain string length */ |
43 | | guint16 dom_len2; /* domain string length */ |
44 | | guint32 dom_off; /* domain string offset */ |
45 | | |
46 | | guint16 host_len1; /* host string length */ |
47 | | guint16 host_len2; /* host string length */ |
48 | | guint32 host_off; /* host string offset (always 0x00000020) */ |
49 | | |
50 | | #if 0 |
51 | | guint8 host[*]; /* host string (ASCII) */ |
52 | | guint8 dom[*]; /* domain string (ASCII) */ |
53 | | #endif |
54 | | }; |
55 | | |
56 | | struct type2_message { |
57 | | guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ |
58 | | guint32 type; /* 0x00000002 */ |
59 | | |
60 | | guint32 zero; |
61 | | guint16 msg_len1; /* target name length */ |
62 | | guint16 msg_len2; /* target name length */ |
63 | | |
64 | | guint32 flags; /* 0x00008201 */ |
65 | | |
66 | | guint8 nonce[8]; /* nonce */ |
67 | | guint8 context[8]; |
68 | | }; |
69 | | |
70 | | struct type3_message { |
71 | | guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/ |
72 | | guint32 type; /* 0x00000003 */ |
73 | | |
74 | | guint16 lm_resp_len1; /* LanManager response length (always 0x18)*/ |
75 | | guint16 lm_resp_len2; /* LanManager response length (always 0x18)*/ |
76 | | guint32 lm_resp_off; /* LanManager response offset */ |
77 | | |
78 | | guint16 nt_resp_len1; /* NT response length (always 0x18) */ |
79 | | guint16 nt_resp_len2; /* NT response length (always 0x18) */ |
80 | | guint32 nt_resp_off; /* NT response offset */ |
81 | | |
82 | | guint16 dom_len1; /* domain string length */ |
83 | | guint16 dom_len2; /* domain string length */ |
84 | | guint32 dom_off; /* domain string offset (always 0x00000040) */ |
85 | | |
86 | | guint16 user_len1; /* username string length */ |
87 | | guint16 user_len2; /* username string length */ |
88 | | guint32 user_off; /* username string offset */ |
89 | | |
90 | | guint16 host_len1; /* host string length */ |
91 | | guint16 host_len2; /* host string length */ |
92 | | guint32 host_off; /* host string offset */ |
93 | | |
94 | | guint16 sess_len1; |
95 | | guint16 sess_len2; |
96 | | guint32 sess_off; /* message length */ |
97 | | |
98 | | guint32 flags; /* 0x00008201 */ |
99 | | /* guint32 flags2; */ /* unknown, used in windows messenger */ |
100 | | /* guint32 flags3; */ |
101 | | |
102 | | #if 0 |
103 | | guint8 dom[*]; /* domain string (unicode UTF-16LE) */ |
104 | | guint8 user[*]; /* username string (unicode UTF-16LE) */ |
105 | | guint8 host[*]; /* host string (unicode UTF-16LE) */ |
106 | | guint8 lm_resp[*]; /* LanManager response */ |
107 | | guint8 nt_resp[*]; /* NT response */ |
108 | | #endif |
109 | | }; |
110 | | |
111 | | gchar * |
112 | | purple_ntlm_gen_type1(const gchar *hostname, const gchar *domain) |
113 | 0 | { |
114 | 0 | int hostnamelen,host_off; |
115 | 0 | int domainlen,dom_off; |
116 | 0 | unsigned char *msg; |
117 | 0 | struct type1_message *tmsg; |
118 | 0 | gchar *tmp; |
119 | |
|
120 | 0 | hostnamelen = strlen(hostname); |
121 | 0 | domainlen = strlen(domain); |
122 | 0 | host_off = sizeof(struct type1_message); |
123 | 0 | dom_off = sizeof(struct type1_message) + hostnamelen; |
124 | 0 | msg = g_malloc0(sizeof(struct type1_message) + hostnamelen + domainlen); |
125 | 0 | tmsg = (struct type1_message*)msg; |
126 | 0 | tmsg->protocol[0] = 'N'; |
127 | 0 | tmsg->protocol[1] = 'T'; |
128 | 0 | tmsg->protocol[2] = 'L'; |
129 | 0 | tmsg->protocol[3] = 'M'; |
130 | 0 | tmsg->protocol[4] = 'S'; |
131 | 0 | tmsg->protocol[5] = 'S'; |
132 | 0 | tmsg->protocol[6] = 'P'; |
133 | 0 | tmsg->protocol[7] = '\0'; |
134 | 0 | tmsg->type = GUINT32_TO_LE(0x00000001); |
135 | 0 | tmsg->flags = GUINT32_TO_LE(0x0000b203); |
136 | 0 | tmsg->dom_len1 = tmsg->dom_len2 = GUINT16_TO_LE(domainlen); |
137 | 0 | tmsg->dom_off = GUINT32_TO_LE(dom_off); |
138 | 0 | tmsg->host_len1 = tmsg->host_len2 = GUINT16_TO_LE(hostnamelen); |
139 | 0 | tmsg->host_off = GUINT32_TO_LE(host_off); |
140 | 0 | memcpy(msg + host_off, hostname, hostnamelen); |
141 | 0 | memcpy(msg + dom_off, domain, domainlen); |
142 | |
|
143 | 0 | tmp = purple_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen); |
144 | 0 | g_free(msg); |
145 | |
|
146 | 0 | return tmp; |
147 | 0 | } |
148 | | |
149 | | guint8 * |
150 | | purple_ntlm_parse_type2(const gchar *type2, guint32 *flags) |
151 | 0 | { |
152 | 0 | gsize retlen; |
153 | 0 | struct type2_message *tmsg; |
154 | 0 | static guint8 nonce[8]; |
155 | |
|
156 | 0 | tmsg = (struct type2_message*)purple_base64_decode(type2, &retlen); |
157 | 0 | if (tmsg != NULL && retlen >= (sizeof(struct type2_message) - 1)) { |
158 | 0 | memcpy(nonce, tmsg->nonce, 8); |
159 | 0 | if (flags != NULL) |
160 | 0 | *flags = GUINT16_FROM_LE(tmsg->flags); |
161 | 0 | } else { |
162 | 0 | purple_debug_error("ntlm", "Unable to parse type2 message - returning empty nonce.\n"); |
163 | 0 | memset(nonce, 0, 8); |
164 | 0 | } |
165 | 0 | g_free(tmsg); |
166 | |
|
167 | 0 | return nonce; |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * Create a 64bit DES key by taking a 56bit key and adding |
172 | | * a parity bit after every 7th bit. |
173 | | */ |
174 | | static void |
175 | | setup_des_key(const guint8 key_56[], guint8 *key) |
176 | 0 | { |
177 | 0 | key[0] = key_56[0]; |
178 | 0 | key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); |
179 | 0 | key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); |
180 | 0 | key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); |
181 | 0 | key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); |
182 | 0 | key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); |
183 | 0 | key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); |
184 | 0 | key[7] = (key_56[6] << 1) & 0xFF; |
185 | 0 | } |
186 | | |
187 | | /* |
188 | | * helper function for purple cipher.c |
189 | | */ |
190 | | static void |
191 | | des_ecb_encrypt(const guint8 *plaintext, guint8 *result, const guint8 *key) |
192 | 0 | { |
193 | 0 | PurpleCipher *cipher; |
194 | 0 | PurpleCipherContext *context; |
195 | 0 | size_t outlen; |
196 | |
|
197 | 0 | cipher = purple_ciphers_find_cipher("des"); |
198 | 0 | context = purple_cipher_context_new(cipher, NULL); |
199 | 0 | purple_cipher_context_set_key(context, key); |
200 | 0 | purple_cipher_context_encrypt(context, plaintext, 8, result, &outlen); |
201 | 0 | purple_cipher_context_destroy(context); |
202 | 0 | } |
203 | | |
204 | | /* |
205 | | * takes a 21 byte array and treats it as 3 56-bit DES keys. The |
206 | | * 8 byte plaintext is encrypted with each key and the resulting 24 |
207 | | * bytes are stored in the results array. |
208 | | */ |
209 | | static void |
210 | | calc_resp(guint8 *keys, const guint8 *plaintext, unsigned char *results) |
211 | 0 | { |
212 | 0 | guint8 key[8]; |
213 | 0 | setup_des_key(keys, key); |
214 | 0 | des_ecb_encrypt(plaintext, results, key); |
215 | |
|
216 | 0 | setup_des_key(keys + 7, key); |
217 | 0 | des_ecb_encrypt(plaintext, results + 8, key); |
218 | |
|
219 | 0 | setup_des_key(keys + 14, key); |
220 | 0 | des_ecb_encrypt(plaintext, results + 16, key); |
221 | 0 | } |
222 | | |
223 | | static void |
224 | | gensesskey(char *buffer, const char *oldkey) |
225 | 0 | { |
226 | 0 | int i = 0; |
227 | 0 | if(oldkey == NULL) { |
228 | 0 | for(i=0; i<16; i++) { |
229 | 0 | buffer[i] = (char)(rand() & 0xff); |
230 | 0 | } |
231 | 0 | } else { |
232 | 0 | memcpy(buffer, oldkey, 16); |
233 | 0 | } |
234 | 0 | } |
235 | | |
236 | | gchar * |
237 | | purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags) |
238 | 0 | { |
239 | 0 | char lm_pw[14]; |
240 | 0 | unsigned char lm_hpw[21]; |
241 | 0 | char sesskey[16]; |
242 | 0 | guint8 key[8]; |
243 | 0 | int domainlen; |
244 | 0 | int usernamelen; |
245 | 0 | int hostnamelen; |
246 | 0 | int msglen; |
247 | 0 | struct type3_message *tmsg; |
248 | 0 | int passwlen, lennt; |
249 | 0 | unsigned char lm_resp[24], nt_resp[24]; |
250 | 0 | unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; |
251 | 0 | unsigned char nt_hpw[21]; |
252 | 0 | char nt_pw[128]; |
253 | 0 | PurpleCipher *cipher; |
254 | 0 | PurpleCipherContext *context; |
255 | 0 | char *tmp; |
256 | 0 | int idx; |
257 | 0 | gchar *ucs2le; |
258 | |
|
259 | 0 | domainlen = strlen(domain) * 2; |
260 | 0 | usernamelen = strlen(username) * 2; |
261 | 0 | hostnamelen = strlen(hostname) * 2; |
262 | 0 | msglen = sizeof(struct type3_message) + domainlen + |
263 | 0 | usernamelen + hostnamelen + 0x18 + 0x18 + ((flags) ? 0x10 : 0); |
264 | 0 | tmsg = g_malloc0(msglen); |
265 | 0 | passwlen = strlen(passw); |
266 | | |
267 | | /* type3 message initialization */ |
268 | 0 | tmsg->protocol[0] = 'N'; |
269 | 0 | tmsg->protocol[1] = 'T'; |
270 | 0 | tmsg->protocol[2] = 'L'; |
271 | 0 | tmsg->protocol[3] = 'M'; |
272 | 0 | tmsg->protocol[4] = 'S'; |
273 | 0 | tmsg->protocol[5] = 'S'; |
274 | 0 | tmsg->protocol[6] = 'P'; |
275 | 0 | tmsg->type = GUINT32_TO_LE(0x00000003); |
276 | 0 | tmsg->lm_resp_len1 = tmsg->lm_resp_len2 = GUINT16_TO_LE(0x18); |
277 | 0 | tmsg->lm_resp_off = GUINT32_TO_LE(sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen); |
278 | 0 | tmsg->nt_resp_len1 = tmsg->nt_resp_len2 = GUINT16_TO_LE(0x18); |
279 | 0 | tmsg->nt_resp_off = GUINT32_TO_LE(sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18); |
280 | |
|
281 | 0 | tmsg->dom_len1 = tmsg->dom_len2 = GUINT16_TO_LE(domainlen); |
282 | 0 | tmsg->dom_off = GUINT32_TO_LE(sizeof(struct type3_message)); |
283 | |
|
284 | 0 | tmsg->user_len1 = tmsg->user_len2 = GUINT16_TO_LE(usernamelen); |
285 | 0 | tmsg->user_off = GUINT32_TO_LE(sizeof(struct type3_message) + domainlen); |
286 | |
|
287 | 0 | tmsg->host_len1 = tmsg->host_len2 = GUINT16_TO_LE(hostnamelen); |
288 | 0 | tmsg->host_off = GUINT32_TO_LE(sizeof(struct type3_message) + domainlen + usernamelen); |
289 | |
|
290 | 0 | if(flags) { |
291 | 0 | tmsg->sess_off = GUINT32_TO_LE(sizeof(struct type3_message) + domainlen + usernamelen + hostnamelen + 0x18 + 0x18); |
292 | 0 | tmsg->sess_len1 = tmsg->sess_len2 = GUINT16_TO_LE(0x0010); |
293 | 0 | } |
294 | |
|
295 | 0 | tmsg->flags = GUINT32_TO_LE(0x00008201); |
296 | |
|
297 | 0 | tmp = (char *)tmsg + sizeof(struct type3_message); |
298 | |
|
299 | 0 | ucs2le = g_convert(domain, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL); |
300 | 0 | if (ucs2le != NULL) { |
301 | 0 | memcpy(tmp, ucs2le, domainlen); |
302 | 0 | g_free(ucs2le); |
303 | 0 | tmp += domainlen; |
304 | 0 | } else { |
305 | 0 | purple_debug_info("ntlm", "Unable to encode domain in UTF-16LE.\n"); |
306 | 0 | } |
307 | |
|
308 | 0 | ucs2le = g_convert(username, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL); |
309 | 0 | if (ucs2le != NULL) { |
310 | 0 | memcpy(tmp, ucs2le, usernamelen); |
311 | 0 | g_free(ucs2le); |
312 | 0 | tmp += usernamelen; |
313 | 0 | } else { |
314 | 0 | purple_debug_info("ntlm", "Unable to encode username in UTF-16LE.\n"); |
315 | 0 | } |
316 | |
|
317 | 0 | ucs2le = g_convert(hostname, -1, "UTF-16LE", "UTF-8", NULL, NULL, NULL); |
318 | 0 | if (ucs2le != NULL) { |
319 | 0 | memcpy(tmp, ucs2le, hostnamelen); |
320 | 0 | g_free(ucs2le); |
321 | 0 | tmp += hostnamelen; |
322 | 0 | } else { |
323 | 0 | purple_debug_info("ntlm", "Unable to encode hostname in UTF-16LE.\n"); |
324 | 0 | } |
325 | | |
326 | | /* LM */ |
327 | 0 | if (passwlen > 14) |
328 | 0 | passwlen = 14; |
329 | |
|
330 | 0 | for (idx = 0; idx < passwlen; idx++) |
331 | 0 | lm_pw[idx] = g_ascii_toupper(passw[idx]); |
332 | 0 | for (; idx < 14; idx++) |
333 | 0 | lm_pw[idx] = 0; |
334 | |
|
335 | 0 | setup_des_key((unsigned char*)lm_pw, key); |
336 | 0 | des_ecb_encrypt(magic, lm_hpw, key); |
337 | |
|
338 | 0 | setup_des_key((unsigned char*)(lm_pw + 7), key); |
339 | 0 | des_ecb_encrypt(magic, lm_hpw + 8, key); |
340 | |
|
341 | 0 | memset(lm_hpw + 16, 0, 5); |
342 | 0 | calc_resp(lm_hpw, nonce, lm_resp); |
343 | 0 | memcpy(tmp, lm_resp, 0x18); |
344 | 0 | tmp += 0x18; |
345 | | |
346 | | /* NTLM */ |
347 | | /* Convert the password to UTF-16LE */ |
348 | 0 | lennt = strlen(passw); |
349 | 0 | for (idx = 0; idx < lennt; idx++) |
350 | 0 | { |
351 | 0 | nt_pw[2 * idx] = passw[idx]; |
352 | 0 | nt_pw[2 * idx + 1] = 0; |
353 | 0 | } |
354 | |
|
355 | 0 | cipher = purple_ciphers_find_cipher("md4"); |
356 | 0 | context = purple_cipher_context_new(cipher, NULL); |
357 | 0 | purple_cipher_context_append(context, (guint8 *)nt_pw, 2 * lennt); |
358 | 0 | purple_cipher_context_digest(context, 21, nt_hpw, NULL); |
359 | 0 | purple_cipher_context_destroy(context); |
360 | |
|
361 | 0 | memset(nt_hpw + 16, 0, 5); |
362 | 0 | calc_resp(nt_hpw, nonce, nt_resp); |
363 | 0 | memcpy(tmp, nt_resp, 0x18); |
364 | 0 | tmp += 0x18; |
365 | | |
366 | | /* LCS Stuff */ |
367 | 0 | if (flags) { |
368 | 0 | tmsg->flags = GUINT32_TO_LE(0x409082d4); |
369 | 0 | gensesskey(sesskey, NULL); |
370 | 0 | memcpy(tmp, sesskey, 0x10); |
371 | 0 | } |
372 | | |
373 | | /*tmsg->flags2 = 0x0a280105; |
374 | | tmsg->flags3 = 0x0f000000;*/ |
375 | |
|
376 | 0 | tmp = purple_base64_encode((guchar *)tmsg, msglen); |
377 | 0 | g_free(tmsg); |
378 | |
|
379 | 0 | return tmp; |
380 | 0 | } |