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