changelog update
[siplcs.git] / src / core / sip-sec-ntlm.c
blobfb8b0f6a913c59d7e57d8c57856efc7fabb5c188
1 /**
2 * @file sip-sec-ntlm.c
4 * pidgin-sipe
6 * Copyright (C) 2009, 2010 pier11 <pier11@operamail.com>
7 * Copyright (C) 2008 Novell, Inc.
8 * Modify 2007, Anibal Avelar <avelar@gmail.com>
9 * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
11 * Implemented with reference to the follow documentation:
12 * - http://davenport.sourceforge.net/ntlm.html
13 * - MS-NLMP: http://msdn.microsoft.com/en-us/library/cc207842.aspx
14 * - MS-SIP : http://msdn.microsoft.com/en-us/library/cc246115.aspx
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 * Byte order policy:
34 * - NTLM messages (byte streams) should be in LE (Little-Endian) byte order.
35 * - internal int16, int32, int64 should contain proper values.
36 * For example: 01 00 00 00 LE should be translated to (int32)1
37 * - When reading/writing from/to NTLM message appropriate conversion should
38 * be taken to properly present integer values. glib's "Byte Order Macros"
39 * should be used for that, for example GUINT32_FROM_LE
40 * - All calculations should be made in dedicated local variables (system-endian),
41 * not in NTLM (LE) structures.
44 #include <glib.h>
45 #include <glib/gprintf.h>
46 #include <stdio.h>
47 #include <errno.h>
48 #include <string.h>
49 #include <stdlib.h>
50 #include "debug.h"
52 #ifndef _WIN32
53 #include <sys/types.h>
54 #ifdef __sun__
55 #include <sys/sockio.h>
56 #endif
57 #else /* _WIN32 */
58 #include "libc_interface.h"
59 #ifdef _DLL
60 #define _WS2TCPIP_H_
61 #define _WINSOCK2API_
62 #define _LIBC_INTERNAL_
63 #endif /* _DLL */
64 #include "network.h"
65 #include "internal.h"
66 #endif /* _WIN32 */
68 #ifdef HAVE_LANGINFO_CODESET
69 #include <langinfo.h>
70 #endif /* HAVE_LANGINFO_CODESET */
72 #include "util.h"
73 #include "cipher.h"
75 #include "sipe.h"
76 #include "sipe-utils.h"
77 #include "sip-sec-mech.h"
78 #include "sip-sec-ntlm.h"
80 /* [MS-NLMP] */
81 #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* A */
82 #define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* B */
83 #define NTLMSSP_REQUEST_TARGET 0x00000004 /* C */
84 #define r9 0x00000008 /* r9 */
85 #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* D */
86 #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* E */
87 #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* F */
88 #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* G */
89 #define r8 0x00000100 /* r8 */
90 #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* H */
91 #define NTLMSSP_NEGOTIATE_NT_ONLY 0x00000400 /* I */
92 #define anonymous 0x00000800 /* J */
93 #define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x00001000 /* K */
94 #define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x00002000 /* L */
95 #define r7 0x00004000 /* r7 */
96 #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* M */
97 #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* N */
98 #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* O */
99 #define r6 0x00040000 /* r6 */
100 #define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000 /* P */
101 #define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 /* Q */
102 #define r5 0x00200000 /* r5 */
103 #define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 /* R */
104 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* S */
105 #define r4 0x01000000 /* r4 */
106 #define NTLMSSP_NEGOTIATE_VERSION 0x02000000 /* T */
107 #define r3 0x04000000 /* r3 */
108 #define r2 0x08000000 /* r2 */
109 #define r1 0x10000000 /* r1 */
110 #define NTLMSSP_NEGOTIATE_128 0x20000000 /* U */
111 #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 /* V */
112 #define NTLMSSP_NEGOTIATE_56 0x80000000 /* W */
114 /* AvId */
115 #define MsvAvEOL 0
116 #define MsvAvNbComputerName 1
117 #define MsvAvNbDomainName 2
118 #define MsvAvDnsComputerName 3
119 #define MsvAvDnsDomainName 4
120 /** @since Windows XP */
121 #define MsvAvDnsTreeName 5
122 /** @since Windows XP */
123 #define MsvAvFlags 6
124 /** @since Windows Vista */
125 #define MsvAvTimestamp 7
126 /** @since Windows Vista */
127 #define MsAvRestrictions 8
128 /** @since Windows 7 */
129 #define MsvAvTargetName 9
130 /** @since Windows 7 */
131 #define MsvChannelBindings 10
133 /* time_t <-> (guint64) time_val conversion */
134 #define TIME_VAL_FACTOR 10000000
135 #define TIME_VAL_OFFSET 116444736000000000LL
136 #define TIME_T_TO_VAL(time_t) (((guint64)(time_t)) * TIME_VAL_FACTOR + TIME_VAL_OFFSET)
137 #define TIME_VAL_TO_T(time_val) ((time_t)((GUINT64_FROM_LE((time_val)) - TIME_VAL_OFFSET) / TIME_VAL_FACTOR))
140 * NTLMv1 is no longer used except in tests. R.I.P.
142 * It remains in this file only for documentary purposes
144 #ifdef _SIPE_COMPILING_TESTS
145 static gboolean use_ntlm_v2 = FALSE;
146 #endif
148 /* Common negotiate flags */
149 #define NEGOTIATE_FLAGS_CONN \
150 NTLMSSP_NEGOTIATE_UNICODE | \
151 NTLMSSP_NEGOTIATE_56 | \
152 NTLMSSP_NEGOTIATE_128 | \
153 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | \
154 NTLMSSP_NEGOTIATE_NTLM | \
155 NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \
156 NTLMSSP_NEGOTIATE_KEY_EXCH | \
157 NTLMSSP_REQUEST_TARGET
159 /* Negotiate flags required in connectionless NTLM */
160 #define NEGOTIATE_FLAGS \
161 ( NEGOTIATE_FLAGS_CONN | \
162 NTLMSSP_NEGOTIATE_SIGN | \
163 NTLMSSP_NEGOTIATE_DATAGRAM )
165 #define NTLMSSP_LN_OR_NT_KEY_LEN 16
166 #define NTLMSSP_LM_RESP_LEN 24
167 #define NTLMSSP_SESSION_KEY_LEN 16
168 #define MD4_DIGEST_LEN 16
169 #define MD5_DIGEST_LEN 16
171 #define IS_FLAG(flags, flag) ((flags & flag) == flag)
173 struct av_pair {
174 guint16 av_id;
175 guint16 av_len;
176 /* value */
179 /* 8 bytes */
180 struct version {
181 guint8 product_major_version;
182 guint8 product_minor_version;
183 guint16 product_build;
184 guint8 zero2[3];
185 guint8 ntlm_revision_current;
188 /* 8 bytes */
189 struct smb_header {
190 guint16 len;
191 guint16 maxlen;
192 guint32 offset;
195 struct ntlm_message {
196 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/
197 guint32 type; /* 0x00000003 */
200 struct negotiate_message {
201 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */
202 guint32 type; /* 0x00000001 */
203 guint32 flags; /* 0xb203 */
204 struct smb_header domain;
205 struct smb_header host;
206 struct version ver;
207 /* payload
208 * - DomainName (always ASCII)
209 * - WorkstationName (always ASCII)
213 struct challenge_message {
214 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/
215 guint32 type; /* 0x00000002 */
216 struct smb_header target_name;
217 guint32 flags; /* 0x8201 */
218 guint8 nonce[8];
219 guint8 zero1[8];
220 struct smb_header target_info;
221 struct version ver;
222 /* payload
223 * - TargetName (negotiated encoding)
224 * - TargetInfo (a sequence of AV_PAIR structures) (always Unicode)
228 struct authenticate_message {
229 guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'*/
230 guint32 type; /* 0x00000003 */
231 /** LmChallengeResponseFields */
232 struct smb_header lm_resp;
233 /** NtChallengeResponseFields */
234 struct smb_header nt_resp;
235 /** DomainNameFields */
236 struct smb_header domain;
237 /** UserNameFields */
238 struct smb_header user;
239 /** WorkstationFields */
240 struct smb_header host;
241 /** EncryptedRandomSessionKeyFields */
242 struct smb_header session_key;
243 guint32 flags;
244 struct version ver;
245 //guint8 mic[16];
246 /* payload
247 * - LmChallengeResponse
248 * - NtChallengeResponse
249 * - DomainName (negotiated encoding)
250 * - UserName (negotiated encoding)
251 * - Workstation (negotiated encoding)
252 * - EncryptedRandomSessionKey
256 #ifndef HAVE_LANGINFO_CODESET
257 static char SIPE_DEFAULT_CODESET[] = "ANSI_X3.4-1968";
258 #endif
260 /* Private Methods */
262 /* Utility Functions */
264 static int
265 unicode_strconvcopy_dir(gchar *dest, const gchar *source, int remlen, gsize source_len, gboolean to_16LE)
267 GIConv fd;
268 gchar *inbuf = (gchar *) source;
269 gchar *outbuf = dest;
270 gsize inbytes = source_len;
271 gsize outbytes = remlen;
272 #ifdef HAVE_LANGINFO_CODESET
273 char *sys_cp = nl_langinfo(CODESET);
274 #else
275 char *sys_cp = SIPE_DEFAULT_CODESET;
276 #endif /* HAVE_LANGINFO_CODESET */
278 /* fall back to utf-8 */
279 if (!sys_cp) sys_cp = "UTF-8";
281 fd = to_16LE ? g_iconv_open("UTF-16LE", sys_cp) : g_iconv_open(sys_cp, "UTF-16LE");
282 if( fd == (GIConv)-1 ) {
283 purple_debug_error( "sipe", "iconv_open returned -1, cannot continue\n" );
285 g_iconv(fd, &inbuf, &inbytes, &outbuf, &outbytes);
286 g_iconv_close(fd);
287 return (remlen - outbytes);
290 static int
291 unicode_strconvcopy(gchar *dest, const gchar *source, int remlen)
293 return unicode_strconvcopy_dir(dest, source, remlen, strlen(source), TRUE);
296 /* UTF-16LE to native encoding
297 * Must be g_free'd after use */
298 static gchar *
299 unicode_strconvcopy_back(const gchar *source,
300 int len)
302 char *res = NULL;
303 int dest_len = 2 * len;
304 gchar *dest = g_new0(gchar, dest_len);
306 dest_len = unicode_strconvcopy_dir(dest, source, dest_len, len, FALSE);
307 res = g_strndup(dest, dest_len);
308 g_free(dest);
310 return res;
313 /* crc32 source copy from gg's common.c */
314 static guint32 crc32_table[256];
315 static int crc32_initialized = 0;
317 static void crc32_make_table()
319 guint32 h = 1;
320 unsigned int i, j;
322 memset(crc32_table, 0, sizeof(crc32_table));
324 for (i = 128; i; i >>= 1) {
325 h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
327 for (j = 0; j < 256; j += 2 * i)
328 crc32_table[i + j] = crc32_table[j] ^ h;
331 crc32_initialized = 1;
334 static guint32 crc32(guint32 crc, const guint8 *buf, int len)
336 if (!crc32_initialized)
337 crc32_make_table();
339 if (!buf || len < 0)
340 return crc;
342 crc ^= 0xffffffffL;
344 while (len--)
345 crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff];
347 return crc ^ 0xffffffffL;
350 static guint32
351 CRC32 (const char *msg, int len)
353 guint32 crc = 0L;//crc32(0L, Z_NULL, 0);
354 crc = crc32(crc, (guint8 *) msg, len);
355 //char * ptr = (char*) &crc;
356 //return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | (ptr[3] & 0xff);
357 return crc;
360 /* Cyphers */
362 #ifdef _SIPE_COMPILING_TESTS
363 static void setup_des_key(const unsigned char key_56[], unsigned char *key)
365 key[0] = key_56[0];
366 key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
367 key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
368 key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
369 key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
370 key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
371 key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
372 key[7] = (key_56[6] << 1) & 0xFF;
375 static void des_ecb_encrypt(const unsigned char *plaintext, unsigned char *result, const unsigned char *key)
377 PurpleCipher *cipher;
378 PurpleCipherContext *context;
379 size_t outlen;
381 cipher = purple_ciphers_find_cipher("des");
382 context = purple_cipher_context_new(cipher, NULL);
383 purple_cipher_context_set_key(context, (guchar*)key);
384 purple_cipher_context_encrypt(context, (guchar*)plaintext, 8, (guchar*)result, &outlen);
385 purple_cipher_context_destroy(context);
387 #endif
389 #ifdef _SIPE_COMPILING_TESTS
390 /* (k = 7 byte key, d = 8 byte data) returns 8 bytes in results */
391 static void
392 DES (const unsigned char *k, const unsigned char *d, unsigned char * results)
394 unsigned char key[8];
395 setup_des_key(k, key);
396 des_ecb_encrypt(d, results, key);
399 /* (K = 21 byte key, D = 8 bytes of data) returns 24 bytes in results: */
400 static void
401 DESL (const unsigned char *k, const unsigned char *d, unsigned char * results)
403 unsigned char keys[21];
405 /* Copy the first 16 bytes */
406 memcpy(keys, k, 16);
408 /* Zero out the last 5 bytes of the key */
409 memset(keys + 16, 0, 5);
411 DES(keys, d, results);
412 DES(keys + 7, d, results + 8);
413 DES(keys + 14, d, results + 16);
415 #endif
417 static void
418 RC4K (const unsigned char * k, unsigned long key_len, const unsigned char * d, int len, unsigned char * result)
420 PurpleCipherContext * context = purple_cipher_context_new_by_name("rc4", NULL);
421 purple_cipher_context_set_option(context, "key_len", (gpointer)key_len);
422 purple_cipher_context_set_key(context, k);
423 purple_cipher_context_encrypt(context, (const guchar *)d, len, result, NULL);
424 purple_cipher_context_destroy(context);
427 /* out 16 bytes */
428 static void
429 MD4 (const unsigned char * d, int len, unsigned char * result)
431 PurpleCipher * cipher = purple_ciphers_find_cipher("md4");
432 PurpleCipherContext * context = purple_cipher_context_new(cipher, NULL);
433 purple_cipher_context_append(context, (guchar*)d, len);
434 purple_cipher_context_digest(context, MD4_DIGEST_LEN, (guchar*)result, NULL);
435 purple_cipher_context_destroy(context);
438 /* out 16 bytes */
439 static void
440 MD5 (const unsigned char * d, int len, unsigned char * result)
442 PurpleCipher * cipher = purple_ciphers_find_cipher("md5");
443 PurpleCipherContext * context = purple_cipher_context_new(cipher, NULL);
444 purple_cipher_context_append(context, (guchar*)d, len);
445 purple_cipher_context_digest(context, MD5_DIGEST_LEN, (guchar*)result, NULL);
446 purple_cipher_context_destroy(context);
449 /* out 16 bytes */
451 static void
452 HMACT64 (const unsigned char *key, int key_len, const unsigned char *data, int data_len, unsigned char *result)
454 int i;
455 unsigned char ibuff[64 + data_len];
456 unsigned char obuff[64 + 16];
458 if (key_len > 64)
459 key_len = 64;
461 for (i = 0; i < key_len; i++) {
462 ibuff[i] = key[i] ^ 0x36;
463 obuff[i] = key[i] ^ 0x5c;
465 for (i = key_len; i < 64; i++) {
466 ibuff[i] = 0x36;
467 obuff[i] = 0x5c;
470 memcpy(ibuff+64, data, data_len);
472 MD5 (ibuff, 64 + data_len, obuff+64);
473 MD5 (obuff, 64 + 16, result);
475 #define HMAC_MD5 HMACT64
478 /* out 16 bytes */
479 static void
480 HMAC_MD5 (const unsigned char *key, int key_len, const unsigned char *data, int data_len, unsigned char *result)
482 PurpleCipher *cipher = purple_ciphers_find_cipher("hmac");
483 PurpleCipherContext *context = purple_cipher_context_new(cipher, NULL);
485 purple_cipher_context_set_option(context, "hash", "md5");
486 purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len));
488 purple_cipher_context_append(context, (guchar *)data, data_len);
489 purple_cipher_context_digest(context, 16, (guchar*)result, NULL);
490 purple_cipher_context_destroy(context);
493 /* NTLM Core Methods */
495 static void
496 NONCE(unsigned char *buffer, int num)
498 int i;
499 for (i = 0; i < num; i++) {
500 buffer[i] = (rand() & 0xff);
504 #ifdef _SIPE_COMPILING_TESTS
505 static void
506 Z(unsigned char *buffer, int num)
508 memset(buffer, 0, num);
510 #endif
512 #ifdef _SIPE_COMPILING_TESTS
513 static void
514 LMOWFv1 (const char *password, SIPE_UNUSED_PARAMETER const char *user, SIPE_UNUSED_PARAMETER const char *domain, unsigned char *result)
516 /* "KGS!@#$%" */
517 unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
518 unsigned char uppercase_password[14];
519 int i;
521 int len = strlen(password);
522 if (len > 14) {
523 len = 14;
526 // Uppercase password
527 for (i = 0; i < len; i++) {
528 uppercase_password[i] = g_ascii_toupper(password[i]);
531 // Zero the rest
532 for (; i < 14; i++) {
533 uppercase_password[i] = 0;
536 DES (uppercase_password, magic, result);
537 DES (uppercase_password + 7, magic, result + 8);
539 #endif
542 Define NTOWFv1(Passwd, User, UserDom) as
543 MD4(UNICODE(Passwd))
544 EndDefine
546 /* out 16 bytes */
547 static void
548 NTOWFv1 (const char* password, SIPE_UNUSED_PARAMETER const char *user, SIPE_UNUSED_PARAMETER const char *domain, unsigned char *result)
550 int len_u = 2 * strlen(password); // utf16 should not be more
551 unsigned char unicode_password[len_u];
553 len_u = unicode_strconvcopy((gchar *)unicode_password, password, len_u);
554 MD4 (unicode_password, len_u, result);
558 Define NTOWFv2(Passwd, User, UserDom) as
559 HMAC_MD5( MD4(UNICODE(Passwd)), ConcatenationOf( Uppercase(User), UserDom ) )
560 EndDefine
562 /* out 16 bytes */
563 void
564 NTOWFv2 (const char* password, const char *user, const char *domain, unsigned char *result)
566 unsigned char response_key_nt_v1 [16];
567 int len_user = user ? strlen(user) : 0;
568 int len_domain = domain ? strlen(domain) : 0;
569 unsigned char user_upper[len_user + 1];
570 int len_user_u = 2 * len_user; // utf16 should not be more
571 int len_domain_u = 2 * len_domain; // utf16 should not be more
572 unsigned char buff[(len_user + len_domain)*2];
573 int i;
575 /* Uppercase user */
576 for (i = 0; i < len_user; i++) {
577 user_upper[i] = g_ascii_toupper(user[i]);
579 user_upper[len_user] = 0;
581 len_user_u = unicode_strconvcopy((gchar *)buff, (gchar *)user_upper, len_user_u);
582 len_domain_u = unicode_strconvcopy((gchar *)(buff+len_user_u), domain ? (gchar *)domain : "", len_domain_u);
584 NTOWFv1(password, user, domain, response_key_nt_v1);
586 HMAC_MD5(response_key_nt_v1, 16, buff, len_user_u + len_domain_u, result);
589 static void
590 compute_response(const guint32 neg_flags,
591 const unsigned char *response_key_nt,
592 const unsigned char *response_key_lm,
593 const guint8 *server_challenge,
594 const guint8 *client_challenge,
595 const guint64 time_val,
596 const guint8 *target_info,
597 int target_info_len,
598 unsigned char *lm_challenge_response,
599 unsigned char *nt_challenge_response,
600 unsigned char *session_base_key)
602 #ifdef _SIPE_COMPILING_TESTS
603 if (use_ntlm_v2)
605 #endif
607 Responserversion - The 1-byte response version. Currently set to 1.
608 HiResponserversion - The 1-byte highest response version understood by the client. Currently set to 1.
609 Time - The 8-byte little-endian time in GMT.
610 ServerName - The TargetInfo field structure of the CHALLENGE_MESSAGE payload.
611 ClientChallenge - The 8-byte challenge message generated by the client.
612 CHALLENGE_MESSAGE.ServerChallenge - The 8-byte challenge message generated by the server.
614 Define ComputeResponse(NegFlg, ResponseKeyNT, ResponseKeyLM, CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge, Time, ServerName) as
615 Set temp to ConcatenationOf(Responserversion, HiResponserversion, Z(6), //8bytes - 0
616 Time, //8bytes - 8
617 ClientChallenge, //8bytes - 16
618 Z(4), //4bytes - 24
619 ServerName, //variable - 28
620 Z(4)) //4bytes - 28+target_info_len
621 Set NTProofStr to HMAC_MD5(ResponseKeyNT, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp))
622 Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp)
623 Set LmChallengeResponse to ConcatenationOf(
624 HMAC_MD5(ResponseKeyLM, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge)),
625 ClientChallenge )
626 Set SessionBaseKey to HMAC_MD5(ResponseKeyNT, NTProofStr)
627 EndDefine
629 guint8 tmp [16];
630 guint8 nt_proof_str [16];
632 /* client_challenge (8) & temp (temp_len) buff */
633 int temp_len = 8+8+8+4+target_info_len+4;
634 guint8 temp2 [8 + temp_len];
635 memset(temp2, 0, 8 + temp_len); /* init to 0 */
636 temp2[8+0] = 1;
637 temp2[8+1] = 1;
638 *((guint64 *)(temp2+8+8)) = GUINT64_TO_LE(time_val); /* should be int64 aligned: OK for sparc */
639 memcpy(temp2+8+16, client_challenge, 8);
640 memcpy(temp2+8+28, target_info, target_info_len);
642 /* NTProofStr */
643 //Set NTProofStr to HMAC_MD5(ResponseKeyNT, ConcatenationOf(CHALLENGE_MESSAGE.ServerChallenge,temp))
644 memcpy(temp2, server_challenge, 8);
645 HMAC_MD5(response_key_nt, 16, temp2, 8+temp_len, nt_proof_str);
647 /* NtChallengeResponse */
648 //Set NtChallengeResponse to ConcatenationOf(NTProofStr, temp)
649 memcpy(nt_challenge_response, nt_proof_str, 16);
650 memcpy(nt_challenge_response+16, temp2+8, temp_len);
652 /* SessionBaseKey */
653 //SessionBaseKey to HMAC_MD5(ResponseKeyNT, NTProofStr)
654 HMAC_MD5(response_key_nt, 16, nt_proof_str, 16, session_base_key);
656 /* lm_challenge_response */
657 memcpy(tmp, server_challenge, 8);
658 memcpy(tmp+8, client_challenge, 8);
659 HMAC_MD5(response_key_lm, 16, tmp, 16, lm_challenge_response);
660 memcpy(lm_challenge_response+16, client_challenge, 8);
662 #ifndef _SIPE_COMPILING_TESTS
663 /* Not used in NTLMv2 */
664 (void)neg_flags;
665 #else
667 else
669 if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_LM_KEY)) {
670 // @TODO do not even reference nt_challenge_response
671 Z (nt_challenge_response, NTLMSSP_LM_RESP_LEN);
672 DESL (response_key_lm, server_challenge, lm_challenge_response);
673 } else if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
674 unsigned char prehash [16];
675 unsigned char hash [16];
677 /* nt_challenge_response */
678 memcpy(prehash, server_challenge, 8);
679 memcpy(prehash + 8, client_challenge, 8);
680 MD5 (prehash, 16, hash);
681 DESL (response_key_nt, hash, nt_challenge_response);
683 /* lm_challenge_response */
684 memcpy(lm_challenge_response, client_challenge, 8);
685 Z (lm_challenge_response+8, 16);
686 } else {
687 DESL (response_key_nt, server_challenge, nt_challenge_response);
688 if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_NT_ONLY)) {
689 memcpy(lm_challenge_response, nt_challenge_response, NTLMSSP_LM_RESP_LEN);
690 } else {
691 DESL (response_key_lm, server_challenge, lm_challenge_response);
695 /* Session Key */
696 MD4(response_key_nt, 16, session_base_key); // "User Session Key" -> "master key"
698 #endif
701 static void
702 KXKEY ( guint32 flags,
703 const unsigned char * session_base_key,
704 const unsigned char * lm_challenge_resonse,
705 const guint8 * server_challenge, /* 8-bytes, nonce */
706 unsigned char * key_exchange_key)
708 #ifdef _SIPE_COMPILING_TESTS
709 if (use_ntlm_v2)
711 #else
712 /* Not used in NTLMv2 */
713 (void)flags;
714 (void)lm_challenge_resonse;
715 (void)server_challenge;
716 #endif
717 memcpy(key_exchange_key, session_base_key, 16);
718 #ifdef _SIPE_COMPILING_TESTS
720 else
722 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
723 // Define KXKEY(SessionBaseKey, LmChallengeResponse, ServerChallenge) as
724 // Set KeyExchangeKey to HMAC_MD5(SessionBaseKey, ConcatenationOf(ServerChallenge, LmChallengeResponse [0..7]))
725 // EndDefine
726 guint8 tmp[16];
727 memcpy(tmp, server_challenge, 8);
728 memcpy(tmp+8, lm_challenge_resonse, 8);
729 HMAC_MD5(session_base_key, 16, tmp, 16, key_exchange_key);
730 } else {
731 // Assume v1 and NTLMSSP_REQUEST_NON_NT_SESSION_KEY not set
732 memcpy(key_exchange_key, session_base_key, 16);
735 #endif
739 If (Mode equals "Client")
740 Set SignKey to MD5(ConcatenationOf(RandomSessionKey,
741 "session key to client-to-server signing key magic constant"))
742 Else
743 Set SignKey to MD5(ConcatenationOf(RandomSessionKey,
744 "session key to server-to-client signing key magic constant"))
745 Endif
747 static void
748 SIGNKEY (const unsigned char * random_session_key, gboolean client, unsigned char * result)
750 char * magic = client
751 ? "session key to client-to-server signing key magic constant"
752 : "session key to server-to-client signing key magic constant";
754 int len = strlen(magic) + 1;
755 unsigned char md5_input [16 + len];
756 memcpy(md5_input, random_session_key, 16);
757 memcpy(md5_input + 16, magic, len);
759 MD5 (md5_input, len + 16, result);
763 Define SEALKEY(NegotiateFlags, RandomSessionKey, Mode) as
764 If (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag is set in NegFlg)
765 If ( NTLMSSP_NEGOTIATE_128 is set in NegFlg)
766 Set SealKey to RandomSessionKey
767 ElseIf ( NTLMSSP_NEGOTIATE_56 flag is set in NegFlg)
768 Set SealKey to RandomSessionKey[0..6]
769 Else
770 Set SealKey to RandomSessionKey[0..4]
771 Endif
773 If (Mode equals "Client")
774 Set SealKey to MD5(ConcatenationOf(SealKey, "session key to client-to-server sealing key magic constant"))
775 Else
776 Set SealKey to MD5(ConcatenationOf(SealKey, "session key to server-to-client sealing key magic constant"))
777 Endif
779 ElseIf (NTLMSSP_NEGOTIATE_56 flag is set in NegFlg)
780 Set SealKey to ConcatenationOf(RandomSessionKey[0..6], 0xA0)
781 Else
782 Set SealKey to ConcatenationOf(RandomSessionKey[0..4], 0xE5, 0x38, 0xB0)
783 Endif
784 EndDefine
786 /* out 16 bytes or 8 bytes depending if Ext.Sess.Sec is negotiated */
787 static void
788 SEALKEY (guint32 flags, const unsigned char * random_session_key, gboolean client, unsigned char * result)
790 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY))
792 char * magic = client
793 ? "session key to client-to-server sealing key magic constant"
794 : "session key to server-to-client sealing key magic constant";
796 int len = strlen(magic) + 1;
797 unsigned char md5_input [16 + len];
798 int key_len;
800 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_128)) {
801 purple_debug_info("sipe", "NTLM SEALKEY(): 128-bit key (Extended session security)\n");
802 key_len = 16;
803 } else if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_56)) {
804 purple_debug_info("sipe", "NTLM SEALKEY(): 56-bit key (Extended session security)\n");
805 key_len = 7;
806 } else {
807 purple_debug_info("sipe", "NTLM SEALKEY(): 40-bit key (Extended session security)\n");
808 key_len = 5;
811 memcpy(md5_input, random_session_key, key_len);
812 memcpy(md5_input + key_len, magic, len);
814 MD5 (md5_input, key_len + len, result);
816 else if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_LM_KEY)) /* http://davenport.sourceforge.net/ntlm.html#ntlm1KeyWeakening */
818 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_56)) {
819 purple_debug_info("sipe", "NTLM SEALKEY(): 56-bit key\n");
820 memcpy(result, random_session_key, 7);
821 result[7] = 0xA0;
822 } else {
823 purple_debug_info("sipe", "NTLM SEALKEY(): 40-bit key\n");
824 memcpy(result, random_session_key, 5);
825 result[5] = 0xE5;
826 result[6] = 0x38;
827 result[7] = 0xB0;
830 else
832 purple_debug_info("sipe", "NTLM SEALKEY(): 128-bit key\n");
833 memcpy(result, random_session_key, 16);
838 = for Extended Session Security =
839 Version (4 bytes): A 32-bit unsigned integer that contains the signature version. This field MUST be 0x00000001.
840 Checksum (8 bytes): An 8-byte array that contains the checksum for the message.
841 SeqNum (4 bytes): A 32-bit unsigned integer that contains the NTLM sequence number for this application message.
843 = if Extended Session Security is NOT negotiated =
844 Version (4 bytes): A 32-bit unsigned integer that contains the signature version. This field MUST be 0x00000001.
845 RandomPad (4 bytes): A 4-byte array that contains the random pad for the message.
846 Checksum (4 bytes): A 4-byte array that contains the checksum for the message.
847 SeqNum (4 bytes): A 32-bit unsigned integer that contains the NTLM sequence number for this application message.
849 0x00000001, RC4K(RandomPad), RC4K(CRC32(Message)), RC4K(0x00000000) XOR (application supplied SeqNum) -- RC4(X) xor X xor Y = RC4(Y)
851 Version(4), Checksum(8), SeqNum(4) -- for ext.sess.sec.
852 Version(4), RandomPad(4), Checksum(4), SeqNum(4)
854 /** MAC(Handle, SigningKey, SeqNum, Message) */
855 static gchar *
856 MAC (guint32 flags,
857 const char *buf,
858 int buf_len,
859 unsigned char *sign_key,
860 unsigned long sign_key_len,
861 unsigned char *seal_key,
862 unsigned long seal_key_len,
863 guint32 random_pad,
864 guint32 sequence)
866 guchar result [16];
867 guint32 *res_ptr;
868 gchar signature [33];
869 int i, j;
871 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
873 Define MAC(Handle, SigningKey, SeqNum, Message) as
874 Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001
875 Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to
876 HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7]
877 Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to SeqNum
878 Set SeqNum to SeqNum + 1
879 EndDefine
881 /* If a key exchange key is negotiated
882 Define MAC(Handle, SigningKey, SeqNum, Message) as
883 Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001
884 Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to RC4(Handle,
885 HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7])
886 Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to SeqNum
887 Set SeqNum to SeqNum + 1
888 EndDefine
891 unsigned char seal_key_ [16];
892 guchar hmac[16];
893 guchar tmp[4 + buf_len];
895 /* SealingKey' = MD5(ConcatenationOf(SealingKey, SequenceNumber))
896 RC4Init(Handle, SealingKey')
898 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_DATAGRAM)) {
899 unsigned char tmp2 [16+4];
901 memcpy(tmp2, seal_key, seal_key_len);
902 *((guint32 *)(tmp2+16)) = GUINT32_TO_LE(sequence);
903 MD5 (tmp2, 16+4, seal_key_);
904 } else {
905 memcpy(seal_key_, seal_key, seal_key_len);
908 purple_debug_info("sipe", "NTLM MAC(): Extented Session Security\n");
910 res_ptr = (guint32 *)result;
911 res_ptr[0] = GUINT32_TO_LE(1); // 4 bytes
912 res_ptr[3] = GUINT32_TO_LE(sequence);
914 res_ptr = (guint32 *)tmp;
915 res_ptr[0] = GUINT32_TO_LE(sequence);
916 memcpy(tmp+4, buf, buf_len);
918 HMAC_MD5(sign_key, sign_key_len, tmp, 4 + buf_len, hmac);
920 if (IS_FLAG(flags, NTLMSSP_NEGOTIATE_KEY_EXCH)) {
921 purple_debug_info("sipe", "NTLM MAC(): Key Exchange\n");
922 RC4K(seal_key_, seal_key_len, hmac, 8, result+4);
923 } else {
924 purple_debug_info("sipe", "NTLM MAC(): *NO* Key Exchange\n");
925 memcpy(result+4, hmac, 8);
927 } else {
928 /* The content of the first 4 bytes is irrelevant */
929 guint32 plaintext [] = {
930 GUINT32_TO_LE(0),
931 GUINT32_TO_LE(CRC32(buf, strlen(buf))),
932 GUINT32_TO_LE(sequence)
933 }; // 4, 4, 4 bytes
935 purple_debug_info("sipe", "NTLM MAC(): *NO* Extented Session Security\n");
937 RC4K(seal_key, seal_key_len, (const guchar *)plaintext, 12, result+4);
938 //RC4K(seal_key, 8, (const guchar *)plaintext, 12, result+4);
940 res_ptr = (guint32 *)result;
941 // Highest four bytes are the Version
942 res_ptr[0] = GUINT32_TO_LE(0x00000001); // 4 bytes
944 // Replace the first four bytes of the ciphertext with the random_pad
945 res_ptr[1] = GUINT32_TO_LE(random_pad); // 4 bytes
948 for (i = 0, j = 0; i < 16; i++, j+=2) {
949 g_sprintf(&signature[j], "%02X", result[i]);
952 return g_strdup(signature);
955 /* End Core NTLM Methods */
958 * @param server_challenge must be g_free()'d after use if requested
959 * @param target_info must be g_free()'d after use if requested
961 static void
962 purple_ntlm_parse_challenge(SipSecBuffer in_buff,
963 gboolean is_connection_based,
964 guint32 *flags,
965 guchar **server_challenge, /* 8 bytes */
966 guint64 *time_val,
967 guchar **target_info,
968 int *target_info_len)
970 guint32 our_flags = is_connection_based ? NEGOTIATE_FLAGS_CONN : NEGOTIATE_FLAGS;
971 struct challenge_message *cmsg = (struct challenge_message*)in_buff.value;
972 guint32 host_flags = GUINT32_FROM_LE(cmsg->flags);
974 /* server challenge (nonce) */
975 if (server_challenge) {
976 *server_challenge = (guchar *)g_new0(gchar, 8);
977 memcpy(*server_challenge, cmsg->nonce, 8);
980 /* flags */
981 purple_debug_info("sipe", "received NTLM NegotiateFlags = %X; OK? %i\n", host_flags, (host_flags & our_flags) == our_flags);
982 if (flags) {
983 *flags = host_flags;
986 /* target_info */
987 if (cmsg->target_info.len && cmsg->target_info.offset) {
988 const gchar *target_info_content = (((gchar *)cmsg) + GUINT32_FROM_LE(cmsg->target_info.offset));
989 struct av_pair *av = (struct av_pair*)target_info_content;
991 while (GUINT16_FROM_LE(av->av_id) != MsvAvEOL) {
992 gchar *av_value = ((gchar *)av) + 4;
994 switch (GUINT16_FROM_LE(av->av_id)) {
995 /* @since Vista */
996 case MsvAvTimestamp:
997 if (time_val) {
998 /* This is not int64 aligned on sparc */
999 guint64 tmp;
1000 memcpy((gchar *)&tmp, av_value, sizeof(tmp));
1001 *time_val = GUINT64_FROM_LE(tmp);
1003 break;
1005 av = (struct av_pair*)(((guint8*)av) + 4 + GUINT16_FROM_LE(av->av_len));
1008 if (target_info_len) {
1009 *target_info_len = GUINT16_FROM_LE(cmsg->target_info.len);
1011 if (target_info) {
1012 *target_info = (guchar *)g_new0(gchar, GUINT16_FROM_LE(cmsg->target_info.len));
1013 memcpy(*target_info, target_info_content, GUINT16_FROM_LE(cmsg->target_info.len));
1018 static void
1019 purple_ntlm_gen_authenticate(guchar **client_sign_key,
1020 guchar **server_sign_key,
1021 guchar **client_seal_key,
1022 guchar **server_seal_key,
1023 const gchar *user,
1024 const gchar *password,
1025 const gchar *hostname,
1026 const gchar *domain,
1027 const guint8 *server_challenge, /* nonce */
1028 const guint64 time_val,
1029 const guint8 *target_info,
1030 int target_info_len,
1031 gboolean is_connection_based,
1032 SipSecBuffer *out_buff,
1033 guint32 *flags)
1035 guint32 neg_flags = is_connection_based ? NEGOTIATE_FLAGS_CONN : NEGOTIATE_FLAGS;
1036 int ntlmssp_nt_resp_len =
1037 #ifdef _SIPE_COMPILING_TESTS
1038 use_ntlm_v2 ?
1039 #endif
1040 (16 + (32+target_info_len))
1041 #ifdef _SIPE_COMPILING_TESTS
1042 : NTLMSSP_LM_RESP_LEN
1043 #endif
1045 int msglen = sizeof(struct authenticate_message)
1046 + 2*(strlen(domain) + strlen(user)+ strlen(hostname))
1047 + NTLMSSP_LM_RESP_LEN + ntlmssp_nt_resp_len
1048 + (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH) ? NTLMSSP_SESSION_KEY_LEN : 0);
1049 struct authenticate_message *tmsg = g_malloc0(msglen);
1050 char *tmp;
1051 int remlen;
1052 guint32 offset;
1053 guint16 len;
1054 unsigned char response_key_lm [NTLMSSP_LN_OR_NT_KEY_LEN]; /* 16 */
1055 unsigned char response_key_nt [NTLMSSP_LN_OR_NT_KEY_LEN]; /* 16 */
1056 unsigned char lm_challenge_response [NTLMSSP_LM_RESP_LEN]; /* 24 */
1057 unsigned char nt_challenge_response [ntlmssp_nt_resp_len]; /* variable or 24 */
1058 unsigned char session_base_key [16];
1059 unsigned char key_exchange_key [16];
1060 unsigned char exported_session_key[16];
1061 unsigned char encrypted_random_session_key [16];
1062 unsigned char key [16];
1063 unsigned char client_challenge [8];
1065 NONCE (client_challenge, 8);
1067 #ifdef _SIPE_COMPILING_TESTS
1068 if (use_ntlm_v2) {
1069 #endif
1070 NTOWFv2 (password, user, domain, response_key_nt);
1071 memcpy(response_key_lm, response_key_nt, NTLMSSP_LN_OR_NT_KEY_LEN);
1072 #ifdef _SIPE_COMPILING_TESTS
1073 } else {
1074 NTOWFv1 (password, user, domain, response_key_nt);
1075 LMOWFv1 (password, user, domain, response_key_lm);
1077 #endif
1079 compute_response(neg_flags,
1080 response_key_nt,
1081 response_key_lm,
1082 server_challenge,
1083 client_challenge,
1084 time_val ? time_val : TIME_T_TO_VAL(time(NULL)),
1085 target_info,
1086 target_info_len,
1087 lm_challenge_response, /* out */
1088 nt_challenge_response, /* out */
1089 session_base_key); /* out */
1091 /* same as session_base_key for
1092 * - NTLNv1 w/o Ext.Sess.Sec and
1093 * - NTLMv2
1095 KXKEY(neg_flags, session_base_key, lm_challenge_response, server_challenge, key_exchange_key);
1097 if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH)) {
1098 NONCE (exported_session_key, 16); // random master key
1099 RC4K (key_exchange_key, 16, exported_session_key, 16, encrypted_random_session_key);
1100 } else {
1101 memcpy(exported_session_key, key_exchange_key, 16);
1104 /* p.46
1105 Set ClientSigningKey to SIGNKEY(ExportedSessionKey, "Client")
1106 Set ServerSigningKey to SIGNKEY(ExportedSessionKey, "Server")
1108 SIGNKEY(exported_session_key, TRUE, key);
1109 *client_sign_key = (guchar *)g_strndup((gchar *)key, 16);
1110 SIGNKEY(exported_session_key, FALSE, key);
1111 *server_sign_key = (guchar *)g_strndup((gchar *)key, 16);
1112 SEALKEY(neg_flags, exported_session_key, TRUE, key);
1113 *client_seal_key = (guchar *)g_strndup((gchar *)key, 16);
1114 SEALKEY(neg_flags, exported_session_key, FALSE, key);
1115 *server_seal_key = (guchar *)g_strndup((gchar *)key, 16);
1117 /* @since Vista
1118 If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present,
1119 the client SHOULD provide a MIC:
1120 - If there is an AV_PAIR structure (section 2.2.2.1) with the AvId field set to MsvAvFlags,
1121 - then in the Value field, set bit 0x2 to 1.
1122 - else add an AV_PAIR structure (section 2.2.2.1) and set the AvId field to MsvAvFlags
1123 and the Value field bit 0x2 to 1.
1124 - Populate the MIC field with the MIC.
1127 /* Connection-oriented:
1128 Set MIC to HMAC_MD5(ExportedSessionKey,
1129 ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0));
1130 Connectionless:
1131 Set MIC to HMAC_MD5(ExportedSessionKey,
1132 ConcatenationOf( CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE))
1135 /* on the server-side:
1136 If (NTLMSSP_NEGOTIATE_KEY_EXCH flag is set in NegFlg )
1137 Set ExportedSessionKey to RC4K(KeyExchangeKey, AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey)
1138 Set MIC to HMAC_MD5(ExportedSessionKey,
1139 ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0))
1140 Else
1141 Set ExportedSessionKey to KeyExchangeKey
1142 Set MIC to HMAC_MD5(KeyExchangeKey,
1143 ConcatenationOf( NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0)) EndIf
1144 =====
1145 @since Vista
1146 If the AUTHENTICATE_MESSAGE indicates the presence of a MIC field,
1147 then the MIC value computed earlier MUST be compared to the MIC field in the message,
1148 and if the two MIC values are not equal, then an authentication failure MUST be returned.
1149 An AUTHENTICATE_MESSAGE indicates the presence of a MIC field if the TargetInfo field has
1150 an AV_PAIR structure whose two fields:
1151 - AvId == MsvAvFlags
1152 - Value bit 0x2 == 1
1153 @supported NT, 2000, XP
1154 If NTLM v2 authentication is used and the AUTHENTICATE_MESSAGE.NtChallengeResponse.
1155 TimeStamp (section 2.2.2.7) is more than MaxLifetime (section 3.1.1.1) difference from
1156 the server time, then the server SHOULD return a failure.
1158 Connectionless:
1159 Set MIC to HMAC_MD5(ResponseKeyNT,
1160 ConcatenationOf( CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE_MIC0))
1163 /* authenticate message initialization */
1164 memcpy(tmsg->protocol, "NTLMSSP\0", 8);
1165 tmsg->type = GUINT32_TO_LE(3);
1167 /* Set Negotiate Flags */
1168 tmsg->flags = GUINT32_TO_LE(neg_flags);
1170 /* Domain */
1171 tmsg->domain.offset = GUINT32_TO_LE(offset = sizeof(struct authenticate_message));
1172 tmp = ((char*) tmsg) + offset;
1173 remlen = ((char *)tmsg)+msglen-tmp;
1174 tmsg->domain.len = tmsg->domain.maxlen = GUINT16_TO_LE(len = unicode_strconvcopy(tmp, domain, remlen));
1175 tmp += len;
1176 remlen = ((char *)tmsg)+msglen-tmp;
1178 /* User */
1179 tmsg->user.offset = GUINT32_TO_LE(offset += len);
1180 tmsg->user.len = tmsg->user.maxlen = GUINT16_TO_LE(len = unicode_strconvcopy(tmp, user, remlen));
1181 tmp += len;
1182 remlen = ((char *)tmsg)+msglen-tmp;
1184 /* Host */
1185 tmsg->host.offset = GUINT32_TO_LE(offset += len);
1186 tmsg->host.len = tmsg->host.maxlen = GUINT16_TO_LE(len = unicode_strconvcopy(tmp, hostname, remlen));
1187 tmp += len;
1189 /* LM */
1190 /* @since Windows 7
1191 If NTLM v2 authentication is used and the CHALLENGE_MESSAGE contains a TargetInfo field,
1192 the client SHOULD NOT send the LmChallengeResponse and SHOULD set the LmChallengeResponseLen
1193 and LmChallengeResponseMaxLen fields in the AUTHENTICATE_MESSAGE to zero.
1195 tmsg->lm_resp.offset = GUINT32_TO_LE(offset += len);
1196 tmsg->lm_resp.len = tmsg->lm_resp.maxlen = GUINT16_TO_LE(len = NTLMSSP_LM_RESP_LEN);
1197 memcpy(tmp, lm_challenge_response, len);
1198 tmp += len;
1200 /* NT */
1201 tmsg->nt_resp.offset = GUINT32_TO_LE(offset += len);
1202 tmsg->nt_resp.len = tmsg->nt_resp.maxlen = GUINT16_TO_LE(len = ntlmssp_nt_resp_len);
1203 memcpy(tmp, nt_challenge_response, len);
1204 tmp += len;
1206 /* Session Key */
1207 tmsg->session_key.offset = GUINT32_TO_LE(offset += len);
1208 if (IS_FLAG(neg_flags, NTLMSSP_NEGOTIATE_KEY_EXCH))
1210 tmsg->session_key.len = tmsg->session_key.maxlen = GUINT16_TO_LE(len = NTLMSSP_SESSION_KEY_LEN);
1211 memcpy(tmp, encrypted_random_session_key, len);
1212 tmp += len;
1214 else
1216 tmsg->session_key.len = tmsg->session_key.maxlen = 0;
1219 *flags = neg_flags;
1221 tmp = purple_base64_encode(exported_session_key, 16);
1222 purple_debug_info("sipe", "Generated NTLM AUTHENTICATE session key: %s\n", tmp);
1223 g_free(tmp);
1225 out_buff->value = tmsg;
1226 out_buff->length = msglen;
1230 * Generates Type 1 (Negotiate) message for connection-oriented cases (only)
1232 static void
1233 purple_ntlm_gen_negotiate(SipSecBuffer *out_buff)
1235 guint32 offset;
1236 guint16 len;
1237 int msglen = sizeof(struct negotiate_message);
1238 struct negotiate_message *tmsg = g_malloc0(msglen);
1240 /* negotiate message initialization */
1241 memcpy(tmsg->protocol, "NTLMSSP\0", 8);
1242 tmsg->type = GUINT32_TO_LE(1);
1244 /* Set Negotiate Flags */
1245 tmsg->flags = GUINT32_TO_LE(NEGOTIATE_FLAGS_CONN);
1247 /* Domain */
1248 tmsg->domain.offset = GUINT32_TO_LE(offset = sizeof(struct negotiate_message));
1249 tmsg->domain.len = tmsg->domain.maxlen = len = 0;
1251 /* Host */
1252 tmsg->host.offset = GUINT32_TO_LE(offset += len);
1253 tmsg->host.len = tmsg->host.maxlen = len = 0;
1255 /* Version */
1256 //tmsg->ver.product_major_version = 5; /* 5.1.2600 (Windows XP SP2) */
1257 //tmsg->ver.product_minor_version = 1;
1258 //tmsg->ver.product_build = 2600;
1259 //tmsg->ver.ntlm_revision_current = 0x0F; /* NTLMSSP_REVISION_W2K3 */
1261 out_buff->value = tmsg;
1262 out_buff->length = msglen;
1265 static gchar *
1266 purple_ntlm_sipe_signature_make (guint32 flags, const char *msg, guint32 random_pad, unsigned char *sign_key, unsigned char *seal_key)
1268 gchar *res = MAC(flags, msg,strlen(msg), sign_key,16, seal_key,16, random_pad, 100);
1269 //purple_debug_info("sipe", "NTLM calculated MAC: %s\n", res);
1270 return res;
1273 static gboolean
1274 purple_ntlm_verify_signature (char * a, char * b)
1276 return g_ascii_strncasecmp(a, b, 16*2) == 0;
1280 /* Describe NTLM messages functions */
1282 #define APPEND_NEG_FLAG(str, flags, flag, desc) \
1283 if ((flags & flag) == flag) g_string_append_printf(str, "\t%s\n", desc);
1285 static gchar *
1286 sip_sec_ntlm_negotiate_flags_describe(guint32 flags)
1288 GString* str = g_string_new(NULL);
1290 flags = GUINT32_FROM_LE(flags);
1292 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_UNICODE, "NTLMSSP_NEGOTIATE_UNICODE");
1293 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM, "NTLMSSP_NEGOTIATE_OEM");
1294 APPEND_NEG_FLAG(str, flags, NTLMSSP_REQUEST_TARGET, "NTLMSSP_REQUEST_TARGET");
1295 APPEND_NEG_FLAG(str, flags, r9, "r9");
1296 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_SIGN, "NTLMSSP_NEGOTIATE_SIGN");
1297 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_SEAL, "NTLMSSP_NEGOTIATE_SEAL");
1298 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_DATAGRAM, "NTLMSSP_NEGOTIATE_DATAGRAM");
1299 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_LM_KEY, "NTLMSSP_NEGOTIATE_LM_KEY");
1300 APPEND_NEG_FLAG(str, flags, r8, "r8");
1301 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_NTLM, "NTLMSSP_NEGOTIATE_NTLM");
1302 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_NT_ONLY, "NTLMSSP_NEGOTIATE_NT_ONLY");
1303 APPEND_NEG_FLAG(str, flags, anonymous, "anonymous");
1304 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED, "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED");
1305 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED, "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED");
1306 APPEND_NEG_FLAG(str, flags, r7, "r7");
1307 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_ALWAYS_SIGN, "NTLMSSP_NEGOTIATE_ALWAYS_SIGN");
1308 APPEND_NEG_FLAG(str, flags, NTLMSSP_TARGET_TYPE_DOMAIN, "NTLMSSP_TARGET_TYPE_DOMAIN");
1309 APPEND_NEG_FLAG(str, flags, NTLMSSP_TARGET_TYPE_SERVER, "NTLMSSP_TARGET_TYPE_SERVER");
1310 APPEND_NEG_FLAG(str, flags, r6, "r6");
1311 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, "NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY");
1312 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_IDENTIFY, "NTLMSSP_NEGOTIATE_IDENTIFY");
1313 APPEND_NEG_FLAG(str, flags, r5, "r5");
1314 APPEND_NEG_FLAG(str, flags, NTLMSSP_REQUEST_NON_NT_SESSION_KEY, "NTLMSSP_REQUEST_NON_NT_SESSION_KEY");
1315 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_TARGET_INFO, "NTLMSSP_NEGOTIATE_TARGET_INFO");
1316 APPEND_NEG_FLAG(str, flags, r4, "r4");
1317 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_VERSION, "NTLMSSP_NEGOTIATE_VERSION");
1318 APPEND_NEG_FLAG(str, flags, r3, "r3");
1319 APPEND_NEG_FLAG(str, flags, r2, "r2");
1320 APPEND_NEG_FLAG(str, flags, r1, "r1");
1321 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_128, "NTLMSSP_NEGOTIATE_128");
1322 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_KEY_EXCH, "NTLMSSP_NEGOTIATE_KEY_EXCH");
1323 APPEND_NEG_FLAG(str, flags, NTLMSSP_NEGOTIATE_56, "NTLMSSP_NEGOTIATE_56");
1325 return g_string_free(str, FALSE);
1328 static gchar *
1329 sip_sec_ntlm_describe_version(struct version *ver) {
1330 GString* str = g_string_new(NULL);
1331 gchar *ver_desc = "";
1332 gchar *ntlm_revision_desc = "";
1334 if (ver->product_major_version == 6) {
1335 ver_desc = "Windows Vista, Windows Server 2008, Windows 7 or Windows Server 2008 R2";
1336 } else if (ver->product_major_version == 5 && ver->product_minor_version == 2) {
1337 ver_desc = "Windows Server 2003";
1338 } else if (ver->product_major_version == 5 && ver->product_minor_version == 1) {
1339 ver_desc = "Windows XP SP2";
1342 if (ver->ntlm_revision_current == 0x0F) {
1343 ntlm_revision_desc = "NTLMSSP_REVISION_W2K3";
1344 } else if (ver->ntlm_revision_current == 0x0A) {
1345 ntlm_revision_desc = "NTLMSSP_REVISION_W2K3_RC1";
1348 g_string_append_printf(str, "\tproduct: %d.%d.%d (%s)\n",
1349 ver->product_major_version, ver->product_minor_version, ver->product_build, ver_desc);
1350 g_string_append_printf(str, "\tntlm_revision_current: 0x%02X (%s)\n", ver->ntlm_revision_current, ntlm_revision_desc);
1352 return g_string_free(str, FALSE);
1355 static gchar *
1356 sip_sec_ntlm_describe_smb_header(struct smb_header *header,
1357 const char* name)
1359 GString* str = g_string_new(NULL);
1361 g_string_append_printf(str, "\t%s.len : %d\n", name, GUINT16_FROM_LE(header->len));
1362 g_string_append_printf(str, "\t%s.maxlen: %d\n", name, GUINT16_FROM_LE(header->maxlen));
1363 g_string_append_printf(str, "\t%s.offset: %d\n", name, GUINT32_FROM_LE(header->offset));
1365 return g_string_free(str, FALSE);
1368 static gchar *
1369 sip_sec_ntlm_negotiate_message_describe(struct negotiate_message *cmsg)
1371 GString* str = g_string_new(NULL);
1372 char *tmp;
1374 g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags)));
1375 g_free(tmp);
1377 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->domain), "domain")));
1378 g_free(tmp);
1380 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->host), "host")));
1381 g_free(tmp);
1383 tmp = sip_sec_ntlm_describe_version(&(cmsg->ver));
1384 g_string_append(str, tmp);
1385 g_free(tmp);
1387 if (cmsg->domain.len && cmsg->domain.offset) {
1388 gchar *domain = g_strndup(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->domain.offset)), GUINT16_FROM_LE(cmsg->domain.len));
1389 g_string_append_printf(str, "\tdomain: %s\n", domain);
1390 g_free(domain);
1393 if (cmsg->host.len && cmsg->host.offset) {
1394 gchar *host = g_strndup(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->host.offset)), GUINT16_FROM_LE(cmsg->host.len));
1395 g_string_append_printf(str, "\thost: %s\n", host);
1396 g_free(host);
1399 return g_string_free(str, FALSE);
1402 static void
1403 describe_av_pairs(GString* str, const struct av_pair *av)
1405 guint16 av_id = GUINT16_FROM_LE(av->av_id);
1407 while (av_id != MsvAvEOL) {
1408 gchar *av_value = ((gchar *)av) + 4;
1410 #define AV_DESC(av_name) \
1412 gchar *tmp = unicode_strconvcopy_back(av_value, GUINT16_FROM_LE(av->av_len)); \
1413 g_string_append_printf(str, "\t%s: %s\n", av_name, tmp); \
1414 g_free(tmp); \
1417 switch (av_id) {
1418 case MsvAvNbComputerName:
1419 AV_DESC("MsvAvNbComputerName");
1420 break;
1421 case MsvAvNbDomainName:
1422 AV_DESC("MsvAvNbDomainName");
1423 break;
1424 case MsvAvDnsComputerName:
1425 AV_DESC("MsvAvDnsComputerName");
1426 break;
1427 case MsvAvDnsDomainName:
1428 AV_DESC("MsvAvDnsDomainName");
1429 break;
1430 case MsvAvDnsTreeName:
1431 AV_DESC("MsvAvDnsTreeName");
1432 break;
1433 case MsvAvFlags:
1434 g_string_append_printf(str, "\t%s: %d\n", "MsvAvFlags", GUINT32_FROM_LE(*((guint32*)av_value)));
1435 break;
1436 case MsvAvTimestamp:
1438 SipSecBuffer buff;
1439 char *tmp;
1440 guint64 time_val;
1441 time_t time_t_val;
1443 /* This is not int64 aligned on sparc */
1444 memcpy((gchar *)&time_val, av_value, sizeof(time_val));
1445 time_t_val = TIME_VAL_TO_T(time_val);
1447 buff.length = 8;
1448 buff.value = av_value;
1449 g_string_append_printf(str, "\t%s: %s - %s", "MsvAvTimestamp", (tmp = bytes_to_hex_str(&buff)),
1450 asctime(gmtime(&time_t_val)));
1451 g_free(tmp);
1452 break;
1454 case MsAvRestrictions:
1455 g_string_append_printf(str, "\t%s\n", "MsAvRestrictions");
1456 break;
1457 case MsvAvTargetName:
1458 AV_DESC("MsvAvTargetName");
1459 break;
1460 case MsvChannelBindings:
1461 g_string_append_printf(str, "\t%s\n", "MsvChannelBindings");
1462 break;
1465 av = (struct av_pair *)(((guint8 *)av) + 4 + GUINT16_FROM_LE(av->av_len));
1466 av_id = GUINT16_FROM_LE(av->av_id);
1470 static gchar *
1471 sip_sec_ntlm_authenticate_message_describe(struct authenticate_message *cmsg)
1473 GString* str = g_string_new(NULL);
1474 char *tmp;
1475 SipSecBuffer buff;
1477 g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags)));
1478 g_free(tmp);
1480 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->lm_resp), "lm_resp")));
1481 g_free(tmp);
1483 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->nt_resp), "nt_resp")));
1484 g_free(tmp);
1486 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->domain), "domain")));
1487 g_free(tmp);
1489 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->user), "user")));
1490 g_free(tmp);
1492 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->host), "host")));
1493 g_free(tmp);
1495 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->session_key), "session_key")));
1496 g_free(tmp);
1498 tmp = sip_sec_ntlm_describe_version(&(cmsg->ver));
1499 g_string_append(str, tmp);
1500 g_free(tmp);
1502 /* mic */
1503 //buff.length = 16;
1504 //buff.value = cmsg->mic;
1505 //g_string_append_printf(str, "\t%s: %s\n", "mic", (tmp = bytes_to_hex_str(&buff)));
1506 //g_free(tmp);
1508 if (cmsg->lm_resp.len && cmsg->lm_resp.offset) {
1509 buff.length = GUINT16_FROM_LE(cmsg->lm_resp.len);
1510 buff.value = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->lm_resp.offset);
1511 g_string_append_printf(str, "\t%s: %s\n", "lm_resp", (tmp = bytes_to_hex_str(&buff)));
1512 g_free(tmp);
1515 if (cmsg->nt_resp.len && cmsg->nt_resp.offset) {
1516 guint16 nt_resp_len_full = GUINT16_FROM_LE(cmsg->nt_resp.len);
1517 int nt_resp_len = nt_resp_len_full;
1519 buff.length = nt_resp_len_full;
1520 buff.value = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset);
1521 g_string_append_printf(str, "\t%s: %s\n", "nt_resp raw", (tmp = bytes_to_hex_str(&buff)));
1522 g_free(tmp);
1524 if (nt_resp_len > 24) { /* NTLMv2 */
1525 nt_resp_len = 16;
1528 buff.length = nt_resp_len;
1529 buff.value = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset);
1530 g_string_append_printf(str, "\t%s: %s\n", "nt_resp", (tmp = bytes_to_hex_str(&buff)));
1531 g_free(tmp);
1533 if (nt_resp_len_full > 24) { /* NTLMv2 */
1534 char *tmp;
1535 SipSecBuffer buff;
1536 const gchar *temp = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->nt_resp.offset) + 16;
1537 const int response_version = *((guchar*)temp);
1538 const int hi_response_version = *((guchar*)(temp+1));
1539 guint64 time_val;
1540 time_t time_t_val;
1541 const gchar *client_challenge = temp + 16;
1542 const gchar *target_info = temp + 28;
1543 guint16 target_info_len = nt_resp_len_full - 16 - 32;
1544 const struct av_pair *av = (struct av_pair*)target_info;
1546 buff.length = target_info_len;
1547 buff.value = (gchar *)target_info;
1548 g_string_append_printf(str, "\t%s: %s\n", "target_info raw", (tmp = bytes_to_hex_str(&buff)));
1549 g_free(tmp);
1551 /* This is not int64 aligned on sparc */
1552 memcpy((gchar *)&time_val, temp+8, sizeof(time_val));
1553 time_t_val = TIME_VAL_TO_T(time_val);
1555 g_string_append_printf(str, "\t%s: %d\n", "response_version", response_version);
1556 g_string_append_printf(str, "\t%s: %d\n", "hi_response_version", hi_response_version);
1558 buff.length = 8;
1559 buff.value = (gchar*)&time_val;
1560 g_string_append_printf(str, "\t%s: %s - %s", "time", (tmp = bytes_to_hex_str(&buff)),
1561 asctime(gmtime(&time_t_val)));
1562 g_free(tmp);
1564 buff.length = 8;
1565 buff.value = (gchar*)client_challenge;
1566 g_string_append_printf(str, "\t%s: %s\n", "client_challenge", (tmp = bytes_to_hex_str(&buff)));
1567 g_free(tmp);
1569 describe_av_pairs(str, av);
1571 g_string_append_printf(str, "\t%s\n", "----------- end of nt_resp v2 -----------");
1575 if (cmsg->domain.len && cmsg->domain.offset) {
1576 gchar *domain = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->domain.offset)), GUINT16_FROM_LE(cmsg->domain.len));
1577 g_string_append_printf(str, "\t%s: %s\n", "domain", domain);
1578 g_free(domain);
1581 if (cmsg->user.len && cmsg->user.offset) {
1582 gchar *user = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->user.offset)), GUINT16_FROM_LE(cmsg->user.len));
1583 g_string_append_printf(str, "\t%s: %s\n", "user", user);
1584 g_free(user);
1587 if (cmsg->host.len && cmsg->host.offset) {
1588 gchar *host = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->host.offset)), GUINT16_FROM_LE(cmsg->host.len));
1589 g_string_append_printf(str, "\t%s: %s\n", "host", host);
1590 g_free(host);
1593 if (cmsg->session_key.len && cmsg->session_key.offset) {
1594 buff.length = GUINT16_FROM_LE(cmsg->session_key.len);
1595 buff.value = (gchar *)cmsg + GUINT32_FROM_LE(cmsg->session_key.offset);
1596 g_string_append_printf(str, "\t%s: %s\n", "session_key", (tmp = bytes_to_hex_str(&buff)));
1597 g_free(tmp);
1600 return g_string_free(str, FALSE);
1603 static gchar *
1604 sip_sec_ntlm_challenge_message_describe(struct challenge_message *cmsg)
1606 GString* str = g_string_new(NULL);
1607 char *tmp;
1608 SipSecBuffer buff;
1610 g_string_append(str, (tmp = sip_sec_ntlm_negotiate_flags_describe(cmsg->flags)));
1611 g_free(tmp);
1613 /* nonce (server_challenge) */
1614 buff.length = 8;
1615 buff.value = cmsg->nonce;
1616 g_string_append_printf(str, "\t%s: %s\n", "server_challenge", (tmp = bytes_to_hex_str(&buff)));
1617 g_free(tmp);
1619 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->target_name), "target_name")));
1620 g_free(tmp);
1622 g_string_append(str, (tmp = sip_sec_ntlm_describe_smb_header(&(cmsg->target_info), "target_info")));
1623 g_free(tmp);
1625 g_string_append(str, (tmp = sip_sec_ntlm_describe_version(&(cmsg->ver))));
1626 g_free(tmp);
1628 if (cmsg->target_name.len && cmsg->target_name.offset) {
1629 gchar *target_name = unicode_strconvcopy_back(((gchar *)cmsg + GUINT32_FROM_LE(cmsg->target_name.offset)), GUINT16_FROM_LE(cmsg->target_name.len));
1630 g_string_append_printf(str, "\ttarget_name: %s\n", target_name);
1631 g_free(target_name);
1634 if (cmsg->target_info.len && cmsg->target_info.offset) {
1635 void *target_info = ((gchar *)cmsg + GUINT32_FROM_LE(cmsg->target_info.offset));
1636 guint16 target_info_len = GUINT16_FROM_LE(cmsg->target_info.len);
1637 struct av_pair *av = (struct av_pair*)target_info;
1638 SipSecBuffer buff;
1640 buff.length = target_info_len;
1641 buff.value = (gchar *)target_info;
1642 g_string_append_printf(str, "\t%s: %s\n", "target_info raw", (tmp = bytes_to_hex_str(&buff)));
1643 g_free(tmp);
1645 describe_av_pairs(str, av);
1648 return g_string_free(str, FALSE);
1651 gchar *
1652 sip_sec_ntlm_message_describe(SipSecBuffer buff)
1654 struct ntlm_message *msg;
1655 gchar *res = NULL;
1657 if (buff.length == 0 || buff.value == NULL || buff.length < 12) return NULL;
1659 msg = buff.value;
1660 if(!sipe_strequal("NTLMSSP", (char*)msg)) return NULL;
1662 switch (GUINT32_FROM_LE(msg->type)) {
1663 case 1: res = sip_sec_ntlm_negotiate_message_describe((struct negotiate_message *)msg);
1664 break;
1665 case 2: res = sip_sec_ntlm_challenge_message_describe((struct challenge_message *)msg);
1666 break;
1667 case 3: res = sip_sec_ntlm_authenticate_message_describe((struct authenticate_message *)msg);
1668 break;
1671 return res;
1674 /* sip-sec-mech.h API implementation for NTLM */
1676 /* Security context for NTLM */
1677 typedef struct _context_ntlm {
1678 struct sip_sec_context common;
1679 char* domain;
1680 char *username;
1681 char *password;
1682 int step;
1683 guchar *client_sign_key;
1684 guchar *server_sign_key;
1685 guchar *client_seal_key;
1686 guchar *server_seal_key;
1687 guint32 flags;
1688 } *context_ntlm;
1691 static sip_uint32
1692 sip_sec_acquire_cred__ntlm(SipSecContext context,
1693 const char *domain,
1694 const char *username,
1695 const char *password)
1697 context_ntlm ctx = (context_ntlm)context;
1699 /* NTLM requires a domain, username & password */
1700 if (!domain || !username || !password)
1701 return SIP_SEC_E_INTERNAL_ERROR;
1703 ctx->domain = g_strdup(domain);
1704 ctx->username = g_strdup(username);
1705 ctx->password = g_strdup(password);
1707 return SIP_SEC_E_OK;
1710 static sip_uint32
1711 sip_sec_init_sec_context__ntlm(SipSecContext context,
1712 SipSecBuffer in_buff,
1713 SipSecBuffer *out_buff,
1714 SIPE_UNUSED_PARAMETER const char *service_name)
1716 context_ntlm ctx = (context_ntlm) context;
1718 purple_debug_info("sipe", "sip_sec_init_sec_context__ntlm: in use\n");
1720 ctx->step++;
1721 if (ctx->step == 1) {
1722 if (!context->is_connection_based) {
1723 out_buff->length = 0;
1724 out_buff->value = NULL;
1725 } else {
1726 purple_ntlm_gen_negotiate(out_buff);
1728 return SIP_SEC_I_CONTINUE_NEEDED;
1730 } else {
1731 guchar *client_sign_key;
1732 guchar *server_sign_key;
1733 guchar *client_seal_key;
1734 guchar *server_seal_key;
1735 guchar *server_challenge = NULL;
1736 guint64 time_val = 0;
1737 guchar *target_info = NULL;
1738 int target_info_len = 0;
1739 guint32 flags;
1740 gchar *tmp;
1742 if (!in_buff.value || !in_buff.length) {
1743 return SIP_SEC_E_INTERNAL_ERROR;
1746 purple_ntlm_parse_challenge(in_buff,
1747 context->is_connection_based,
1748 &flags,
1749 &server_challenge, /* 8 bytes */
1750 &time_val,
1751 &target_info,
1752 &target_info_len);
1754 purple_ntlm_gen_authenticate(&client_sign_key,
1755 &server_sign_key,
1756 &client_seal_key,
1757 &server_seal_key,
1758 ctx->username,
1759 ctx->password,
1760 (tmp = g_ascii_strup(sipe_get_host_name(), -1)),
1761 ctx->domain,
1762 server_challenge,
1763 time_val,
1764 target_info,
1765 target_info_len,
1766 context->is_connection_based,
1767 out_buff,
1768 &flags);
1769 g_free(server_challenge);
1770 g_free(target_info);
1771 g_free(tmp);
1773 g_free(ctx->client_sign_key);
1774 ctx->client_sign_key = client_sign_key;
1776 g_free(ctx->server_sign_key);
1777 ctx->server_sign_key = server_sign_key;
1779 g_free(ctx->client_seal_key);
1780 ctx->client_seal_key = client_seal_key;
1782 g_free(ctx->server_seal_key);
1783 ctx->server_seal_key = server_seal_key;
1785 ctx->flags = flags;
1786 return SIP_SEC_E_OK;
1791 * @param message a NULL terminated string to sign
1794 static sip_uint32
1795 sip_sec_make_signature__ntlm(SipSecContext context,
1796 const char *message,
1797 SipSecBuffer *signature)
1799 /* FIXME? We always use a random_pad of 0 */
1800 gchar *signature_hex = purple_ntlm_sipe_signature_make(((context_ntlm) context)->flags,
1801 message,
1803 ((context_ntlm) context)->client_sign_key,
1804 ((context_ntlm) context)->client_seal_key);
1806 hex_str_to_bytes(signature_hex, signature);
1807 g_free(signature_hex);
1809 return SIP_SEC_E_OK;
1813 * @param message a NULL terminated string to check signature of
1814 * @return SIP_SEC_E_OK on success
1816 static sip_uint32
1817 sip_sec_verify_signature__ntlm(SipSecContext context,
1818 const char *message,
1819 SipSecBuffer signature)
1821 guint32 random_pad = GUINT32_FROM_LE(((guint32 *) signature.value)[1]);
1822 char *signature_hex = bytes_to_hex_str(&signature);
1823 gchar *signature_calc = purple_ntlm_sipe_signature_make(((context_ntlm) context)->flags,
1824 message,
1825 random_pad,
1826 ((context_ntlm) context)->server_sign_key,
1827 ((context_ntlm) context)->server_seal_key);
1828 sip_uint32 res;
1830 if (purple_ntlm_verify_signature(signature_calc, signature_hex)) {
1831 res = SIP_SEC_E_OK;
1832 } else {
1833 res = SIP_SEC_E_INTERNAL_ERROR;
1835 g_free(signature_calc);
1836 g_free(signature_hex);
1837 return(res);
1840 static void
1841 sip_sec_destroy_sec_context__ntlm(SipSecContext context)
1843 context_ntlm ctx = (context_ntlm) context;
1845 g_free(ctx->domain);
1846 g_free(ctx->username);
1847 g_free(ctx->password);
1848 g_free(ctx->client_sign_key);
1849 g_free(ctx->server_sign_key);
1850 g_free(ctx->client_seal_key);
1851 g_free(ctx->server_seal_key);
1852 g_free(ctx);
1855 SipSecContext
1856 sip_sec_create_context__ntlm(SIPE_UNUSED_PARAMETER SipSecAuthType type)
1858 context_ntlm context = g_malloc0(sizeof(struct _context_ntlm));
1859 if (!context) return(NULL);
1861 context->common.acquire_cred_func = sip_sec_acquire_cred__ntlm;
1862 context->common.init_context_func = sip_sec_init_sec_context__ntlm;
1863 context->common.destroy_context_func = sip_sec_destroy_sec_context__ntlm;
1864 context->common.make_signature_func = sip_sec_make_signature__ntlm;
1865 context->common.verify_signature_func = sip_sec_verify_signature__ntlm;
1867 return((SipSecContext) context);
1872 Local Variables:
1873 mode: c
1874 c-file-style: "bsd"
1875 indent-tabs-mode: t
1876 tab-width: 8
1877 End: