/* $Id: ntlmauth.c,v 1.7 2004/03/10 13:35:21 thivillon Exp $ © 2003 Alain Thivillon et Hervé Schauer Consultants */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif #include "ntlmauth.h" #include "base64.h" #define FROMLITTLE(p) (((p)[0] & 0xff) + (((p)[1] & 0xff) << 8)) #define TOLITTLE(p, i) ((p)[0] = (i) & 0xff, (p)[1] = ((i) >> 8) & 0xff) #define min(a,b) (((a) < (b)) ? (a) : (b)) extern int write_timeout(int s, char *buffer, int size, int tmo); extern int read_timeout(int s, char *buffer, int size, int tmo); extern void do_log( int prio, char *format , ... ); static void setup_des_key(unsigned char key_56[], des_key_schedule *ks); static void calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results); static char *do_ntlm_auth_1(char *username, char *password); static char *do_ntlm_auth_2(iconv_t myiconv, char *username, char *password, char *challenge); static void dump_hex(unsigned char *string, int len); static char *do_ntlm_auth_1(char *username, char *password) { char hostname[256]; char domainname[256]; mess_ntlm_1 mess_1; char *ptr,*ptr_dest; byte NTLM_string[] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' }; char *buffer_in=NULL, *buffer_out=NULL; unsigned short h_len; unsigned short d_len; unsigned short client_flags; memset(&mess_1,0,sizeof(mess_ntlm_1)); memcpy(&mess_1.protocol , &NTLM_string, 8); mess_1.type = NTLMSSP_NEGOCIATE; client_flags = NTLMSSP_ALWAYS_SIGN | NTLMSSP_WANT_NTLM | NTLMSSP_WANT_LM; client_flags |= (NTLMSSP_WANT_UNICODE | NTLMSSP_WANT_OEM); if (gethostname ( hostname, 255) ) { do_log(LOG_ERR, "gethostname : %s", strerror(errno)); goto catch; } hostname[255] = 0; if ((ptr = strchr(hostname, '.')) != NULL) *ptr= '\0'; strncpy(domainname, username, 255); /* If username is DOMAIN\user , get it and set flags */ if ((ptr = strchr(domainname, '\\')) != NULL) { *ptr = '\0'; client_flags |= (NTLMSSP_HAS_DOMAIN | NTLMSSP_HAS_WORKGROUP); } else domainname[0] = '\0'; TOLITTLE(mess_1.flags , client_flags); h_len = strlen(hostname); d_len = strlen(domainname); TOLITTLE(mess_1.dom_len, d_len ); TOLITTLE(mess_1.dom_len_2, d_len ); TOLITTLE(mess_1.host_len, h_len); TOLITTLE(mess_1.host_len_2 , h_len); TOLITTLE(mess_1.host_off , (unsigned short) 0x20); TOLITTLE(mess_1.dom_off , 0x20 + h_len); buffer_in = malloc(sizeof(mess_1) + h_len + d_len + 10); memcpy(buffer_in, &mess_1, sizeof(mess_1)); ptr_dest = buffer_in + sizeof(mess_1); for (ptr = hostname, ptr_dest = buffer_in + sizeof(mess_1); *ptr; ptr++, ptr_dest++) *ptr_dest = toupper(*ptr); for (ptr = domainname, ptr_dest = buffer_in + sizeof(mess_1) + h_len; *ptr; ptr++, ptr_dest++) *ptr_dest = toupper(*ptr); buffer_out = malloc ((sizeof(mess_1) + h_len + d_len) * 2); base64_encode (buffer_in, buffer_out , sizeof(mess_1) + h_len + d_len); free(buffer_in); return buffer_out; catch: if (buffer_in != NULL) free(buffer_in); return NULL; } static char *do_ntlm_auth_2(iconv_t myiconv, char *username, char *password, char *challenge) { char *bufplain = NULL; mess_ntlm_2 *mess2; mess_ntlm_3 mess3; byte NTLM_string[] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' }; char *buffer_in = NULL, *buffer_out = NULL; unsigned short h_len; unsigned short d_len; unsigned short u_len; char *ptr,*inbuf,*outbuf,*nonce; char *user=username; size_t len_uni; size_t len_uni_after; char hostname[256]; char domainname[256]; char lm_pw[14]; int len,idx; char *nt_pw = NULL,*lm_resp,*nt_resp; const unsigned char magic[] = "KGS!@#$%"; unsigned char lm_hpw[50]; des_key_schedule ks1; des_key_schedule ks2; unsigned char nt_hpw[50]; MD4_CTX context; unsigned short server_flags; unsigned short client_flags; unsigned short ntlm_offset; unsigned short total_len; bufplain = malloc (strlen(challenge)); base64_decode(bufplain, challenge); mess2 = (mess_ntlm_2 *) bufplain; if (strcmp(mess2->protocol,"NTLMSSP")) { do_log(LOG_WARNING,"Bad NTLM challenge"); goto catch; } server_flags = FROMLITTLE(mess2->flags); memset(&mess3, 0, sizeof(mess3)); memcpy(&mess3.protocol , &NTLM_string, 8); /* Set client answer flags */ mess3.type = NTLMSSP_AUTH; client_flags = NTLMSSP_ALWAYS_SIGN; if (server_flags & NTLMSSP_WANT_UNICODE) client_flags |= NTLMSSP_WANT_UNICODE; else client_flags |= NTLMSSP_WANT_OEM; if (server_flags & NTLMSSP_WANT_NTLM) client_flags |= NTLMSSP_WANT_NTLM; else if (server_flags & NTLMSSP_WANT_LM) client_flags |= NTLMSSP_WANT_LM; TOLITTLE(mess3.dom_off, 0x40); if (gethostname ( hostname, 255) ) goto catch; hostname[255] = 0; if ((ptr = strchr(hostname, '.')) != NULL) *ptr= '\0'; strncpy(domainname, username, 255); if ((ptr = strchr(domainname, '\\')) != NULL) { *ptr = '\0'; user = ptr+1; client_flags |= (NTLMSSP_HAS_DOMAIN | NTLMSSP_HAS_WORKGROUP); } else domainname[0] = '\0'; TOLITTLE(mess3.flags , (unsigned short) client_flags); h_len = strlen(hostname); d_len = strlen(domainname); u_len = strlen(user); ptr = hostname; while (*ptr) { *ptr = toupper(*ptr); ptr ++; } ptr = domainname; while (*ptr) { *ptr = toupper(*ptr); ptr ++; } buffer_in = malloc(sizeof(mess3) + 2*h_len + 2*u_len + 2*d_len + 2*0x18); /* Start Iconv */ iconv(myiconv, NULL, NULL, NULL, NULL); if (client_flags & NTLMSSP_WANT_UNICODE) { /* Conv Domain Name */ len_uni = 2 * d_len; len_uni_after = len_uni; inbuf = domainname; outbuf = buffer_in + sizeof(mess3); iconv(myiconv, (char* *) &inbuf, (size_t * ) &len_uni, (char* *) &outbuf, (size_t *) &len_uni_after); if (len_uni_after != 0) { do_log(LOG_WARNING,"Bad Conversion of domainname"); goto catch; } } else { len_uni = d_len; len_uni_after = len_uni; inbuf = domainname; outbuf = buffer_in + sizeof(mess3); memcpy(outbuf, inbuf, len_uni); } /* Conv User Name */ iconv(myiconv, NULL, NULL, NULL, NULL); if (client_flags & NTLMSSP_WANT_UNICODE) { len_uni = 2 * u_len; len_uni_after = len_uni; inbuf = user; outbuf = buffer_in + sizeof(mess3) + 2 * d_len; iconv(myiconv, (char* *) &inbuf, (size_t * ) &len_uni, (char* *) &outbuf, (size_t *) &len_uni_after); if (len_uni_after != 0) { do_log(LOG_WARNING,"Bad Conversion of username"); goto catch; } } else { len_uni = u_len; len_uni_after = len_uni; inbuf = user; outbuf = buffer_in + sizeof(mess3) + d_len; memcpy(outbuf, inbuf, len_uni); } /* Conv Hostname */ iconv(myiconv, NULL, NULL, NULL, NULL); if (client_flags & NTLMSSP_WANT_UNICODE) { len_uni = 2 * h_len; len_uni_after = len_uni; inbuf = hostname; outbuf = buffer_in + sizeof(mess3) + 2 * d_len + 2 * u_len; iconv(myiconv, (char* *) &inbuf, (size_t * ) &len_uni, (char* *) &outbuf, (size_t *) &len_uni_after); if (len_uni_after != 0) { do_log(LOG_WARNING,"Bad Conversion of hostname"); goto catch; } } else { len_uni = h_len; len_uni_after = len_uni; inbuf = hostname; outbuf = buffer_in + sizeof(mess3) + d_len + u_len; memcpy(outbuf, inbuf, len_uni); } if (client_flags & NTLMSSP_WANT_UNICODE) { TOLITTLE(mess3.dom_len_1, d_len * 2); TOLITTLE(mess3.dom_len_2, d_len * 2); TOLITTLE(mess3.host_len_1, h_len * 2); TOLITTLE(mess3.host_len_2 , h_len * 2); TOLITTLE(mess3.user_len_1, u_len * 2); TOLITTLE(mess3.user_len_2 , u_len * 2); TOLITTLE(mess3.user_off, sizeof(mess3) + d_len * 2); TOLITTLE(mess3.host_off, sizeof(mess3) + d_len * 2 + u_len *2); nonce = (char *) &(mess2->nonce); ntlm_offset = sizeof(mess3) + d_len * 2 + h_len *2 + u_len * 2 ; total_len = sizeof(mess3) + d_len * 2 + h_len *2 + u_len * 2; /* Not necessary everytime, but does not choke NT and permits Ethereal dissector to work ... */ TOLITTLE(mess3.lm_resp_off, 0x40 + d_len * 2 + h_len *2 + u_len * 2); } else { TOLITTLE(mess3.dom_len_1, d_len ); TOLITTLE(mess3.dom_len_2, d_len ); TOLITTLE(mess3.host_len_1, h_len ); TOLITTLE(mess3.host_len_2 , h_len); TOLITTLE(mess3.user_len_1, u_len ); TOLITTLE(mess3.user_len_2 , u_len); TOLITTLE(mess3.user_off, sizeof(mess3) + d_len ); TOLITTLE(mess3.host_off, sizeof(mess3) + d_len + u_len ); nonce = (char *) &(mess2->nonce); ntlm_offset = sizeof(mess3) + d_len + h_len + u_len ; total_len = sizeof(mess3) + d_len + h_len + u_len ; TOLITTLE(mess3.lm_resp_off, 0x40 + d_len + h_len + u_len ); } len = strlen(password); if (1) { /* Squid NTLM Auth announce NTLM but does not use it !!! So we must stuck to send LM hash ! //if (client_flags & NTLMSSP_WANT_LM) { */ /* Lan Manager part */ TOLITTLE(mess3.lm_resp_len_1, 0x18); TOLITTLE(mess3.lm_resp_len_2, 0x18); ntlm_offset += 0x18; total_len+= 0x18; /* setup LanManager password */ if (len > 14) len = 14; for (idx=0; idx 0) { *(ptr+lg) = 0; if ((ptr_data=strstr(response,"\r\n\r\n")) != NULL) { crlf=1; *ptr_data = 0; } ptr += lg; } else { goto catch; } } doc_length = 0; /* Read rest of data */ if (crlf) { do_log(LOG_INFO, "Proxy Answer:\n%s\n", response); if ((ptr_content_length = strcasestr(response,"Content-Length: ")) != NULL) { sscanf(ptr_content_length+16,"%d", &doc_length); doc_length -= (ptr - (ptr_data + 4)); } if ( mode == WWW_AUTH && (ptr_authenticate = strcasestr(response, "WWW-Authenticate: NTLM ")) != NULL) { if ((ptr = strstr(ptr_authenticate, "\r\n")) != NULL) { *ptr = '\0'; nt_challenge = strdup(ptr_authenticate+23); } } if ( mode == PROXY_AUTH && (ptr_authenticate = strcasestr(response, "Proxy-Authenticate: NTLM ")) != NULL) { if ((ptr = strstr(ptr_authenticate, "\r\n")) != NULL) { *ptr = '\0'; nt_challenge = strdup(ptr_authenticate+25); } } } else { do_log(LOG_WARNING, "No Headers end\n%s", response); goto catch; } while (doc_length > 0) { lg = read_timeout(sock, response, min(2048, doc_length), 10); if (lg > 0) { *(ptr+lg) = 0; doc_length -= lg; } else { fprintf(stderr,"Short read\n"); goto catch; } } /* We have read all data */ if (nt_challenge == NULL) { do_log(LOG_WARNING, "No valid NTLM challenge"); goto catch; } second_packet = do_ntlm_auth_2(myiconv, username, password, nt_challenge); if (second_packet == NULL) { goto catch; } snprintf(buffer, 1023, "%s\r\nConnection: Keep-Alive\r\nHost: %s\r\n%s NTLM %s\r\nUser-Agent: %s\r\n\r\n", uri, host, auth_header, second_packet, useragent ); do_log(LOG_INFO, "Sent to proxy : \n%s", buffer); if (write_timeout(sock, buffer, strlen(buffer), 10) <= 0) { goto catch; } free(first_packet); free(second_packet); free(nt_challenge); if (myiconv != (iconv_t) -1) iconv_close(myiconv); return 0; catch: if (first_packet != NULL) free(first_packet); if (second_packet != NULL) free(second_packet); if (nt_challenge != NULL) free(nt_challenge); if (myiconv != (iconv_t) -1) iconv_close(myiconv); return -1; } /* * turns a 56 bit key into the 64 bit, odd parity key and sets the key. * The key schedule ks is also set. */ static void setup_des_key(unsigned char key_56[], des_key_schedule *ks) { des_cblock key; key[0] = key_56[0]; key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); key[7] = (key_56[6] << 1) & 0xFF; DES_set_odd_parity(&key); DES_set_key_unchecked(&key, ks); } /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The * 8 byte plaintext is encrypted with each key and the resulting 24 * bytes are stored in the results array. */ static void calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results) { des_key_schedule ks; setup_des_key(keys, &ks); DES_ecb_encrypt((const_DES_cblock *) plaintext, (const_DES_cblock *) results, &ks, DES_ENCRYPT); setup_des_key(keys+7, &ks); DES_ecb_encrypt((const_DES_cblock*) plaintext, (const_DES_cblock*) (results+8), &ks, DES_ENCRYPT); setup_des_key(keys+14, &ks); DES_ecb_encrypt((const_DES_cblock*) plaintext, (const_DES_cblock*) (results+16), &ks, DES_ENCRYPT); } #if 0 int main (int argc, char **argv) { #define NTLM_USER "FENETRE2k\\at" #define NTLM_PASS "zboing" //#define URL "GET http://www.hsc.fr/ HTTP/1.1" #define URL "CONNECT www.hsc.fr:443 HTTP/1.1" #define SERVER_IP "192.70.106.143" #define SERVER_HOST "www.hsc.fr" int sock; struct sockaddr_in sin; in_addr_t addr; char buffer[2048]; int one = 1; int len; if ( ( sock = socket (AF_INET , SOCK_STREAM, 0 ) ) < 0 ) { fprintf(stderr,"socket : %s\n", strerror(errno)); return (-2) ; } memset( &sin, sizeof(struct sockaddr_in), 0 ); sin.sin_family = AF_INET; addr = inet_addr ( SERVER_IP ); sin.sin_port = ntohs ( 8080 ); sin.sin_addr.s_addr = addr; setsockopt ( sock, 6, TCP_NODELAY , &one, sizeof (int) ); if ( connect ( sock , ( struct sockaddr * ) &sin , sizeof ( sin ) ) == -1 ) { fprintf(stderr, "connect: %s", strerror(errno)) ; exit(0); } ioctl(sock, FIONBIO, &one); if (do_ntlm_auth (sock, NTLM_USER , NTLM_PASS , URL , SERVER_HOST, PROXY_AUTH) == 0) { while ((len = read_timeout(sock, buffer, 2048, 10 )) > 0) write(1, buffer, len); } close(sock); return 0; } #endif static void dump_hex(unsigned char *string, int len) { int i; for (i=0;i