1 /**
2  * HTTP Authentication
3  *
4  * Copyright (C) 2002-2016 by
5  * Jeffrey Fulmer - <jeff@joedog.org>, et al.
6  * This file is distributed as part of Siege
7  *
8  * Additional copyright information is noted below
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  *--
24  */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <base64.h>
29 #include <md5.h>
30 #include <auth.h>
31 #include <array.h>
32 #include <util.h>
33 #include <pthread.h>
34 #include <setup.h>
35 #include <memory.h>
36 #include <notify.h>
37 
38 #ifdef HAVE_SSL
39 #include <openssl/des.h>
40 #include <openssl/md4.h>
41 #include <openssl/opensslv.h>
42 # if OPENSSL_VERSION_NUMBER < 0x00907001L
43 #  define DES_key_schedule des_key_schedule
44 #  define DES_cblock des_cblock
45 #  define DES_set_odd_parity des_set_odd_parity
46 #  define DES_set_key des_set_key
47 #  define DES_ecb_encrypt des_ecb_encrypt
48 #  define DESKEY(x) x
49 #  define DESKEYARG(x) x
50 # else
51 #  define DESKEYARG(x) *x
52 #  define DESKEY(x) &x
53 # endif
54 #endif
55 
56 typedef enum
57 {
58   TYPE_N = 0,
59   TYPE_1 = 1,
60   TYPE_2 = 2,
61   TYPE_3 = 3,
62   TYPE_L = 4
63 } STATE;
64 
65 struct AUTH_T {
66   ARRAY   creds;
67   BOOLEAN okay;
68   struct {
69     char *encode;
70   } basic;
71   struct {
72     char *encode;
73   } digest;
74   struct {
75     STATE state;
76     char *encode;
77     BOOLEAN  ready;
78     unsigned char nonce[8];
79   } ntlm;
80   struct {
81     BOOLEAN required;   /* boolean, TRUE == use a proxy server.    */
82     char *hostname;     /* hostname for the proxy server.          */
83     int  port;          /* port number for proxysrv                */
84     char *encode;       /* base64 encoded username and password    */
85   } proxy;
86   pthread_mutex_t lock;
87 };
88 
89 struct DIGEST_CRED {
90   char *username;
91   char *password;
92   char *cnonce_value;
93   char *h_a1;
94   char nc[9];
95   unsigned int nc_value;
96 };
97 
98 struct DIGEST_CHLG {
99  char *realm;
100  char *domain;
101  char *nonce;
102  char *opaque;
103  char *stale;
104  char *algorithm;
105  char *qop;
106 };
107 
108 typedef enum {
109   REALM,
110   DOMAIN,
111   NONCE,
112   OPAQUE,
113   STALE,
114   ALGORITHM,
115   QOP,
116   UNKNOWN
117 } KEY_HEADER_E;
118 
119 /**
120  * Flag bits definitions available at on
121  * http://davenport.sourceforge.net/ntlm.html
122  */
123 #define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
124 #define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
125 #define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
126 
127 size_t AUTHSIZE = sizeof(struct AUTH_T);
128 
129 private BOOLEAN       __basic_header(AUTH this, SCHEME scheme, CREDS creds);
130 private BOOLEAN       __ntlm_header(AUTH this, SCHEME scheme, const char *header, CREDS creds);
131 private DCHLG *       __digest_challenge(const char *challenge);
132 private DCRED *       __digest_credentials(CREDS creds, unsigned int *randseed);
133 private KEY_HEADER_E  __get_keyval(const char *key);
134 private char *        __get_random_string(size_t length, unsigned int *randseed);
135 private char *        __get_h_a1(const DCHLG *chlg, DCRED *cred, const char *nonce_value);
136 private char *        __get_md5_str(const char *buf);
137 private BOOLEAN       __str_list_contains(const char *str, const char *pattern, size_t pattern_len);
138 #ifdef HAVE_SSL
139 private void          __mkhash(const char *pass, unsigned char *nonce, unsigned char *lmresp, unsigned char *ntresp);
140 #endif/*HAVE_SSL*/
141 
142 AUTH
new_auth()143 new_auth()
144 {
145   AUTH this;
146 
147   this = calloc(AUTHSIZE, 1);
148   this->creds  = new_array();
149   this->basic.encode  = NULL;
150   this->digest.encode = NULL;
151   this->ntlm.encode   = NULL;
152   this->ntlm.state    = TYPE_N;
153   this->proxy.encode  = NULL;
154   return this;
155 }
156 
157 AUTH
auth_destroy(AUTH this)158 auth_destroy(AUTH this)
159 {
160   this->creds = array_destroy(this->creds);
161   xfree(this->basic.encode);
162   xfree(this->digest.encode);
163   xfree(this->ntlm.encode);
164   xfree(this->proxy.encode);
165   xfree(this);
166   return NULL;
167 }
168 
169 void
auth_add(AUTH this,CREDS creds)170 auth_add(AUTH this, CREDS creds)
171 {
172   array_npush(this->creds, creds, CREDSIZE);
173   return;
174 }
175 
176 void
auth_display(AUTH this,SCHEME scheme)177 auth_display(AUTH this, SCHEME scheme)
178 {
179   size_t  i;
180   char    space[] = "                                ";
181   BOOLEAN first   = TRUE;
182   //XXX: Needs to be reformatted for siege -C
183   for (i = 0; i < array_length(this->creds); i++) {
184     CREDS tmp = array_get(this->creds, i);
185     if (creds_get_scheme(tmp) == scheme) {
186       printf("%scredentials:  %s:%s:%s\n", first==TRUE?"":space, creds_get_username(tmp), creds_get_password(tmp), creds_get_realm(tmp));
187       first = FALSE;
188     }
189   }
190 }
191 
192 char *
auth_get_basic_header(AUTH this,SCHEME scheme)193 auth_get_basic_header(AUTH this, SCHEME scheme)
194 {
195   if (scheme == PROXY) {
196     return this->proxy.encode;
197   } else {
198     return this->basic.encode;
199   }
200 }
201 
202 BOOLEAN
auth_set_basic_header(AUTH this,SCHEME scheme,char * realm)203 auth_set_basic_header(AUTH this, SCHEME scheme, char *realm)
204 {
205   size_t i;
206   for (i = 0; i < array_length(this->creds); i++) {
207     CREDS tmp = array_get(this->creds, i);
208     if (realm == NULL) break;
209     if (strmatch(creds_get_realm(tmp), realm)) {
210       if (creds_get_scheme(tmp) == scheme) {
211         return __basic_header(this, scheme, tmp);
212       }
213     }
214   }
215   /**
216    * didn't match a realm, trying 'any'
217    */
218   for (i = 0; i < array_length(this->creds); i++) {
219     CREDS tmp = array_get(this->creds, i);
220     if (strmatch(creds_get_realm(tmp), "any")) {
221       if (creds_get_scheme(tmp) == scheme) {
222         return __basic_header(this, scheme, tmp);
223       }
224     }
225   }
226   return FALSE;
227 }
228 
229 BOOLEAN
auth_set_ntlm_header(AUTH this,SCHEME scheme,char * header,char * realm)230 auth_set_ntlm_header(AUTH this, SCHEME scheme, char *header, char *realm)
231 {
232   size_t i;
233   for (i = 0; i < array_length(this->creds); i++) {
234     CREDS tmp = array_get(this->creds, i);
235     if (realm == NULL) break;
236     if (strmatch(creds_get_realm(tmp), realm)) {
237       if (creds_get_scheme(tmp) == HTTP || creds_get_scheme(tmp) == HTTPS) {
238         return __ntlm_header(this, scheme, header, tmp);
239       }
240     }
241   }
242   /**
243    * didn't match a realm, trying 'any'
244    */
245   for (i = 0; i < array_length(this->creds); i++) {
246     CREDS tmp = array_get(this->creds, i);
247     if (strmatch(creds_get_realm(tmp), "any")) {
248       if (creds_get_scheme(tmp) == HTTP || creds_get_scheme(tmp) == HTTPS) {
249         return __ntlm_header(this, scheme, header, tmp);
250       }
251     }
252   }
253   return FALSE;
254 }
255 
256 char *
auth_get_ntlm_header(AUTH this,SCHEME scheme)257 auth_get_ntlm_header(AUTH this, SCHEME scheme)
258 {
259   if (scheme == PROXY) {
260     return this->proxy.encode;
261   } else {
262     return this->ntlm.encode;
263   }
264 }
265 
266 char *
auth_get_digest_header(AUTH this,SCHEME scheme,DCHLG * chlg,DCRED * cred,const char * method,const char * uri)267 auth_get_digest_header(AUTH this, SCHEME scheme, DCHLG *chlg, DCRED *cred, const char *method, const char *uri)
268 {
269   size_t len;
270   char  *cnonce         = NULL;
271   char  *nonce_count    = NULL;
272   char  *qop            = NULL;
273   char  *response       = NULL;
274   char  *request_digest = NULL;
275   char  *h_a1           = NULL;
276   char  *h_a2           = NULL;
277   char  *opaque         = NULL;
278   char  *result         = NULL;
279   char  *tmp            = NULL;
280 
281   /**
282    * The user probably didn't set login credentials.
283    * We'll return "" here and display a message after
284    * the authorization failure.
285    */
286   if (chlg == NULL || cred == NULL) return "";
287 
288   if (chlg != NULL && chlg->qop != NULL) {
289     nonce_count = xstrcat(", nc=", cred->nc, NULL);
290     cnonce = xstrcat(", cnonce=\"", cred->cnonce_value, "\"", NULL);
291 
292     if (NULL == (h_a1 = __get_h_a1(chlg, cred, chlg->nonce))) {
293       fprintf(stderr, "error calling __get_h_a1\n");
294       return NULL;
295     }
296 
297     if (__str_list_contains(chlg->qop, "auth", 4)) {
298       qop = xstrdup(", qop=auth");
299       tmp = xstrcat(method, ":", uri, NULL);
300       h_a2 = __get_md5_str(tmp);
301       xfree(tmp);
302 
303       tmp = xstrcat(h_a1,":",chlg->nonce,":",cred->nc,":",cred->cnonce_value,":auth:",h_a2,NULL);
304       request_digest = __get_md5_str(tmp);
305       xfree(tmp);
306       response = xstrcat(", response=\"", request_digest, "\"", NULL);
307     } else {
308       fprintf(stderr, "error quality of protection not supported: %s\n", chlg->qop);
309       return NULL;
310     }
311   } else {
312     if (NULL == (h_a1 = __get_h_a1(chlg, cred, ""))) {
313       NOTIFY(ERROR, "__get_h_a1\n");
314       return NULL;
315     }
316     tmp = xstrcat(method, ":", uri, NULL);
317     h_a2 = __get_md5_str(tmp);
318     xfree(tmp);
319     tmp = xstrcat(h_a1, ":", chlg->nonce, ":", h_a2, NULL);
320     request_digest = __get_md5_str(tmp);
321     xfree(tmp);
322     response = xstrcat(" response=\"", request_digest, "\"", NULL);
323   }
324   if (chlg != NULL && chlg->opaque != NULL)
325     opaque = xstrcat(", opaque=\"", chlg->opaque, "\"", NULL);
326 
327   result = xstrcat (
328     "Digest username=\"", cred->username, "\", realm=\"", chlg->realm, "\", nonce=\"", chlg->nonce,
329     "\", uri=\"", uri, "\", algorithm=", chlg->algorithm, response, opaque ? opaque : "", qop ? qop : "",
330     nonce_count ? nonce_count : "", cnonce ? cnonce : "", NULL
331   );
332 
333   (cred->nc_value)++;
334   snprintf(cred->nc, sizeof(cred->nc), "%.8x", cred->nc_value);
335 
336   if (0 == strcasecmp("MD5", chlg->algorithm))
337     xfree(h_a1);
338 
339   xfree(nonce_count);
340   xfree(cnonce);
341   xfree(qop);
342   xfree(response);
343   xfree(request_digest);
344   xfree(h_a2);
345   xfree(opaque);
346 
347   len = strlen(result)+32;
348 
349   if (scheme == PROXY) {
350     this->proxy.encode = xmalloc(len);
351     memset(this->proxy.encode, '\0', len);
352     snprintf(this->proxy.encode, len, "Proxy-Authorization: %s\015\012", result);
353     xfree(result);
354     return this->proxy.encode;
355   } else {
356     this->digest.encode = xmalloc(len);
357     memset(this->digest.encode, '\0', len);
358     snprintf(this->digest.encode, len, "Authorization: %s\015\012", result);
359     xfree(result);
360     return this->digest.encode;
361   }
362 }
363 
364 BOOLEAN
auth_set_digest_header(AUTH this,DCHLG ** chlg,DCRED ** cred,unsigned int * rand,char * realm,char * str)365 auth_set_digest_header(AUTH this, DCHLG **chlg, DCRED **cred, unsigned int *rand, char *realm, char *str) {
366   size_t  i;
367   for (i = 0; i < array_length(this->creds); i++) {
368     CREDS tmp = array_get(this->creds, i);
369     if (realm == NULL) break;
370     if (strmatch(creds_get_realm(tmp), realm)) {
371       *chlg = __digest_challenge(str);
372       *cred = __digest_credentials(tmp, rand);
373       if (*cred == NULL || *chlg == NULL) return FALSE;
374       return TRUE;
375     }
376   }
377   /**
378    * didn't match a realm, trying 'any'
379    */
380   for (i = 0; i < array_length(this->creds); i++) {
381     CREDS tmp = array_get(this->creds, i);
382     if (strmatch(creds_get_realm(tmp), "any")) {
383       *chlg = __digest_challenge(str);
384       *cred = __digest_credentials(tmp, rand);
385       if (*cred == NULL || *chlg == NULL) return FALSE;
386       return TRUE;
387     }
388   }
389   return FALSE;
390 }
391 
392 BOOLEAN
auth_get_proxy_required(AUTH this)393 auth_get_proxy_required(AUTH this)
394 {
395   if (this == NULL)
396     return FALSE;
397   return this->proxy.required;
398 }
399 
400 char *
auth_get_proxy_host(AUTH this)401 auth_get_proxy_host(AUTH this)
402 {
403   return this->proxy.hostname;
404 }
405 
406 int
auth_get_proxy_port(AUTH this)407 auth_get_proxy_port(AUTH this)
408 {
409   return this->proxy.port;
410 }
411 
412 void
auth_set_proxy_required(AUTH this,BOOLEAN required)413 auth_set_proxy_required(AUTH this, BOOLEAN required)
414 {
415   this->proxy.required = required;
416 }
417 
418 void
auth_set_proxy_host(AUTH this,char * host)419 auth_set_proxy_host(AUTH this, char *host)
420 {
421   this->proxy.hostname = xstrdup(host);
422   this->proxy.required = TRUE;
423 }
424 
425 void
auth_set_proxy_port(AUTH this,int port)426 auth_set_proxy_port(AUTH this, int port)
427 {
428   this->proxy.port = port;
429 }
430 
431 char *
auth_get_ftp_username(AUTH this,char * realm)432 auth_get_ftp_username(AUTH this, char *realm)
433 {
434   size_t  i;
435   for (i = 0; i < array_length(this->creds); i++) {
436     CREDS tmp = array_get(this->creds, i);
437     if (strmatch(creds_get_realm(tmp), realm)) {
438       if (creds_get_scheme(tmp) == FTP) {
439         return creds_get_username(tmp);
440       }
441     }
442   }
443   /**
444    * didn't match a realm, trying 'any'
445    */
446   for (i = 0; i < array_length(this->creds); i++) {
447     CREDS tmp = array_get(this->creds, i);
448     if (strmatch(creds_get_realm(tmp), "any")) {
449       if (creds_get_scheme(tmp) == FTP) {
450         return creds_get_username(tmp);
451       }
452     }
453   }
454   return "";
455 }
456 
457 char *
auth_get_ftp_password(AUTH this,char * realm)458 auth_get_ftp_password(AUTH this, char *realm)
459 {
460   size_t i;
461   for (i = 0; i < array_length(this->creds); i++) {
462     CREDS tmp = array_get(this->creds, i);
463     if (strmatch(creds_get_realm(tmp), realm)) {
464       if (creds_get_scheme(tmp) == FTP) {
465         return creds_get_password(tmp);
466       }
467     }
468   }
469   /**
470    * didn't match a realm, trying 'any'
471    */
472   for (i = 0; i < array_length(this->creds); i++) {
473     CREDS tmp = array_get(this->creds, i);
474     if (strmatch(creds_get_realm(tmp), "any")) {
475       if (creds_get_scheme(tmp) == FTP) {
476         return creds_get_password(tmp);
477       }
478     }
479   }
480   return "";
481 }
482 
483 private BOOLEAN
__basic_header(AUTH this,SCHEME scheme,CREDS creds)484 __basic_header(AUTH this, SCHEME scheme, CREDS creds)
485 {
486   char    buf[256];
487   char   *hdr;
488   size_t  len;
489   BOOLEAN ret = TRUE;
490 
491   memset(buf, '\0', sizeof(buf));
492   pthread_mutex_lock(&(this->lock));
493   snprintf(buf, sizeof buf,"%s:%s",creds_get_username(creds), creds_get_password(creds));
494   if (scheme==PROXY) {
495     xfree(this->proxy.encode);
496     if ((base64_encode(buf, strlen(buf), &hdr) < 0)) {
497       ret = FALSE;
498     } else {
499       len = strlen(hdr)+32;
500       this->proxy.encode = xmalloc(len);
501       memset(this->proxy.encode, '\0', len);
502       snprintf(this->proxy.encode, len, "Proxy-Authorization: Basic %s\015\012", hdr);
503     }
504   } else {
505     xfree(this->basic.encode);
506     if ((base64_encode(buf, strlen(buf), &hdr) < 0 )) {
507       ret = FALSE;
508     } else {
509       len = strlen(hdr)+32;
510       this->basic.encode = xmalloc(len);
511       memset(this->basic.encode, '\0', len);
512       snprintf(this->basic.encode, len, "Authorization: Basic %s\015\012", hdr);
513     }
514   }
515   pthread_mutex_unlock(&(this->lock));
516   return ret;
517 }
518 
519 /**
520  * NTLM authentication is based on Daniel Stenberg's
521  * implementation from libcurl and contribution to wget.
522  * See: http://curl.haxx.se and http://www.gnu.org/software/wget/
523  * Copyright (c) 1996 - 2015, Daniel Stenberg, daniel@haxx.se.
524  * MIT (or Modified BSD)-style license.
525  * Copyright (C) 2011 by Free Software Foundation
526  * Modified for Siege by J. Fulmer
527  * Copyright (C) 2015 GNU Public License v3
528  */
529 #define SHORTPAIR(x) (char) ((x) & 0xff), (char) ((x) >> 8)
530 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
531   (((x) >>16)&0xff), ((x)>>24)
532 
533 private BOOLEAN
__ntlm_header(AUTH this,SCHEME scheme,const char * header,CREDS creds)534 __ntlm_header(AUTH this, SCHEME scheme, const char *header, CREDS creds)
535 {
536 #ifndef HAVE_SSL
537   NOTIFY(
538     ERROR, "NTLM authentication requires libssl: %d, %d, %s, %s",
539     this->okay, scheme, header, creds_get_username(creds)
540   ); // this message is mainly intended to silence the compiler
541   return FALSE;
542 #else
543   size_t size        = 0;
544   size_t final       = 0;
545   const char *domstr = "";
546   const char *srvstr = "";
547   size_t domlen      = strlen(domstr);
548   size_t srvlen      = strlen(srvstr);
549   size_t srvoff;
550   size_t domoff;
551   char *hdr;
552   char  tmp[BUFSIZ];
553   char  buf[256];
554 
555   /**
556    * The header is head->auth.challenge.www which was set in client.c
557    * It includes everything after 'WWW-Authenticate: ' which means it
558    * must begin NTLM for this authentication mechanism. Let's check it
559    */
560   if (strncasecmp(header, "NTLM", 4)) {
561     return FALSE;
562   }
563 
564   NOTIFY( // honestly this is here to silence the compiler...
565     DEBUG, "Parsing NTLM header:  %d, %d, %s, %s",
566     this->okay, scheme, header, creds_get_username(creds)
567   );
568 
569   header += 4; // Step past NTLM
570   while (*header && ISSPACE(*header)) {
571     header++;
572   }
573 
574   if (*header) {
575     /**
576      * We got a type-2 message here:
577      *
578      *  Index   Description         Content
579      *    0     NTLMSSP Signature   Null-terminated ASCII "NTLMSSP"
580      *                                (0x4e544c4d53535000)
581      *    8     NTLM Message Type   long (0x02000000)
582      *   12     Target Name         security buffer(*)
583      *   20     Flags               long
584      *   24     Challenge           8 bytes
585      *  (32)    Context (optional)  8 bytes (two consecutive longs)
586      *  (40)    Target Information  (optional) security buffer(*)
587      *   32 (48) start of data block
588      */
589     ssize_t size;
590     memset(tmp, '\0', strlen(header));
591     if ((size = base64_decode(header, &tmp)) < 0) {
592       return FALSE;
593     }
594 
595     if (size >= 48) {
596       /* the nonce of interest is index [24 .. 31], 8 bytes */
597       memcpy (this->ntlm.nonce, &tmp[24], 8);
598     }
599     this->ntlm.state = TYPE_2;
600   } else {
601     if (this->ntlm.state >= TYPE_1) {
602       return FALSE; /* this is an error */
603     }
604     this->ntlm.state = TYPE_1; /* we should send type-1 */
605   }
606 
607   switch (this->ntlm.state) {
608     case TYPE_1:
609     case TYPE_N:
610     case TYPE_L:
611       srvoff = 32;
612       domoff = srvoff + srvlen;
613       /**
614        * Create and send a type-1 message:
615        * Index Description          Content
616        *  0    NTLMSSP Signature    Null-terminated ASCII "NTLMSSP"
617        *                            (0x4e544c4d53535000)
618        *  8    NTLM Message Type    long (0x01000000)
619        * 12    Flags                long
620        * 16    Supplied Domain      security buffer(*)
621        * 24    Supplied Workstation security buffer(*)
622        * 32    start of data block
623        */
624       snprintf (
625         buf, sizeof(buf), "NTLMSSP%c"
626         "\x01%c%c%c" /* 32-bit type = 1 */
627         "%c%c%c%c"   /* 32-bit NTLM flag field */
628         "%c%c"       /* domain length */
629         "%c%c"       /* domain allocated space */
630         "%c%c"       /* domain name offset */
631         "%c%c"       /* 2 zeroes */
632         "%c%c"       /* host length */
633         "%c%c"       /* host allocated space */
634         "%c%c"       /* host name offset */
635         "%c%c"       /* 2 zeroes */
636         "%s"         /* host name */
637         "%s",        /* domain string */
638         0,           /* trailing zero */
639         0,0,0,       /* part of type-1 long */
640         LONGQUARTET(
641           /* equals 0x0202 */
642           NTLMFLAG_NEGOTIATE_OEM|      /*   2 */
643           NTLMFLAG_NEGOTIATE_NTLM_KEY  /* 200 */
644         ),
645         SHORTPAIR(domlen),
646         SHORTPAIR(domlen),
647         SHORTPAIR(domoff),
648         0,0,
649         SHORTPAIR(srvlen),
650         SHORTPAIR(srvlen),
651         SHORTPAIR(srvoff),
652         0,0,
653         srvstr, domstr
654       );
655       size   = 32 + srvlen + domlen;
656       if ((base64_encode(buf, size, &hdr) < 0 )) {
657         return FALSE;
658       }
659 
660       final  =  strlen(hdr) + 23;
661       this->ntlm.encode = xmalloc(final);
662       this->ntlm.state = TYPE_2; /* we sent type one */
663       memset(this->ntlm.encode, '\0', final);
664       snprintf(this->ntlm.encode, final, "Authorization: NTLM %s\015\012", hdr);
665       break;
666     case  TYPE_2:
667       /**
668        * We have type-2; need to create a type-3 message:
669        *
670        * Index    Description            Content
671        *   0      NTLMSSP Signature      Null-terminated ASCII "NTLMSSP"
672        *                                 (0x4e544c4d53535000)
673        *   8      NTLM Message Type      long (0x03000000)
674        *  12      LM/LMv2 Response       security buffer(*)
675        *  20      NTLM/NTLMv2 Response   security buffer(*)
676        *  28      Domain Name            security buffer(*)
677        *  36      User Name              security buffer(*)
678        *  44      Workstation Name       security buffer(*)
679        * (52)     Session Key (optional) security buffer(*)
680        * (60)     Flags (optional)       long
681        *  52 (64) start of data block
682        */
683       {
684         size_t lmrespoff;
685         size_t ntrespoff;
686         size_t usroff;
687         unsigned char lmresp[0x18]; /* fixed-size */
688         unsigned char ntresp[0x18]; /* fixed-size */
689         size_t usrlen;
690         const  char *usr;
691 
692         usr = strchr(creds_get_username(creds), '\\');
693         if (!usr) {
694           usr = strchr(creds_get_username(creds), '/');
695         }
696 
697         if (usr) {
698           domstr = creds_get_username(creds);
699           domlen = (size_t) (usr - domstr);
700           usr++;
701         } else {
702           usr = creds_get_username(creds);
703         }
704         usrlen = strlen(usr);
705         __mkhash(creds_get_password(creds), &this->ntlm.nonce[0], lmresp, ntresp);
706         domoff = 64;
707         usroff = domoff + domlen;
708         srvoff = usroff + usrlen;
709         lmrespoff = srvoff + srvlen;
710         ntrespoff = lmrespoff + 0x18;
711         size = (size_t) snprintf (
712           buf, sizeof(buf),
713           "NTLMSSP%c"
714           "\x03%c%c%c"     /* type-3, 32 bits */
715           "%c%c%c%c"       /* LanManager length + allocated space */
716           "%c%c"           /* LanManager offset */
717           "%c%c"           /* 2 zeroes */
718           "%c%c"           /* NT-response length */
719           "%c%c"           /* NT-response allocated space */
720           "%c%c"           /* NT-response offset */
721           "%c%c"           /* 2 zeroes */
722           "%c%c"           /* domain length */
723           "%c%c"           /* domain allocated space */
724           "%c%c"           /* domain name offset */
725           "%c%c"           /* 2 zeroes */
726           "%c%c"           /* user length */
727           "%c%c"           /* user allocated space */
728           "%c%c"           /* user offset */
729           "%c%c"           /* 2 zeroes */
730           "%c%c"           /* host length */
731           "%c%c"           /* host allocated space */
732           "%c%c"           /* host offset */
733           "%c%c%c%c%c%c"   /* 6 zeroes */
734           "\xff\xff"       /* message length */
735           "%c%c"           /* 2 zeroes */
736           "\x01\x82"       /* flags */
737           "%c%c"           /* 2 zeroes */
738                            /* domain string */
739                            /* user string */
740                            /* host string */
741                            /* LanManager response */
742                            /* NT response */
743           ,
744           0,               /* zero termination */
745           0,0,0,           /* type-3 long, the 24 upper bits */
746           SHORTPAIR(0x18), /* LanManager response length, twice */
747           SHORTPAIR(0x18),
748           SHORTPAIR(lmrespoff),
749           0x0, 0x0,
750           SHORTPAIR(0x18),
751           SHORTPAIR(0x18),
752           SHORTPAIR(ntrespoff),
753           0x0, 0x0,
754           SHORTPAIR(domlen),
755           SHORTPAIR(domlen),
756           SHORTPAIR(domoff),
757           0x0, 0x0,
758           SHORTPAIR(usrlen),
759           SHORTPAIR(usrlen),
760           SHORTPAIR(usroff),
761           0x0, 0x0,
762           SHORTPAIR(srvlen),
763           SHORTPAIR(srvlen),
764           SHORTPAIR(srvoff),
765           0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
766           0x0, 0x0,
767           0x0, 0x0
768         );
769 
770         size=64;
771         buf[62]=buf[63]=0;
772 
773        if ((size + usrlen + domlen) >= sizeof(buf))
774          return FALSE;
775 
776        memcpy(&buf[size], domstr, domlen);
777        size += domlen;
778 
779        memcpy(&buf[size], usr, usrlen);
780        size += usrlen;
781 
782        /* we append the binary hashes to the end of the blob */
783        if (size < (sizeof(buf) - 0x18)) {
784          memcpy(&buf[size], lmresp, 0x18);
785          size += 0x18;
786        }
787 
788        if (size < (sizeof(buf) - 0x18)) {
789          memcpy(&buf[size], ntresp, 0x18);
790          size += 0x18;
791        }
792 
793        buf[56] = (char) (size & 0xff);
794        buf[57] = (char) (size >> 8);
795        if ((base64_encode(buf, size, &hdr) < 0)) {
796          return FALSE;
797        }
798 
799        this->ntlm.state = TYPE_3; /* we sent a type-3 */
800        this->ntlm.ready = TRUE;
801        final  =  strlen(hdr) + 23;
802        this->ntlm.encode = (char*)xrealloc(this->ntlm.encode, final);
803        memset(this->ntlm.encode, '\0', final);
804        snprintf(this->ntlm.encode, final, "Authorization: NTLM %s\015\012", hdr);
805        break;
806      }
807      case TYPE_3:
808        this->ntlm.ready = TRUE;
809        break;
810      default:
811        break;
812   }
813   return TRUE;
814 #endif/*HAVE_SSL*/
815 }
816 
817 /**
818  * Copyright (C) 2011 by Free Software Foundation, Inc.
819  * Contributed by Daniel Stenberg.
820  * This code is part of wget and based on mk_lm_hash from libcurl
821  * Modified by J. Fulmer for siege
822  */
823 #ifdef HAVE_SSL
824 static void
setup_des_key(unsigned char * key_56,DES_key_schedule DESKEYARG (ks))825 setup_des_key(unsigned char *key_56, DES_key_schedule DESKEYARG(ks))
826 {
827   DES_cblock key;
828 
829   key[0] = key_56[0];
830   key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
831   key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
832   key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
833   key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
834   key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
835   key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
836   key[7] =  (key_56[6] << 1) & 0xFF;
837 
838   DES_set_odd_parity(&key);
839   DES_set_key(&key, ks);
840 }
841 #endif/*HAVE_SSL*/
842 
843 /**
844  * Copyright (C) 2011 by Free Software Foundation, Inc.
845  * Contributed by Daniel Stenberg.
846  * This code is part of wget and based on mk_lm_hash from libcurl
847  * Modified by J. Fulmer for siege
848  */
849 #ifdef HAVE_SSL
850 static void
calc_resp(unsigned char * keys,unsigned char * plaintext,unsigned char * results)851 calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results)
852 {
853   DES_key_schedule ks;
854 
855   setup_des_key(keys, DESKEY(ks));
856   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results, DESKEY(ks), DES_ENCRYPT);
857 
858   setup_des_key(keys+7, DESKEY(ks));
859   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8), DESKEY(ks), DES_ENCRYPT);
860 
861   setup_des_key(keys+14, DESKEY(ks));
862   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16), DESKEY(ks), DES_ENCRYPT);
863 }
864 #endif/*HAVE_SSL*/
865 
866 /**
867  * Copyright (C) 2011 by Free Software Foundation, Inc.
868  * Contributed by Daniel Stenberg.
869  * This code is part of wget and based on mk_lm_hash from libcurl
870  * Modified by J. Fulmer for siege
871  */
872 #ifdef HAVE_SSL
873 private void
__mkhash(const char * password,unsigned char * nonce,unsigned char * lmresp,unsigned char * ntresp)874 __mkhash(const char *password, unsigned char *nonce, unsigned char *lmresp, unsigned char *ntresp)
875 {
876   unsigned char *pw;
877   unsigned char lmbuffer[21];
878   unsigned char ntbuffer[21];
879   static const unsigned char magic[] = {
880     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
881   };
882   size_t i, len = strlen(password);
883 
884   pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2);
885 
886   if (len > 14)
887     len = 14;
888 
889   for (i=0; i<len; i++)
890     pw[i] = (unsigned char) TOUPPER(password[i]);
891 
892   for (; i<14; i++)
893     pw[i] = 0;
894   {
895     DES_key_schedule ks;
896 
897     setup_des_key(pw, DESKEY(ks));
898     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT);
899 
900     setup_des_key(pw+7, DESKEY(ks));
901     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8), DESKEY(ks), DES_ENCRYPT);
902     memset(lmbuffer+16, 0, 5);
903   }
904   calc_resp(lmbuffer, nonce, lmresp);
905 
906   {
907     MD4_CTX MD4;
908     len = strlen(password);
909 
910     for (i=0; i<len; i++) {
911       pw[2*i]   = (unsigned char) password[i];
912       pw[2*i+1] = 0;
913     }
914 
915     MD4_Init(&MD4);
916     MD4_Update(&MD4, pw, 2*len);
917     MD4_Final(ntbuffer, &MD4);
918 
919     memset(ntbuffer+16, 0, 5);
920   }
921   calc_resp(ntbuffer, nonce, ntresp);
922 }
923 #endif/*HAVE_SSL*/
924 
925 
926 typedef struct
927 {
928   const char *keyname;
929   KEY_HEADER_E keyval;
930 } KEYPARSER;
931 
932 static const KEYPARSER keyparser[] =
933 {
934   { "realm",     REALM     },
935   { "domain",    DOMAIN    },
936   { "nonce",     NONCE     },
937   { "opaque",    OPAQUE    },
938   { "stale",     STALE     },
939   { "algorithm", ALGORITHM },
940   { "qop",       QOP       },
941   { NULL,        UNKNOWN   }
942 };
943 
944 
945 private KEY_HEADER_E
__get_keyval(const char * key)946 __get_keyval(const char *key)
947 {
948   int i;
949 
950   for (i = 0; keyparser[i].keyname; i++) {
951     if (!strcasecmp(key, keyparser[i].keyname)) {
952       return keyparser[i].keyval;
953     }
954   }
955   return UNKNOWN;
956 }
957 
958 private char *
__get_random_string(size_t length,unsigned int * randseed)959 __get_random_string(size_t length, unsigned int *randseed)
960 {
961   const unsigned char b64_alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
962   unsigned char *result;
963   size_t i;
964 
965   result = xmalloc(sizeof(unsigned char) * (length + 1));
966 
967   for(i = 0; i < length; i++)
968     result[i] = (int) (255.0 * (pthread_rand_np(randseed) / (RAND_MAX + 1.0)));
969   for (i = 0; i < length; i++)
970     result[i] = b64_alphabet[(result[i] % ((sizeof(b64_alphabet) - 1) / sizeof(unsigned char)))];
971 
972   result[length] = '\0';
973 
974   return (char *) result;
975 }
976 
977 
978 #define DIGEST_CNONCE_SIZE 16
979 
980 private DCRED *
__digest_credentials(CREDS creds,unsigned int * randseed)981 __digest_credentials(CREDS creds, unsigned int *randseed)
982 {
983   DCRED *result;
984 
985   result = xcalloc(1, sizeof(struct DIGEST_CRED));
986   result->username = xstrdup(creds_get_username(creds));
987   result->password = xstrdup(creds_get_password(creds));
988   /* Generate a pseudo random cnonce */
989   result->cnonce_value = __get_random_string(DIGEST_CNONCE_SIZE, randseed);
990   result->nc_value = 1U;
991   snprintf(result->nc, sizeof(result->nc), "%.8x", result->nc_value);
992   result->h_a1 = NULL;
993 
994   return result;
995 }
996 
997 private DCHLG *
__digest_challenge(const char * challenge)998 __digest_challenge(const char *challenge)
999 {
1000   DCHLG *result;
1001   char  *key;
1002   char  *val;
1003   const char *beg;
1004   const char *end;
1005   KEY_HEADER_E keyval;
1006 
1007   result = xcalloc(1, sizeof(struct DIGEST_CHLG));
1008 
1009   for (beg = end = challenge; !isspace(*end) && *end; ++end);
1010 
1011   if (strncasecmp("Digest", beg, end - beg)) {
1012     fprintf(stderr, "no Digest keyword in challenge [%s]\n", challenge);
1013     return NULL;
1014   }
1015 
1016   for (beg = end; isspace(*beg); ++beg);
1017 
1018   while (*beg != '\0') {
1019 
1020     /* find key */
1021     while (isspace(*beg))
1022       beg++;
1023 
1024     end = beg;
1025     while (*end != '=' && *end != ',' && *end != '\0' && !isspace(*end))
1026       end++;
1027 
1028     key = xmalloc((1 + end - beg) * sizeof(char));
1029     memcpy(key, beg, end - beg);
1030     key[end - beg] = '\0';
1031 
1032     beg = end;
1033     while (isspace(*beg))
1034       beg++;
1035 
1036     /* find value */
1037     val = NULL;
1038     if (*beg == '=') {
1039       beg++;
1040       while (isspace(*beg))
1041         beg++;
1042 
1043       if (*beg == '\"') {     /* quoted string */
1044         beg++;
1045         end = beg;
1046         while (*end != '\"' && *end != '\0') {
1047           if (*end == '\\' && end[1] != '\0') {
1048             end++;      /* escaped char */
1049           }
1050           end++;
1051         }
1052         val = xmalloc((1 + end - beg) * sizeof(char));
1053         memcpy(val, beg, end - beg);
1054         val[end - beg] = '\0';
1055         beg = end;
1056         if (*beg != '\0') {
1057           beg++;
1058         }
1059       }
1060       else {              /* token */
1061         end = beg;
1062         while (*end != ',' && *end != '\0' && !isspace(*end))
1063           end++;
1064 
1065         val = xmalloc((1 + end - beg) * sizeof(char));
1066         memcpy(val, beg, end - beg);
1067         val[end - beg] = '\0';
1068         beg = end;
1069       }
1070     }
1071 
1072     while (*beg != ',' && *beg != '\0')
1073       beg++;
1074 
1075     if (*beg != '\0') {
1076       beg++;
1077     }
1078 
1079     keyval = __get_keyval(key);
1080     switch (keyval) {
1081       case REALM:
1082       result->realm = val;
1083       break;
1084       case DOMAIN:
1085       result->domain = val;
1086       break;
1087       case NONCE:
1088       result->nonce = val;
1089       break;
1090       case OPAQUE:
1091       result->opaque = val;
1092       break;
1093       case STALE:
1094       result->stale = val;
1095       break;
1096       case ALGORITHM:
1097       result->algorithm = val;
1098       break;
1099       case QOP:
1100       result->qop = val;
1101       break;
1102       default:
1103       fprintf(stderr, "unknown key [%s]\n", key);
1104       xfree(val);
1105       break;
1106     }
1107     xfree(key);
1108   }
1109 
1110   return result;
1111 }
1112 
1113 private char *
__get_md5_str(const char * buf)1114 __get_md5_str(const char *buf)
1115 {
1116   const char *hex = "0123456789abcdef";
1117   struct md5_ctx ctx;
1118   unsigned char hash[16];
1119   char *r, *result;
1120   size_t length;
1121   int i;
1122 
1123   length = strlen(buf);
1124   result = xmalloc(33 * sizeof(char));
1125   md5_init_ctx(&ctx);
1126   md5_process_bytes(buf, length, &ctx);
1127   md5_finish_ctx(&ctx, hash);
1128 
1129   for (i = 0, r = result; i < 16; i++) {
1130     *r++ = hex[hash[i] >> 4];
1131     *r++ = hex[hash[i] & 0xF];
1132   }
1133   *r = '\0';
1134 
1135   return result;
1136 }
1137 
1138 
1139 private char *
__get_h_a1(const DCHLG * chlg,DCRED * cred,const char * nonce_value)1140 __get_h_a1(const DCHLG *chlg, DCRED *cred, const char *nonce_value)
1141 {
1142   char *h_usrepa, *result, *tmp;
1143 
1144   if (0 == strcasecmp("MD5", chlg->algorithm)) {
1145     tmp = xstrcat(cred->username, ":", chlg->realm, ":", cred->password, NULL);
1146     h_usrepa = __get_md5_str(tmp);
1147     xfree(tmp);
1148     result = h_usrepa;
1149   }
1150   else if (0 == strcasecmp("MD5-sess", chlg->algorithm)) {
1151     if ((NULL == cred->h_a1)) {
1152       tmp = xstrcat(cred->username, ":", chlg->realm, ":", cred->password, NULL);
1153       h_usrepa = __get_md5_str(tmp);
1154       xfree(tmp);
1155       tmp = xstrcat(h_usrepa, ":", nonce_value, ":", cred->cnonce_value, NULL);
1156       result = __get_md5_str(tmp);
1157       xfree(tmp);
1158       cred->h_a1 = result;
1159     }
1160     else {
1161       return cred->h_a1;
1162     }
1163   }
1164   else {
1165     fprintf(stderr, "invalid call to %s algorithm is [%s]\n", __FUNCTION__, chlg->algorithm);
1166     return NULL;
1167   }
1168 
1169   return result;
1170 }
1171 
1172 private BOOLEAN
__str_list_contains(const char * str,const char * pattern,size_t pattern_len)1173 __str_list_contains(const char *str, const char *pattern, size_t pattern_len)
1174 {
1175   const char *ptr;
1176 
1177   ptr = str;
1178   do {
1179     if (0 == strncmp(ptr, pattern, pattern_len)
1180         && ((',' == ptr[pattern_len]) || ('\0' == ptr[pattern_len]))) {
1181       return TRUE;
1182     }
1183 
1184     if (NULL != (ptr = strchr(ptr, ','))) ptr++;
1185   }
1186   while (NULL != ptr);
1187 
1188   return FALSE;
1189 }
1190 
1191