1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
6 * This file provides the necessary methods for authenticating with
7 * Microsoft's Secure Password Authentication.
9 * All the original code used here was torn by Marc Prud'hommeaux out of the
10 * Samba project (by Andrew Tridgell, Jeremy Allison, and others).
12 * Copyright (c) The Exim Maintainers 2021 - 2023
13 * SPDX-License-Identifier: GPL-2.0-or-later
15 * Tom Kistner provided additional code, adding spa_build_auth_challenge() to
16 * support server authentication mode.
18 * Mark Lyda provided a patch to solve this problem:
20 - Exim is indicating in its Authentication Request message (Type 1) that it
21 can transmit text in either Unicode or OEM format.
23 - Microsoft's SMTP server (smtp.email.msn.com) is responding in its
24 Challenge message (Type 2) that it will be expecting the OEM format.
26 - Exim does not pay attention to the text format requested by Microsoft's
27 SMTP server and, instead, defaults to using the Unicode format.
30 * http://www.innovation.ch/java/ntlm.html
31 * http://www.kuro5hin.org/story/2002/4/28/1436/66154
33 * It seems that some systems have existing but different definitions of some
34 * of the following types. I received a complaint about "int16" causing
35 * compilation problems. So I (PH) have renamed them all, to be on the safe
36 * side, by adding 'x' on the end.
38 * typedef signed short int16;
39 * typedef unsigned short uint16;
40 * typedef unsigned uint32;
41 * typedef unsigned char uint8;
43 * The API is extremely simple:
44 * 1. Form a SPA authentication request based on the username
45 * and (optional) domain
46 * 2. Send the request to the server and get an SPA challenge
47 * 3. Build the challenge response and send it back.
52 int main (int argc, char ** argv)
54 SPAAuthRequest request;
55 SPAAuthChallenge challenge;
56 SPAAuthResponse response;
59 char *username, *password, *domain, *challenge_str;
63 printf ("Usage: %s <username> <password> [SPA Challenge]\n",
72 spa_build_auth_request (&request, username, domain);
74 spa_bits_to_base64 (msgbuf, US &request,
75 spa_request_length(&request));
77 printf ("SPA Login request for username=%s:\n %s\n",
82 printf ("Run: %s <username> <password> [NTLM Challenge] " \
83 "to complete authenitcation\n", argv [0]);
87 challenge_str = argv [3];
89 if (spa_base64_to_bits (CS &challenge, sizeof(challenge),
90 CCS (challenge_str))<0)
92 printf("bad base64 data in challenge: %s\n", challenge_str);
96 spa_build_auth_response (&challenge, &response, username, password);
97 spa_bits_to_base64 (msgbuf, US &response,
98 spa_request_length(&response));
100 printf ("SPA Response to challenge:\n %s\n for " \
101 "username=%s, password=%s:\n %s\n",
102 argv[3], argv [1], argv [2], msgbuf);
107 * All the client code used here was torn by Marc Prud'hommeaux out of the
108 * Samba project (by Andrew Tridgell, Jeremy Allison, and others).
109 * Previous comments are below:
113 Unix SMB/Netbios implementation.
116 a partial implementation of DES designed for use in the
117 SMB authentication protocol
119 Copyright (C) Andrew Tridgell 1998
121 This program is free software; you can redistribute it and/or modify
122 it under the terms of the GNU General Public License as published by
123 the Free Software Foundation; either version 2 of the License, or
124 (at your option) any later version.
126 This program is distributed in the hope that it will be useful,
127 but WITHOUT ANY WARRANTY; without even the implied warranty of
128 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
129 GNU General Public License for more details.
131 You should have received a copy of the GNU General Public License
132 along with this program; if not, write to the Free Software
133 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
139 This code makes no attempt to be fast! In fact, it is a very
142 This code is NOT a complete DES implementation. It implements only
143 the minimum necessary for SMB authentication, as used by all SMB
144 products (including every copy of Microsoft Windows95 ever sold)
146 In particular, it can only do a unchained forward DES pass. This
147 means it is not possible to use this code for encryption/decryption
148 of data, instead it is only useful as a "hash" algorithm.
150 There is no entry point into this code that allows normal DES operation.
152 I believe this means that this code does not come under ITAR
153 regulations but this is NOT a legal opinion. If you are concerned
154 about the applicability of ITAR regulations to this code then you
155 should confirm it for yourself (and maybe let me know if you come
156 up with a different answer to the one above)
162 #define DEBUG_X(a,b) ;
164 extern int DEBUGLEVEL;
167 #include "auth-spa.h"
172 # define _BYTEORDER_H
174 # define RW_PCVAL(read,inbuf,outbuf,len) \
175 { if (read) { PCVAL (inbuf,0,outbuf,len); } \
176 else { PSCVAL(inbuf,0,outbuf,len); } }
178 # define RW_PIVAL(read,big_endian,inbuf,outbuf,len) \
179 { if (read) { if (big_endian) { RPIVAL(inbuf,0,outbuf,len); } else { PIVAL(inbuf,0,outbuf,len); } } \
180 else { if (big_endian) { RPSIVAL(inbuf,0,outbuf,len); } else { PSIVAL(inbuf,0,outbuf,len); } } }
182 # define RW_PSVAL(read,big_endian,inbuf,outbuf,len) \
183 { if (read) { if (big_endian) { RPSVAL(inbuf,0,outbuf,len); } else { PSVAL(inbuf,0,outbuf,len); } } \
184 else { if (big_endian) { RPSSVAL(inbuf,0,outbuf,len); } else { PSSVAL(inbuf,0,outbuf,len); } } }
186 # define RW_CVAL(read, inbuf, outbuf, offset) \
187 { if (read) { (outbuf) = CVAL (inbuf,offset); } \
188 else { SCVAL(inbuf,offset,outbuf); } }
190 # define RW_IVAL(read, big_endian, inbuf, outbuf, offset) \
191 { if (read) { (outbuf) = ((big_endian) ? RIVAL(inbuf,offset) : IVAL (inbuf,offset)); } \
192 else { if (big_endian) { RSIVAL(inbuf,offset,outbuf); } else { SIVAL(inbuf,offset,outbuf); } } }
194 # define RW_SVAL(read, big_endian, inbuf, outbuf, offset) \
195 { if (read) { (outbuf) = ((big_endian) ? RSVAL(inbuf,offset) : SVAL (inbuf,offset)); } \
196 else { if (big_endian) { RSSVAL(inbuf,offset,outbuf); } else { SSVAL(inbuf,offset,outbuf); } } }
198 # undef CAREFUL_ALIGNMENT
200 /* we know that the 386 can handle misalignment and has the "right"
203 # define CAREFUL_ALIGNMENT 0
206 # ifndef CAREFUL_ALIGNMENT
207 # define CAREFUL_ALIGNMENT 1
210 # define CVAL(buf,pos) ((US (buf))[pos])
211 # define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
212 # define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val))
215 # if CAREFUL_ALIGNMENT
217 # define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
218 # define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
219 # define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
220 # define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
221 # define SVALS(buf,pos) ((int16x)SVAL(buf,pos))
222 # define IVALS(buf,pos) ((int32x)IVAL(buf,pos))
223 # define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16x)(val)))
224 # define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32x)(val)))
225 # define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16x)(val)))
226 # define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32x)(val)))
228 # else /* CAREFUL_ALIGNMENT */
230 /* this handles things for architectures like the 386 that can handle
233 WARNING: This section is dependent on the length of int16x and int32x
237 /* get single value from an SMB buffer */
238 # define SVAL(buf,pos) (*(uint16x *)(CS (buf) + (pos)))
239 # define IVAL(buf,pos) (*(uint32x *)(CS (buf) + (pos)))
240 # define SVALS(buf,pos) (*(int16x *)(CS (buf) + (pos)))
241 # define IVALS(buf,pos) (*(int32x *)(CS (buf) + (pos)))
243 /* store single value in an SMB buffer */
244 # define SSVAL(buf,pos,val) SVAL(buf,pos)=((uint16x)(val))
245 # define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32x)(val))
246 # define SSVALS(buf,pos,val) SVALS(buf,pos)=((int16x)(val))
247 # define SIVALS(buf,pos,val) IVALS(buf,pos)=((int32x)(val))
249 # endif /* CAREFUL_ALIGNMENT */
251 /* macros for reading / writing arrays */
253 # define SMBMACRO(macro,buf,pos,val,len,size) \
254 { for (int l = 0; l < (len); l++) (val)[l] = macro((buf), (pos) + (size)*l); }
256 # define SSMBMACRO(macro,buf,pos,val,len,size) \
257 { for (int l = 0; l < (len); l++) macro((buf), (pos) + (size)*l, (val)[l]); }
259 /* reads multiple data from an SMB buffer */
260 # define PCVAL(buf,pos,val,len) SMBMACRO(CVAL,buf,pos,val,len,1)
261 # define PSVAL(buf,pos,val,len) SMBMACRO(SVAL,buf,pos,val,len,2)
262 # define PIVAL(buf,pos,val,len) SMBMACRO(IVAL,buf,pos,val,len,4)
263 # define PCVALS(buf,pos,val,len) SMBMACRO(CVALS,buf,pos,val,len,1)
264 # define PSVALS(buf,pos,val,len) SMBMACRO(SVALS,buf,pos,val,len,2)
265 # define PIVALS(buf,pos,val,len) SMBMACRO(IVALS,buf,pos,val,len,4)
267 /* stores multiple data in an SMB buffer */
268 # define PSCVAL(buf,pos,val,len) SSMBMACRO(SCVAL,buf,pos,val,len,1)
269 # define PSSVAL(buf,pos,val,len) SSMBMACRO(SSVAL,buf,pos,val,len,2)
270 # define PSIVAL(buf,pos,val,len) SSMBMACRO(SIVAL,buf,pos,val,len,4)
271 # define PSCVALS(buf,pos,val,len) SSMBMACRO(SCVALS,buf,pos,val,len,1)
272 # define PSSVALS(buf,pos,val,len) SSMBMACRO(SSVALS,buf,pos,val,len,2)
273 # define PSIVALS(buf,pos,val,len) SSMBMACRO(SIVALS,buf,pos,val,len,4)
276 /* now the reverse routines - these are used in nmb packets (mostly) */
277 # define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
278 # define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
280 # define RSVAL(buf,pos) SREV(SVAL(buf,pos))
281 # define RSVALS(buf,pos) SREV(SVALS(buf,pos))
282 # define RIVAL(buf,pos) IREV(IVAL(buf,pos))
283 # define RIVALS(buf,pos) IREV(IVALS(buf,pos))
284 # define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
285 # define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
286 # define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
287 # define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
289 /* reads multiple data from an SMB buffer (big-endian) */
290 # define RPSVAL(buf,pos,val,len) SMBMACRO(RSVAL,buf,pos,val,len,2)
291 # define RPIVAL(buf,pos,val,len) SMBMACRO(RIVAL,buf,pos,val,len,4)
292 # define RPSVALS(buf,pos,val,len) SMBMACRO(RSVALS,buf,pos,val,len,2)
293 # define RPIVALS(buf,pos,val,len) SMBMACRO(RIVALS,buf,pos,val,len,4)
295 /* stores multiple data in an SMB buffer (big-endian) */
296 # define RPSSVAL(buf,pos,val,len) SSMBMACRO(RSSVAL,buf,pos,val,len,2)
297 # define RPSIVAL(buf,pos,val,len) SSMBMACRO(RSIVAL,buf,pos,val,len,4)
298 # define RPSSVALS(buf,pos,val,len) SSMBMACRO(RSSVALS,buf,pos,val,len,2)
299 # define RPSIVALS(buf,pos,val,len) SSMBMACRO(RSIVALS,buf,pos,val,len,4)
301 # define DBG_RW_PCVAL(charmode,string,depth,base,read,inbuf,outbuf,len) \
302 { RW_PCVAL(read,inbuf,outbuf,len) \
303 DEBUG_X(5,("%s%04x %s: ", \
304 tab_depth(depth), base,string)); \
305 if (charmode) print_asc(5, US (outbuf), (len)); else \
306 for (int idx = 0; idx < len; idx++) { DEBUG_X(5,("%02x ", (outbuf)[idx])); } \
309 # define DBG_RW_PSVAL(charmode,string,depth,base,read,big_endian,inbuf,outbuf,len) \
310 { RW_PSVAL(read,big_endian,inbuf,outbuf,len) \
311 DEBUG_X(5,("%s%04x %s: ", \
312 tab_depth(depth), base,string)); \
313 if (charmode) print_asc(5, US (outbuf), 2*(len)); else \
314 for (int idx = 0; idx < len; idx++) { DEBUG_X(5,("%04x ", (outbuf)[idx])); } \
317 # define DBG_RW_PIVAL(charmode,string,depth,base,read,big_endian,inbuf,outbuf,len) \
318 { RW_PIVAL(read,big_endian,inbuf,outbuf,len) \
319 DEBUG_X(5,("%s%04x %s: ", \
320 tab_depth(depth), base,string)); \
321 if (charmode) print_asc(5, US (outbuf), 4*(len)); else \
322 for (int idx = 0; idx < len; idx++) { DEBUG_X(5,("%08x ", (outbuf)[idx])); } \
325 # define DBG_RW_CVAL(string,depth,base,read,inbuf,outbuf) \
326 { RW_CVAL(read,inbuf,outbuf,0) \
327 DEBUG_X(5,("%s%04x %s: %02x\n", \
328 tab_depth(depth), base, string, outbuf)); }
330 # define DBG_RW_SVAL(string,depth,base,read,big_endian,inbuf,outbuf) \
331 { RW_SVAL(read,big_endian,inbuf,outbuf,0) \
332 DEBUG_X(5,("%s%04x %s: %04x\n", \
333 tab_depth(depth), base, string, outbuf)); }
335 # define DBG_RW_IVAL(string,depth,base,read,big_endian,inbuf,outbuf) \
336 { RW_IVAL(read,big_endian,inbuf,outbuf,0) \
337 DEBUG_X(5,("%s%04x %s: %08x\n", \
338 tab_depth(depth), base, string, outbuf)); }
340 #endif /* _BYTEORDER_H */
342 void E_P16 (uschar *p14, uschar *p16);
343 void E_P24 (uschar *p21, uschar *c8, uschar *p24);
344 void D_P16 (uschar *p14, uschar *in, uschar *out);
345 void SMBOWFencrypt (uschar passwd[16], uschar * c8, uschar p24[24]);
347 void mdfour (uschar *out, uschar *in, int n);
351 * base64.c -- base-64 conversion routines.
353 * For license terms, see the file COPYING in this directory.
355 * This base 64 encoding is defined in RFC2045 section 6.8,
356 * "Base64 Content-Transfer-Encoding", but lines must not be broken in the
360 static const char base64digits[] =
361 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
363 #define BAD (char) -1
364 static const char base64val[] = {
365 BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
367 BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
369 BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 62, BAD, BAD, BAD,
371 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD, BAD, BAD, BAD, BAD, BAD,
372 BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
373 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD, BAD, BAD, BAD, BAD,
374 BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
375 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD, BAD, BAD, BAD, BAD
377 #define DECODE64(c) (isascii(c) ? base64val[c] : BAD)
380 spa_bits_to_base64 (uschar *out, const uschar *in, int inlen)
381 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
383 for (; inlen >= 3; inlen -= 3)
385 *out++ = base64digits[in[0] >> 2];
386 *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
387 *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
388 *out++ = base64digits[in[2] & 0x3f];
395 *out++ = base64digits[in[0] >> 2];
396 fragment = (in[0] << 4) & 0x30;
398 fragment |= in[1] >> 4;
399 *out++ = base64digits[fragment];
400 *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c];
407 /* The outlength parameter was added by PH, December 2004 */
410 spa_base64_to_bits (char *out, int outlength, const char *in)
411 /* base 64 to raw bytes in quasi-big-endian order, returning count of bytes */
414 uschar digit1, digit2, digit3, digit4;
416 if (in[0] == '+' && in[1] == ' ')
423 if (len >= outlength) /* Added by PH */
424 return -1; /* Added by PH */
426 if (DECODE64 (digit1) == BAD)
429 if (DECODE64 (digit2) == BAD)
432 if (digit3 != '=' && DECODE64 (digit3) == BAD)
435 if (digit4 != '=' && DECODE64 (digit4) == BAD)
438 *out++ = (DECODE64 (digit1) << 2) | (DECODE64 (digit2) >> 4);
442 if (len >= outlength) /* Added by PH */
443 return -1; /* Added by PH */
445 ((DECODE64 (digit2) << 4) & 0xf0) | (DECODE64 (digit3) >> 2);
449 if (len >= outlength) /* Added by PH */
450 return -1; /* Added by PH */
451 *out++ = ((DECODE64 (digit3) << 6) & 0xc0) | DECODE64 (digit4);
456 while (*in && *in != '\r' && digit4 != '=');
462 static uschar perm1[56] = { 57, 49, 41, 33, 25, 17, 9,
463 1, 58, 50, 42, 34, 26, 18,
464 10, 2, 59, 51, 43, 35, 27,
465 19, 11, 3, 60, 52, 44, 36,
466 63, 55, 47, 39, 31, 23, 15,
467 7, 62, 54, 46, 38, 30, 22,
468 14, 6, 61, 53, 45, 37, 29,
469 21, 13, 5, 28, 20, 12, 4
472 static uschar perm2[48] = { 14, 17, 11, 24, 1, 5,
473 3, 28, 15, 6, 21, 10,
474 23, 19, 12, 4, 26, 8,
475 16, 7, 27, 20, 13, 2,
476 41, 52, 31, 37, 47, 55,
477 30, 40, 51, 45, 33, 48,
478 44, 49, 39, 56, 34, 53,
479 46, 42, 50, 36, 29, 32
482 static uschar perm3[64] = { 58, 50, 42, 34, 26, 18, 10, 2,
483 60, 52, 44, 36, 28, 20, 12, 4,
484 62, 54, 46, 38, 30, 22, 14, 6,
485 64, 56, 48, 40, 32, 24, 16, 8,
486 57, 49, 41, 33, 25, 17, 9, 1,
487 59, 51, 43, 35, 27, 19, 11, 3,
488 61, 53, 45, 37, 29, 21, 13, 5,
489 63, 55, 47, 39, 31, 23, 15, 7
492 static uschar perm4[48] = { 32, 1, 2, 3, 4, 5,
494 8, 9, 10, 11, 12, 13,
495 12, 13, 14, 15, 16, 17,
496 16, 17, 18, 19, 20, 21,
497 20, 21, 22, 23, 24, 25,
498 24, 25, 26, 27, 28, 29,
499 28, 29, 30, 31, 32, 1
502 static uschar perm5[32] = { 16, 7, 20, 21,
513 static uschar perm6[64] = { 40, 8, 48, 16, 56, 24, 64, 32,
514 39, 7, 47, 15, 55, 23, 63, 31,
515 38, 6, 46, 14, 54, 22, 62, 30,
516 37, 5, 45, 13, 53, 21, 61, 29,
517 36, 4, 44, 12, 52, 20, 60, 28,
518 35, 3, 43, 11, 51, 19, 59, 27,
519 34, 2, 42, 10, 50, 18, 58, 26,
520 33, 1, 41, 9, 49, 17, 57, 25
524 static uschar sc[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
526 static uschar sbox[8][4][16] = {
527 {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
528 {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
529 {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
530 {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
532 {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
533 {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
534 {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
535 {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
537 {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
538 {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
539 {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
540 {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
542 {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
543 {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
544 {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
545 {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
547 {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
548 {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
549 {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
550 {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
552 {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
553 {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
554 {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
555 {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
557 {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
558 {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
559 {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
560 {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
562 {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
563 {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
564 {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
565 {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}
569 permute (char *out, char *in, uschar * p, int n)
571 for (int i = 0; i < n; i++)
572 out[i] = in[p[i] - 1];
576 lshift (char *d, int count, int n)
579 for (int i = 0; i < n; i++)
580 out[i] = d[(i + count) % n];
581 for (int i = 0; i < n; i++)
586 concat (char *out, char *in1, char *in2, int l1, int l2)
595 xor (char *out, char *in1, char *in2, int n)
597 for (int i = 0; i < n; i++)
598 out[i] = in1[i] ^ in2[i];
602 dohash (char *out, char *in, char *key, int forw)
614 permute (pk1, key, perm1, 56);
616 for (i = 0; i < 28; i++)
618 for (i = 0; i < 28; i++)
621 for (i = 0; i < 16; i++)
623 lshift (c, sc[i], 28);
624 lshift (d, sc[i], 28);
626 concat (cd, c, d, 28, 28);
627 permute (ki[i], cd, perm2, 48);
630 permute (pd1, in, perm3, 64);
632 for (j = 0; j < 32; j++)
638 for (i = 0; i < 16; i++)
647 permute (er, r, perm4, 48);
649 xor (erk, er, ki[forw ? i : 15 - i], 48);
651 for (j = 0; j < 8; j++)
652 for (k = 0; k < 6; k++)
653 b[j][k] = erk[j * 6 + k];
655 for (j = 0; j < 8; j++)
658 m = (b[j][0] << 1) | b[j][5];
660 n = (b[j][1] << 3) | (b[j][2] << 2) | (b[j][3] << 1) | b[j][4];
662 for (k = 0; k < 4; k++)
663 b[j][k] = (sbox[j][m][n] & (1 << (3 - k))) ? 1 : 0;
666 for (j = 0; j < 8; j++)
667 for (k = 0; k < 4; k++)
668 cb[j * 4 + k] = b[j][k];
669 permute (pcb, cb, perm5, 32);
671 xor (r2, l, pcb, 32);
673 for (j = 0; j < 32; j++)
676 for (j = 0; j < 32; j++)
680 concat (rl, r, l, 32, 32);
682 permute (out, rl, perm6, 64);
686 str_to_key (uschar *str, uschar *key)
690 key[0] = str[0] >> 1;
691 key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
692 key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
693 key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
694 key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
695 key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
696 key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
697 key[7] = str[6] & 0x7F;
698 for (i = 0; i < 8; i++)
699 key[i] = (key[i] << 1);
704 smbhash (uschar *out, uschar *in, uschar *key, int forw)
712 str_to_key (key, key2);
714 for (i = 0; i < 64; i++)
716 inb[i] = (in[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
717 keyb[i] = (key2[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
721 dohash (outb, inb, keyb, forw);
723 for (i = 0; i < 8; i++)
726 for (i = 0; i < 64; i++)
728 out[i / 8] |= (1 << (7 - (i % 8)));
732 E_P16 (uschar *p14, uschar *p16)
734 uschar sp8[8] = { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
735 smbhash (p16, sp8, p14, 1);
736 smbhash (p16 + 8, sp8, p14 + 7, 1);
740 E_P24 (uschar *p21, uschar *c8, uschar *p24)
742 smbhash (p24, c8, p21, 1);
743 smbhash (p24 + 8, c8, p21 + 7, 1);
744 smbhash (p24 + 16, c8, p21 + 14, 1);
748 D_P16 (uschar *p14, uschar *in, uschar *out)
750 smbhash (out, in, p14, 0);
751 smbhash (out + 8, in + 8, p14 + 7, 0);
754 /****************************************************************************
755 Like strncpy but always null terminates. Make sure there is room!
756 The variable n should always be one less than the available size.
757 ****************************************************************************/
760 StrnCpy (char *dest, const char *src, size_t n)
770 while (n-- && (*d++ = *src++));
776 skip_multibyte_char (char c)
778 /* bogus if to get rid of unused compiler warning */
786 /*******************************************************************
787 safe string copy into a known length string. maxlength does not
788 include the terminating zero.
789 ********************************************************************/
792 safe_strcpy (char *dest, const char *src, size_t maxlength)
798 DEBUG_X (0, ("ERROR: NULL dest in safe_strcpy\n"));
812 DEBUG_X (0, ("ERROR: string overflow by %d in safe_strcpy [%.50s]\n",
813 (int) (len - maxlength), src));
817 memcpy (dest, src, len);
828 size_t skip = skip_multibyte_char (*s);
833 if (islower ((uschar)(*s)))
842 This implements the X/Open SMB password encryption
843 It takes a password, a 8 byte "crypt key" and puts 24 bytes of
844 encrypted password into p24
848 spa_smb_encrypt (uschar * passwd, uschar * c8, uschar * p24)
850 uschar p14[15], p21[21];
852 memset (p21, '\0', 21);
853 memset (p14, '\0', 14);
854 StrnCpy (CS p14, CS passwd, 14);
859 SMBOWFencrypt (p21, c8, p24);
861 #ifdef DEBUG_PASSWORD
862 DEBUG_X (100, ("spa_smb_encrypt: lm#, challenge, response\n"));
863 dump_data (100, CS p21, 16);
864 dump_data (100, CS c8, 8);
865 dump_data (100, CS p24, 24);
869 /* Routines for Windows NT MD4 Hash functions. */
871 _my_wcslen (int16x * str)
880 * Convert a string into an NT UNICODE string.
881 * Note that regardless of processor type
882 * this must be in intel (little-endian)
887 _my_mbstowcs (int16x * dst, uschar * src, int len)
892 for (i = 0; i < len; i++)
905 * Creates the MD4 Hash of the users password in NT UNICODE.
909 E_md4hash (uschar * passwd, uschar * p16)
914 /* Password cannot be longer than 128 characters */
915 len = strlen (CS passwd);
918 /* Password must be converted to NT unicode */
919 _my_mbstowcs (wpwd, passwd, len);
920 wpwd[len] = 0; /* Ensure string is null terminated */
921 /* Calculate length in bytes */
922 len = _my_wcslen (wpwd) * sizeof (int16x);
924 mdfour (p16, US wpwd, len);
927 /* Does both the NT and LM owfs of a user's password */
929 nt_lm_owf_gen (char *pwd, uschar nt_p16[16], uschar p16[16])
933 memset (passwd, '\0', 130);
934 safe_strcpy (passwd, pwd, sizeof (passwd) - 1);
936 /* Calculate the MD4 hash (NT compatible) of the password */
937 memset (nt_p16, '\0', 16);
938 E_md4hash (US passwd, nt_p16);
940 #ifdef DEBUG_PASSWORD
941 DEBUG_X (100, ("nt_lm_owf_gen: pwd, nt#\n"));
942 dump_data (120, passwd, strlen (passwd));
943 dump_data (100, CS nt_p16, 16);
946 /* Mangle the passwords into Lanman format */
950 /* Calculate the SMB (lanman) hash functions of the password */
952 memset (p16, '\0', 16);
953 E_P16 (US passwd, US p16);
955 #ifdef DEBUG_PASSWORD
956 DEBUG_X (100, ("nt_lm_owf_gen: pwd, lm#\n"));
957 dump_data (120, passwd, strlen (passwd));
958 dump_data (100, CS p16, 16);
960 /* clear out local copy of user's password (just being paranoid). */
961 memset (passwd, '\0', sizeof (passwd));
964 /* Does the des encryption from the NT or LM MD4 hash. */
966 SMBOWFencrypt (uschar passwd[16], uschar * c8, uschar p24[24])
970 memset (p21, '\0', 21);
972 memcpy (p21, passwd, 16);
973 E_P24 (p21, c8, p24);
976 /* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
978 NTLMSSPOWFencrypt (uschar passwd[8], uschar * ntlmchalresp, uschar p24[24])
982 memset (p21, '\0', 21);
983 memcpy (p21, passwd, 8);
984 memset (p21 + 8, 0xbd, 8);
986 E_P24 (p21, ntlmchalresp, p24);
987 #ifdef DEBUG_PASSWORD
988 DEBUG_X (100, ("NTLMSSPOWFencrypt: p21, c8, p24\n"));
989 dump_data (100, CS p21, 21);
990 dump_data (100, CS ntlmchalresp, 8);
991 dump_data (100, CS p24, 24);
996 /* Does the NT MD4 hash then des encryption. */
999 spa_smb_nt_encrypt (uschar * passwd, uschar * c8, uschar * p24)
1003 memset (p21, '\0', 21);
1005 E_md4hash (passwd, p21);
1006 SMBOWFencrypt (p21, c8, p24);
1008 #ifdef DEBUG_PASSWORD
1009 DEBUG_X (100, ("spa_smb_nt_encrypt: nt#, challenge, response\n"));
1010 dump_data (100, CS p21, 16);
1011 dump_data (100, CS c8, 8);
1012 dump_data (100, CS p24, 24);
1016 static uint32x A, B, C, D;
1019 F (uint32x X, uint32x Y, uint32x Z)
1021 return (X & Y) | ((~X) & Z);
1025 G (uint32x X, uint32x Y, uint32x Z)
1027 return (X & Y) | (X & Z) | (Y & Z);
1031 H (uint32x X, uint32x Y, uint32x Z)
1037 lshift_a (uint32x x, int s)
1040 return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
1043 #define ROUND1(a,b,c,d,k,s) a = lshift_a(a + F(b,c,d) + X[k], s)
1044 #define ROUND2(a,b,c,d,k,s) a = lshift_a(a + G(b,c,d) + X[k] + (uint32x)0x5A827999,s)
1045 #define ROUND3(a,b,c,d,k,s) a = lshift_a(a + H(b,c,d) + X[k] + (uint32x)0x6ED9EBA1,s)
1047 /* this applies md4 to 64 byte chunks */
1049 spa_mdfour64 (uint32x * M)
1052 uint32x AA, BB, CC, DD;
1055 for (j = 0; j < 16; j++)
1063 ROUND1 (A, B, C, D, 0, 3);
1064 ROUND1 (D, A, B, C, 1, 7);
1065 ROUND1 (C, D, A, B, 2, 11);
1066 ROUND1 (B, C, D, A, 3, 19);
1067 ROUND1 (A, B, C, D, 4, 3);
1068 ROUND1 (D, A, B, C, 5, 7);
1069 ROUND1 (C, D, A, B, 6, 11);
1070 ROUND1 (B, C, D, A, 7, 19);
1071 ROUND1 (A, B, C, D, 8, 3);
1072 ROUND1 (D, A, B, C, 9, 7);
1073 ROUND1 (C, D, A, B, 10, 11);
1074 ROUND1 (B, C, D, A, 11, 19);
1075 ROUND1 (A, B, C, D, 12, 3);
1076 ROUND1 (D, A, B, C, 13, 7);
1077 ROUND1 (C, D, A, B, 14, 11);
1078 ROUND1 (B, C, D, A, 15, 19);
1080 ROUND2 (A, B, C, D, 0, 3);
1081 ROUND2 (D, A, B, C, 4, 5);
1082 ROUND2 (C, D, A, B, 8, 9);
1083 ROUND2 (B, C, D, A, 12, 13);
1084 ROUND2 (A, B, C, D, 1, 3);
1085 ROUND2 (D, A, B, C, 5, 5);
1086 ROUND2 (C, D, A, B, 9, 9);
1087 ROUND2 (B, C, D, A, 13, 13);
1088 ROUND2 (A, B, C, D, 2, 3);
1089 ROUND2 (D, A, B, C, 6, 5);
1090 ROUND2 (C, D, A, B, 10, 9);
1091 ROUND2 (B, C, D, A, 14, 13);
1092 ROUND2 (A, B, C, D, 3, 3);
1093 ROUND2 (D, A, B, C, 7, 5);
1094 ROUND2 (C, D, A, B, 11, 9);
1095 ROUND2 (B, C, D, A, 15, 13);
1097 ROUND3 (A, B, C, D, 0, 3);
1098 ROUND3 (D, A, B, C, 8, 9);
1099 ROUND3 (C, D, A, B, 4, 11);
1100 ROUND3 (B, C, D, A, 12, 15);
1101 ROUND3 (A, B, C, D, 2, 3);
1102 ROUND3 (D, A, B, C, 10, 9);
1103 ROUND3 (C, D, A, B, 6, 11);
1104 ROUND3 (B, C, D, A, 14, 15);
1105 ROUND3 (A, B, C, D, 1, 3);
1106 ROUND3 (D, A, B, C, 9, 9);
1107 ROUND3 (C, D, A, B, 5, 11);
1108 ROUND3 (B, C, D, A, 13, 15);
1109 ROUND3 (A, B, C, D, 3, 3);
1110 ROUND3 (D, A, B, C, 11, 9);
1111 ROUND3 (C, D, A, B, 7, 11);
1112 ROUND3 (B, C, D, A, 15, 15);
1124 for (j = 0; j < 16; j++)
1129 copy64 (uint32x * M, uschar *in)
1133 for (i = 0; i < 16; i++)
1134 M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) |
1135 (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0);
1139 copy4 (uschar *out, uint32x x)
1142 out[1] = (x >> 8) & 0xFF;
1143 out[2] = (x >> 16) & 0xFF;
1144 out[3] = (x >> 24) & 0xFF;
1147 /* produce a md4 message digest from data of length n bytes */
1149 mdfour (uschar *out, uschar *in, int n)
1169 for (i = 0; i < 128; i++)
1171 memcpy (buf, in, n);
1176 copy4 (buf + 56, b);
1182 copy4 (buf + 120, b);
1185 copy64 (M, buf + 64);
1189 for (i = 0; i < 128; i++)
1196 copy4 (out + 12, D);
1201 char versionString[] = "libntlm version 0.21";
1203 /* Utility routines that handle NTLM auth structures. */
1205 /* The [IS]VAL macros are to take care of byte order for non-Intel
1206 * Machines -- I think this file is OK, but it hasn't been tested.
1207 * The other files (the ones stolen from Samba) should be OK.
1211 /* I am not crazy about these macros -- they seem to have gotten
1212 * a bit complex. A new scheme for handling string/buffer fields
1213 * in the structures probably needs to be designed
1216 #define spa_bytes_add(ptr, header, buf, count) \
1218 if ( buf && (count) != 0 /* we hate -Wint-in-bool-contex */ \
1219 && ptr->bufIndex + count < sizeof(ptr->buffer) \
1222 SSVAL(&ptr->header.len,0,count); \
1223 SSVAL(&ptr->header.maxlen,0,count); \
1224 SIVAL(&ptr->header.offset,0,((ptr->buffer - ((uint8x*)ptr)) + ptr->bufIndex)); \
1225 memcpy(ptr->buffer+ptr->bufIndex, buf, count); \
1226 ptr->bufIndex += count; \
1231 ptr->header.maxlen = 0; \
1232 SIVAL(&ptr->header.offset,0,((ptr->buffer - ((uint8x*)ptr)) + ptr->bufIndex)); \
1236 #define spa_string_add(ptr, header, string) \
1238 uschar * p = string; \
1240 if (p) len = Ustrlen(p); \
1241 spa_bytes_add(ptr, header, p, len); \
1244 #define spa_unicode_add_string(ptr, header, string) \
1246 uschar * p = string; \
1247 uschar * b = NULL; \
1252 b = US strToUnicode(CS p); \
1254 spa_bytes_add(ptr, header, b, len*2); \
1260 #define DumpBuffer(fp, structPtr, header) \
1261 dumpRaw(fp,(US structPtr)+IVAL(&structPtr->header.offset,0),SVAL(&structPtr->header.len,0))
1265 dumpRaw (FILE * fp, uschar *buf, size_t len)
1269 for (i = 0; i < len; ++i)
1270 fprintf (fp, "%02x ", buf[i]);
1278 unicodeToString (char *p, size_t len)
1281 static char buf[1024];
1283 assert (len + 1 < sizeof buf);
1285 for (i = 0; i < len; ++i)
1296 strToUnicode (char *p)
1298 static uschar buf[1024];
1299 size_t l = strlen (p);
1302 assert (l * 2 < sizeof buf);
1314 toString (char *p, size_t len)
1316 static uschar buf[1024];
1318 assert (len + 1 < sizeof buf);
1320 memcpy (buf, p, len);
1325 static inline uschar *
1326 get_challenge_unistr(SPAAuthChallenge * challenge, SPAStrHeader * hdr)
1328 int off = IVAL(&hdr->offset, 0);
1329 int len = SVAL(&hdr->len, 0);
1330 return off + len < sizeof(SPAAuthChallenge)
1331 ? US unicodeToString(CS challenge + off, len/2) : US"";
1334 static inline uschar *
1335 get_challenge_str(SPAAuthChallenge * challenge, SPAStrHeader * hdr)
1337 int off = IVAL(&hdr->offset, 0);
1338 int len = SVAL(&hdr->len, 0);
1339 return off + len < sizeof(SPAAuthChallenge)
1340 ? US toString(CS challenge + off, len) : US"";
1345 #define GetUnicodeString(structPtr, header) \
1346 unicodeToString(((char*)structPtr) + IVAL(&structPtr->header.offset,0) , SVAL(&structPtr->header.len,0)/2)
1348 #define GetString(structPtr, header) \
1349 toString(((CS structPtr) + IVAL(&structPtr->header.offset,0)), SVAL(&structPtr->header.len,0))
1353 dumpSmbNtlmAuthRequest (FILE * fp, SPAAuthRequest * request)
1355 fprintf (fp, "NTLM Request:\n");
1356 fprintf (fp, " Ident = %s\n", request->ident);
1357 fprintf (fp, " mType = %d\n", IVAL (&request->msgType, 0));
1358 fprintf (fp, " Flags = %08x\n", IVAL (&request->flags, 0));
1359 fprintf (fp, " User = %s\n", GetString (request, user));
1360 fprintf (fp, " Domain = %s\n", GetString (request, domain));
1364 dumpSmbNtlmAuthChallenge (FILE * fp, SPAAuthChallenge * challenge)
1366 fprintf (fp, "NTLM Challenge:\n");
1367 fprintf (fp, " Ident = %s\n", challenge->ident);
1368 fprintf (fp, " mType = %d\n", IVAL (&challenge->msgType, 0));
1369 fprintf (fp, " Domain = %s\n", GetUnicodeString (challenge, uDomain));
1370 fprintf (fp, " Flags = %08x\n", IVAL (&challenge->flags, 0));
1371 fprintf (fp, " Challenge = ");
1372 dumpRaw (fp, challenge->challengeData, 8);
1376 dumpSmbNtlmAuthResponse (FILE * fp, SPAAuthResponse * response)
1378 fprintf (fp, "NTLM Response:\n");
1379 fprintf (fp, " Ident = %s\n", response->ident);
1380 fprintf (fp, " mType = %d\n", IVAL (&response->msgType, 0));
1381 fprintf (fp, " LmResp = ");
1382 DumpBuffer (fp, response, lmResponse);
1383 fprintf (fp, " NTResp = ");
1384 DumpBuffer (fp, response, ntResponse);
1385 fprintf (fp, " Domain = %s\n", GetUnicodeString (response, uDomain));
1386 fprintf (fp, " User = %s\n", GetUnicodeString (response, uUser));
1387 fprintf (fp, " Wks = %s\n", GetUnicodeString (response, uWks));
1388 fprintf (fp, " sKey = ");
1389 DumpBuffer (fp, response, sessionKey);
1390 fprintf (fp, " Flags = %08x\n", IVAL (&response->flags, 0));
1395 spa_build_auth_request (SPAAuthRequest * request, uschar * user, uschar * domain)
1397 uschar * u = string_copy(user);
1398 uschar * p = Ustrchr(u, '@');
1407 request->bufIndex = 0;
1408 memcpy (request->ident, "NTLMSSP\0\0\0", 8);
1409 SIVAL (&request->msgType, 0, 1);
1410 SIVAL (&request->flags, 0, 0x0000b207); /* have to figure out what these mean */
1411 spa_string_add (request, user, u);
1412 spa_string_add (request, domain, domain);
1418 spa_build_auth_challenge (SPAAuthRequest * request, SPAAuthChallenge * challenge)
1422 int p = (int)getpid();
1423 int random_seed = (int)time(NULL) ^ ((p << 16) | p);
1425 /* Ensure challenge data is cleared, in case it isn't all used. This
1426 patch added by PH on suggestion of Russell King */
1428 memset(challenge, 0, sizeof(SPAAuthChallenge));
1430 challenge->bufIndex = 0;
1431 memcpy (challenge->ident, "NTLMSSP\0", 8);
1432 SIVAL (&challenge->msgType, 0, 2);
1433 SIVAL (&challenge->flags, 0, 0x00008201);
1434 SIVAL (&challenge->uDomain.len, 0, 0x0000);
1435 SIVAL (&challenge->uDomain.maxlen, 0, 0x0000);
1436 SIVAL (&challenge->uDomain.offset, 0, 0x00002800);
1438 /* generate eight pseudo random bytes (method ripped from host.c) */
1442 chalstr[i] = (uschar)(random_seed >> 16) % 256;
1443 random_seed = (1103515245 - (chalstr[i])) * random_seed + 12345;
1446 memcpy(challenge->challengeData,chalstr,8);
1452 /* This is the original source of this function, preserved here for reference.
1453 The new version below was re-organized by PH following a patch and some further
1454 suggestions from Mark Lyda to fix the problem that is described at the head of
1455 this module. At the same time, I removed the untidiness in the code below that
1456 involves the "d" and "domain" variables. */
1460 spa_build_auth_response (SPAAuthChallenge * challenge,
1461 SPAAuthResponse * response, char *user,
1464 uint8x lmRespData[24];
1465 uint8x ntRespData[24];
1466 char *d = strdup (GetUnicodeString (challenge, uDomain));
1468 char *u = strdup (user);
1469 char *p = strchr (u, '@');
1477 spa_smb_encrypt (US password, challenge->challengeData, lmRespData);
1478 spa_smb_nt_encrypt (US password, challenge->challengeData, ntRespData);
1480 response->bufIndex = 0;
1481 memcpy (response->ident, "NTLMSSP\0\0\0", 8);
1482 SIVAL (&response->msgType, 0, 3);
1484 spa_bytes_add (response, lmResponse, lmRespData, 24);
1485 spa_bytes_add (response, ntResponse, ntRespData, 24);
1486 spa_unicode_add_string (response, uDomain, domain);
1487 spa_unicode_add_string (response, uUser, u);
1488 spa_unicode_add_string (response, uWks, u);
1489 spa_string_add (response, sessionKey, NULL);
1491 response->flags = challenge->flags;
1499 /* This is the re-organized version (see comments above) */
1502 spa_build_auth_response (SPAAuthChallenge * challenge,
1503 SPAAuthResponse * response, uschar * user,
1506 uint8x lmRespData[24];
1507 uint8x ntRespData[24];
1508 uint32x cf = IVAL(&challenge->flags, 0);
1509 uschar * u = string_copy(user);
1510 uschar * p = Ustrchr(u, '@');
1520 else domain = d = string_copy(cf & 0x1
1521 ? CUS get_challenge_unistr(challenge, &challenge->uDomain)
1522 : CUS get_challenge_str(challenge, &challenge->uDomain));
1524 spa_smb_encrypt(password, challenge->challengeData, lmRespData);
1525 spa_smb_nt_encrypt(password, challenge->challengeData, ntRespData);
1527 response->bufIndex = 0;
1528 memcpy (response->ident, "NTLMSSP\0\0\0", 8);
1529 SIVAL (&response->msgType, 0, 3);
1531 spa_bytes_add(response, lmResponse, lmRespData, cf & 0x200 ? 24 : 0);
1532 spa_bytes_add(response, ntResponse, ntRespData, cf & 0x8000 ? 24 : 0);
1534 if (cf & 0x1) { /* Unicode Text */
1535 spa_unicode_add_string(response, uDomain, domain);
1536 spa_unicode_add_string(response, uUser, u);
1537 spa_unicode_add_string(response, uWks, u);
1538 } else { /* OEM Text */
1539 spa_string_add(response, uDomain, domain);
1540 spa_string_add(response, uUser, u);
1541 spa_string_add(response, uWks, u);
1544 spa_string_add(response, sessionKey, NULL);
1545 response->flags = challenge->flags;
1549 #endif /*!MACRO_PREDEF*/