mirror of
https://github.com/proxytunnel/proxytunnel.git
synced 2026-01-23 02:34:59 +00:00
447 lines
12 KiB
C
447 lines
12 KiB
C
/* Proxytunnel - (C) 2001-2008 Jos Visser / Mark Janssen */
|
|
/* Contact: josv@osp.nl / maniac@maniac.nl */
|
|
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* ntlm.c -- Code for handling NTLM authentication */
|
|
/* NTLM Code from Paul Solomon <psolomon@tpg.com.au> */
|
|
|
|
#include "ntlm.h"
|
|
#include "global.h"
|
|
#include "base64.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "proxytunnel.h"
|
|
#include <ctype.h>
|
|
#include <sys/time.h>
|
|
#include <openssl/md4.h>
|
|
#include <openssl/md5.h>
|
|
|
|
#define TYPE1_DATA_SEG 8
|
|
#define TYPE2_BUF_SIZE 2048
|
|
#define DOMAIN_BUFLEN 256
|
|
#define LM2_DIGEST_LEN 24
|
|
|
|
int ntlm_challenge = 0;
|
|
void message( char *s, ... );
|
|
int unicode = 0;
|
|
|
|
unsigned char challenge[8];
|
|
char domain[DOMAIN_BUFLEN];
|
|
char workstation[] = "WORKSTATION";
|
|
|
|
unsigned char unipasswd[DOMAIN_BUFLEN * 2];
|
|
|
|
unsigned char t2_buf[TYPE2_BUF_SIZE];
|
|
|
|
unsigned char *pblob = NULL;
|
|
int bloblen;
|
|
|
|
unsigned char *t_info;
|
|
int t_info_len;
|
|
|
|
uint32_t flags;
|
|
|
|
unsigned char lm2digest[LM2_DIGEST_LEN];
|
|
|
|
void build_type1() {
|
|
ntlm_type1 *type1;
|
|
int len = sizeof(ntlm_type1) + sizeof(unsigned char) * TYPE1_DATA_SEG;
|
|
|
|
type1 = (ntlm_type1 *)alloca(len);
|
|
|
|
memset(type1, 0, len);
|
|
type1->signature[0] = 'N';
|
|
type1->signature[1] = 'T';
|
|
type1->signature[2] = 'L';
|
|
type1->signature[3] = 'M';
|
|
type1->signature[4] = 'S';
|
|
type1->signature[5] = 'S';
|
|
type1->signature[6] = 'P';
|
|
type1->signature[7] = '\0';
|
|
|
|
type1->message_type = NTLM_TYPE_1;
|
|
type1->flags = NEG_UNICODE | NEG_OEM | REQ_TARGET | NEG_NTLM | NEG_ASIGN | NEG_NTLM2 | NEG_128 | NEG_56 | IE_SETSTHIS;
|
|
|
|
base64((unsigned char *)ntlm_type1_buf, (unsigned char *)type1, len);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
int parse_type2(unsigned char *buf) {
|
|
int len = unbase64(t2_buf, buf, TYPE2_BUF_SIZE);
|
|
ntlm_type2 *t2 = (ntlm_type2 *)t2_buf;
|
|
int i;
|
|
|
|
if (len <= 0) {
|
|
message("parse_type2: failed to decode the message\n");
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp((const char *)t2->signature, "NTLMSSP") != 0) {
|
|
message("parse_type2: Signature did not match\n");
|
|
return -1;
|
|
}
|
|
|
|
if( args_info.verbose_flag )
|
|
message("parse_type2: Signature matched\n");
|
|
|
|
if (t2->message_type != NTLM_TYPE_2) {
|
|
message("parse_type2: Incorrect message type sent\n");
|
|
return -1;
|
|
}
|
|
|
|
if (t2->target_name.length > 0 && t2->target_name.length < DOMAIN_BUFLEN && (t2->target_name.length + t2->target_name.offset < len)) {
|
|
int sp = 1;
|
|
if (t2->flags & NEG_UNICODE)
|
|
sp = 2;
|
|
for (i = 0; i < t2->target_name.length / sp; i++)
|
|
domain[i] = t2_buf[t2->target_name.offset + i * sp];
|
|
domain[i] = 0;
|
|
} else {
|
|
domain[0] = 0;
|
|
}
|
|
|
|
for (i = 0; i < 8; i++)
|
|
challenge[i] = t2->challenge[i];
|
|
|
|
if( args_info.verbose_flag )
|
|
message("NTLM Got Domain: %s\n", domain);
|
|
|
|
if( args_info.domain_given ) {
|
|
if( ! args_info.quiet_flag )
|
|
message( "NTLM Overriding domain: %s\n", args_info.domain_arg );
|
|
for( i = 0; i < strlen(args_info.domain_arg); i++ ) {
|
|
domain[i] = args_info.domain_arg[i];
|
|
}
|
|
domain[i] = 0;
|
|
}
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("NTLM Domain: %s\n", domain);
|
|
message("NTLM Got Challenge: ");
|
|
|
|
for (i = 0; i < 8; i++)
|
|
message("%02X", challenge[i]);
|
|
message("\n");
|
|
}
|
|
|
|
if (!(t2->flags & NEG_NTLM && t2->flags & NEG_NTLM2)) {
|
|
message("parse_type2: Sorry, only NTLMv2 is supported at this time\n");
|
|
return -1;
|
|
}
|
|
|
|
if (t2->flags & NEG_UNICODE)
|
|
unicode = 1;
|
|
else
|
|
unicode = 0;
|
|
|
|
t_info = &t2_buf[t2->target_info.offset];
|
|
t_info_len = t2->target_info.length;
|
|
|
|
flags = t2->flags;
|
|
|
|
ntlm_challenge = 1;
|
|
|
|
build_ntlm2_response();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void build_type3_response() {
|
|
unsigned char *t3;
|
|
ntlm_type3 *type3;
|
|
int len;
|
|
int sp = 1;
|
|
int i;
|
|
|
|
if (unicode)
|
|
sp = 2;
|
|
|
|
len = sizeof(ntlm_type3) + sizeof(unsigned char) * (LM2_DIGEST_LEN + bloblen + (strlen(domain) + strlen(args_info.user_arg) + strlen(workstation)) * sp);
|
|
|
|
type3 = (ntlm_type3 *)alloca(len);
|
|
t3 = (unsigned char *) type3;
|
|
|
|
memset(type3, 0, len);
|
|
type3->signature[0] = 'N';
|
|
type3->signature[1] = 'T';
|
|
type3->signature[2] = 'L';
|
|
type3->signature[3] = 'M';
|
|
type3->signature[4] = 'S';
|
|
type3->signature[5] = 'S';
|
|
type3->signature[6] = 'P';
|
|
type3->signature[7] = '\0';
|
|
|
|
type3->message_type = NTLM_TYPE_3;
|
|
type3->flags = flags & ~TAR_DOMAIN & ~NEG_TARINFO;
|
|
|
|
type3->LM_response.length = LM2_DIGEST_LEN;
|
|
type3->LM_response.space = LM2_DIGEST_LEN;
|
|
type3->LM_response.offset = sizeof(ntlm_type3);
|
|
memcpy(&t3[type3->LM_response.offset], lm2digest, LM2_DIGEST_LEN);
|
|
|
|
type3->NTLM_response.length = bloblen;
|
|
type3->NTLM_response.space = bloblen;
|
|
type3->NTLM_response.offset = type3->LM_response.offset + type3->LM_response.space;
|
|
memcpy(&t3[type3->NTLM_response.offset], pblob, bloblen);
|
|
|
|
type3->domain.length = strlen(domain) * sp;
|
|
type3->domain.space = strlen(domain) * sp;
|
|
type3->domain.offset = type3->NTLM_response.offset + type3->NTLM_response.space;
|
|
for (i = 0; i < strlen(domain); i++)
|
|
t3[type3->domain.offset + i * sp] = domain[i];
|
|
|
|
type3->user.length = strlen(args_info.user_arg) * sp;
|
|
type3->user.space = strlen(args_info.user_arg) * sp;
|
|
type3->user.offset = type3->domain.offset + type3->domain.space;
|
|
for (i = 0; i < strlen(args_info.user_arg); i++)
|
|
t3[type3->user.offset + i * sp] = args_info.user_arg[i];
|
|
|
|
type3->workstation.length = strlen(workstation) * sp;
|
|
type3->workstation.space = strlen(workstation) * sp;
|
|
type3->workstation.offset = type3->user.offset + type3->user.space;
|
|
for (i = 0; i < strlen(workstation); i++)
|
|
t3[type3->workstation.offset + i * sp] = workstation[i];
|
|
|
|
base64((unsigned char *)ntlm_type3_buf, (unsigned char *)type3, len);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Function: hmac_md5
|
|
*/
|
|
|
|
void
|
|
hmac_md5(text, text_len, key, key_len, digest)
|
|
unsigned char* text; /* pointer to data stream */
|
|
int text_len; /* length of data stream */
|
|
unsigned char* key; /* pointer to authentication key */
|
|
int key_len; /* length of authentication key */
|
|
unsigned char digest[16]; /* caller digest to be filled in */
|
|
{
|
|
MD5_CTX context;
|
|
unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
|
|
unsigned char k_opad[65]; /* outer padding - key XORd with opad */
|
|
unsigned char tk[16];
|
|
int i;
|
|
|
|
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
|
if (key_len > 64) {
|
|
MD5_CTX tctx;
|
|
MD5_Init( &tctx );
|
|
MD5_Update( &tctx, key, key_len );
|
|
MD5_Final( tk, &tctx );
|
|
key = tk;
|
|
key_len = 16;
|
|
}
|
|
|
|
/*
|
|
* the HMAC_MD5 transform looks like:
|
|
*
|
|
* MD5(K XOR opad, MD5(K XOR ipad, text))
|
|
*
|
|
* where K is an n byte key
|
|
* ipad is the byte 0x36 repeated 64 times
|
|
* opad is the byte 0x5c repeated 64 times
|
|
* and text is the data being protected
|
|
*/
|
|
|
|
/* start out by storing key in pads */
|
|
bzero( k_ipad, sizeof k_ipad);
|
|
bzero( k_opad, sizeof k_opad);
|
|
bcopy( key, k_ipad, key_len);
|
|
bcopy( key, k_opad, key_len);
|
|
|
|
/* XOR key with ipad and opad values */
|
|
for (i=0; i<64; i++) {
|
|
k_ipad[i] ^= 0x36;
|
|
k_opad[i] ^= 0x5c;
|
|
}
|
|
|
|
/* perform inner MD5 */
|
|
MD5_Init(&context); /* init context for 1st pass */
|
|
MD5_Update(&context, k_ipad, 64); /* start with inner pad */
|
|
MD5_Update(&context, text, text_len); /* then text of datagram */
|
|
MD5_Final(digest, &context); /* finish up 1st pass */
|
|
|
|
/* perform outer MD5 */
|
|
MD5_Init(&context); /* init context for 2nd pass */
|
|
MD5_Update(&context, k_opad, 64); /* start with outer pad */
|
|
MD5_Update(&context, digest, 16); /* then results of 1st hash */
|
|
MD5_Final(digest, &context); /* finish up 2nd pass */
|
|
}
|
|
|
|
void build_ntlm2_response() {
|
|
int i, j;
|
|
int passlen = 0;
|
|
MD4_CTX passcontext;
|
|
unsigned char passdigest[16];
|
|
unsigned char *userdom;
|
|
int userdomlen;
|
|
unsigned char userdomdigest[16];
|
|
blob *b;
|
|
struct timeval t;
|
|
unsigned char responsedigest[16];
|
|
unsigned char lm2data[16];
|
|
|
|
if (pblob != NULL)
|
|
free(pblob);
|
|
|
|
memset(unipasswd, 0, sizeof(unsigned char) * DOMAIN_BUFLEN * 2);
|
|
for (i = 0; i < strlen(args_info.pass_arg); i++) {
|
|
if (unicode) {
|
|
unipasswd[i * 2] = args_info.pass_arg[i];
|
|
passlen++;
|
|
passlen++;
|
|
} else {
|
|
unipasswd[i] = args_info.pass_arg[i];
|
|
passlen++;
|
|
}
|
|
}
|
|
|
|
MD4_Init (&passcontext);
|
|
MD4_Update (&passcontext, unipasswd, passlen);
|
|
MD4_Final (passdigest, &passcontext);
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("NTLM: MD4 of password is: ");
|
|
for( i = 0; i < 16; i++)
|
|
message("%02X", passdigest[i]);
|
|
message("\nDOMAIN: %s\nUSER: %s\n", domain, args_info.user_arg);
|
|
}
|
|
|
|
userdomlen = sizeof(unsigned char) * (strlen(args_info.user_arg) + strlen(domain)) * 2;
|
|
userdom = (unsigned char *)alloca(userdomlen);
|
|
memset(userdom, 0, userdomlen);
|
|
|
|
userdomlen = 0;
|
|
for (i = 0; i < strlen(args_info.user_arg); i++) {
|
|
if (unicode) {
|
|
userdom[i * 2] = toupper(args_info.user_arg[i]);
|
|
userdomlen++;
|
|
userdomlen++;
|
|
} else {
|
|
userdom[i] = toupper(args_info.user_arg[i]);
|
|
userdomlen++;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < strlen(domain); j++) {
|
|
if (unicode) {
|
|
userdom[i * 2 + j * 2] = toupper(domain[j]);
|
|
userdomlen++;
|
|
userdomlen++;
|
|
} else {
|
|
userdom[i + j] = toupper(domain[j]);
|
|
userdomlen++;
|
|
}
|
|
}
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("userdom is: ");
|
|
for( i = 0; i < userdomlen; i++)
|
|
message("%02X", userdom[i]);
|
|
message("\n");
|
|
}
|
|
|
|
hmac_md5(userdom, userdomlen, passdigest, 16, userdomdigest);
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("HMAC_MD5 of userdom keyed with MD4 pass is: ");
|
|
for( i = 0; i < 16; i++)
|
|
message("%02X", userdomdigest[i]);
|
|
message("\n");
|
|
}
|
|
|
|
if ((sizeof(long long) != 8)) {
|
|
message("We are in trouble here.. long long support is not here!!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
bloblen = sizeof(blob) + sizeof(unsigned char) * t_info_len;
|
|
|
|
pblob = (unsigned char *)malloc(bloblen);
|
|
if (!pblob) {
|
|
message("Fatal Error in build_ntlm2_response, Malloc failed\n");
|
|
exit(-1);
|
|
}
|
|
|
|
memset(pblob, 0, bloblen);
|
|
|
|
b = (blob *)pblob;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
b->digest[8 + i] = challenge[i];
|
|
|
|
b->signature = 0x00000101;
|
|
|
|
/* This is nasty, also not sure all this 64bit arithmetic will
|
|
* work all the time.. basically the spec says you need the
|
|
* number of 10ths of microseconds since jan 1, 1601.
|
|
*/
|
|
|
|
gettimeofday(&t, NULL);
|
|
b->timestamp = (long long)t.tv_sec;
|
|
b->timestamp += 11644473600LL;
|
|
b->timestamp *= 1000000LL;
|
|
b->timestamp += (long long)t.tv_usec;
|
|
b->timestamp *= 10LL;
|
|
|
|
// need a ramdom client challenge
|
|
for (i = 0; i < 8; i++)
|
|
b->client_challenge[i] = (unsigned char) ((256.0 * rand()) / (RAND_MAX + 1.0)) ;
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("client_challenge is: ");
|
|
for( i = 0; i < 8; i++)
|
|
message("%02X", b->client_challenge[i]);
|
|
message("\n");
|
|
}
|
|
|
|
memcpy(&b->data_start, t_info, t_info_len);
|
|
|
|
hmac_md5(&pblob[8], bloblen - 8, userdomdigest, 16, responsedigest);
|
|
|
|
for(i = 0; i < 16; i++)
|
|
b->digest[i] = responsedigest[i];
|
|
|
|
if( args_info.verbose_flag ) {
|
|
message("HMAC is: ");
|
|
for( i = 0; i < 16; i++)
|
|
message("%02X", responsedigest[i]);
|
|
message("\n");
|
|
}
|
|
|
|
// LM2 response generation
|
|
|
|
for (i = 0; i < 8; i++)
|
|
lm2data[i] = challenge[i];
|
|
|
|
for (i = 0; i < 8; i++)
|
|
lm2data[8 + i] = b->client_challenge[i];
|
|
|
|
hmac_md5(lm2data, 16, userdomdigest, 16, lm2digest);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
lm2digest[16 + i] = b->client_challenge[i];
|
|
}
|
|
|
|
// vim:noexpandtab:ts=4
|