/* * $Id: ntlmssp.inc.c,v 1.2 2003/02/21 01:48:31 casz Exp $ * */ #define MAX_HOSTLEN 32 #define MAX_DOMLEN 32 #define MAX_USERLEN 32 #define RESP_LEN 24 #define NONCE_LEN 8 /* fhz, 01-10-15 : borrowed from samba code */ /* NTLMSSP negotiation flags */ #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 #define NTLMSSP_NEGOTIATE_OEM 0x00000002 #define NTLMSSP_REQUEST_TARGET 0x00000004 #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 #define NTLMSSP_NEGOTIATE_00001000 0x00001000 #define NTLMSSP_NEGOTIATE_00002000 0x00002000 #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 #define NTLMSSP_NEGOTIATE_128 0x20000000 #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 #define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1 #define NTLM_NTLMSSP_NEG_FLAGS 0x00008206 /* 8201 8207 */ #define LEN_NTLMSSP_FLAGS 4 #define OFFSET_MSG1_NTLMSSP_FLAGS 12 struct ntlm_msg1 { unsigned char protocol[8]; unsigned char type; /* 1 */ unsigned char zero1[3]; unsigned char flags[2]; unsigned char zero2[2]; unsigned char dom_len[4]; unsigned char dom_off[4]; unsigned char host_len[4]; unsigned char host_off[4]; #if 0 unsigned char data[0]; #endif } __attribute__((packed)); struct ntlm_msg2 { unsigned char protocol[8]; unsigned char type; /* 2 */ unsigned char zero1[7]; unsigned char msg_len[4]; unsigned char flags[2]; unsigned char zero2[2]; unsigned char nonce[8]; unsigned char zero3[8]; } __attribute__((packed)); struct ntlm_msg3 { unsigned char protocol[8]; unsigned char type; /* 3 */ unsigned char zero1[3]; unsigned char lm_len[4]; unsigned char lm_off[4]; unsigned char nt_len[4]; unsigned char nt_off[4]; unsigned char dom_len[4]; unsigned char dom_off[4]; unsigned char user_len[4]; unsigned char user_off[4]; unsigned char host_len[4]; unsigned char host_off[4]; unsigned char msg_len[4]; /* Win9x: data begins here! */ #if 0 unsigned char data[0]; #endif } __attribute__((packed)); struct ntlm_msg2_win9x { unsigned char protocol[8]; unsigned char type; /* 2 */ unsigned char zero1[3]; unsigned char dom_len1[2]; unsigned char dom_len2[2]; unsigned char dom_off[4]; unsigned char flags[2]; unsigned char zero2[2]; unsigned char nonce[8]; unsigned char zero3[8]; unsigned char zero4[4]; unsigned char msg_len[4]; unsigned char dom[MAX_DOMLEN]; } __attribute__((packed)); /* size without dom[] : */ #define NTLM_MSG2_WIN9X_FIXED_SIZE (sizeof(struct ntlm_msg2_win9x)-MAX_DOMLEN) typedef struct ntlmssp_info { int msg_type; unsigned char user[MAX_USERLEN + 1]; unsigned char host[MAX_HOSTLEN + 1]; unsigned char domain[MAX_DOMLEN + 1]; unsigned char lm[RESP_LEN]; unsigned char nt[RESP_LEN]; } ntlmssp_info_rec; #define little_endian_word(x) x[0] + (((unsigned)x[1]) << 8) /* fhz 02-02-09: typecasting is needed for a generic use */ #define set_little_endian_word(x,y) (*((char *)x))=(y&0xff);*(((char*)x)+1)=((y>>8)&0xff) static int ntlm_msg_type(unsigned char *raw_msg, unsigned msglen) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; if (msglen < 9) return -1; if (strncmp(msg->protocol, "NTLMSSP", 8)) return -1; return msg->type; } static int ntlm_extract_mem(request_rec * r, unsigned char *dst, unsigned char *src, unsigned srclen, unsigned char *off, unsigned char *len, unsigned max) { unsigned o = little_endian_word(off); unsigned l = little_endian_word(len); if (l > max) return -1; if (o >= srclen) return -1; if (o + l > srclen) return -1; src += o; while (l-- > 0) *dst++ = *src++; return 0; } static int ntlm_extract_string(request_rec * r, unsigned char *dst, unsigned char *src, unsigned srclen, unsigned char *off, unsigned char *len, unsigned max) { unsigned o = little_endian_word(off); unsigned l = little_endian_word(len); if (l > max) return -1; if (o >= srclen) return -1; if (o + l > srclen) return -1; src += o; while (l-- > 0) *dst++ = *src++; *dst = 0; return 0; } static int ntlm_put_in_unicode(unsigned char *dst, unsigned char *src, unsigned srclen, unsigned max) { unsigned l = srclen*2; if (l > max) l=max; /* fhz: bad very bad */ while (l > 0) { /* ASCII to unicode*/ *dst++ = *src++; *dst++=0; l -=2; } return 0; } static int ntlm_extract_unicode(request_rec * r, unsigned char *dst, unsigned char *src, unsigned srclen, unsigned char *off, unsigned char *len, unsigned max) { unsigned o = little_endian_word(off); unsigned l = little_endian_word(len) / 2; /* Unicode! */ if (l > max) return -1; if (o >= srclen) return -1; if (o + l > srclen) return -1; src += o; while (l > 0) { /* Unicode to ASCII */ *dst++ = *src; src += 2; l -= 2; } *dst = 0; return 0; } static int ntlm_msg1_getntlmssp_flags(request_rec * r, unsigned char *raw_msg, unsigned char *ntlmssp_flags) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; *ntlmssp_flags=little_endian_word(msg->flags); return 0; } static int ntlm_msg1_gethostname(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *hostname) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; if (ntlm_extract_string(r, hostname, (char *) msg, msglen, msg->host_off, msg->host_len, MAX_HOSTLEN)) return 1; return 0; } static int ntlm_msg1_getdomainname(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *domainname) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; if (ntlm_extract_string(r, domainname, (char *) msg, msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN)) return 2; return 0; } static int ntlm_msg3_getlm(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *lm) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlm_extract_mem(r, lm, (char *) msg, msglen, msg->lm_off, msg->lm_len, RESP_LEN)) return 4; return 0; } static int ntlm_msg3_getnt(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *nt) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlm_extract_mem(r, nt, (char *) msg, msglen, msg->nt_off, msg->nt_len, RESP_LEN)) /* Win9x: we can't extract nt ... so we use lm... */ if (ntlm_extract_mem(r, nt, (char *) msg, msglen, msg->lm_off, msg->lm_len, RESP_LEN)) return 8; return 0; } static int ntlm_msg3_getusername(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *username, unsigned ntlmssp_flags) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; int c; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { if (ntlm_extract_unicode(r, username, (char *) msg, msglen, msg->user_off, msg->user_len, MAX_USERLEN)) return 16; } else { /* ascii */ if (ntlm_extract_string(r, username, (char *) msg, msglen, msg->user_off, msg->user_len, MAX_USERLEN)) return 16; else { /* Win9x client leave username in uppercase...fix it: */ while (*username!=(unsigned char)NULL) { c=tolower((int)*username); *username=(unsigned char)c; username++; } } } return 0; } static int ntlm_msg3_gethostname(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *hostname,unsigned ntlmssp_flags) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { if (ntlm_extract_unicode(r, hostname, (char *) msg, msglen, msg->host_off, msg->host_len, MAX_HOSTLEN)) return 0; /* this one FAILS, but since the value is not used, * we just pretend it was ok. */ } else { /* ascii */ if (ntlm_extract_string(r, hostname, (char *) msg, msglen, msg->host_off, msg->host_len, MAX_HOSTLEN)) return 0; /* this one FAILS, but since the value is not used, * we just pretend it was ok. */ } return 0; } static int ntlm_msg3_getdomainname(request_rec * r, unsigned char *raw_msg, unsigned msglen, unsigned char *domainname, unsigned ntlmssp_flags) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { if (ntlm_extract_unicode(r, domainname, (char *) msg, msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN)) return 64; } else { /* asii */ if (ntlm_extract_string(r, domainname, (char *) msg, msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN)) return 64; } return 0; } static int ntlm_decode_msg(request_rec * r, struct ntlmssp_info *info, unsigned char *raw_msg, unsigned msglen, unsigned *ntlmssp_flags) { switch (info->msg_type = ntlm_msg_type(raw_msg, msglen)) { case 1: return ntlm_msg1_getntlmssp_flags(r,raw_msg,(unsigned char*)ntlmssp_flags) + ntlm_msg1_gethostname(r, raw_msg, msglen, info->host) + ntlm_msg1_getdomainname(r, raw_msg, msglen, info->domain); case 3: return ntlm_msg3_getlm(r, raw_msg, msglen, info->lm) + ntlm_msg3_getnt(r, raw_msg, msglen, info->nt) + ntlm_msg3_getusername(r, raw_msg, msglen, info->user,*ntlmssp_flags) + ntlm_msg3_gethostname(r, raw_msg, msglen, info->host,*ntlmssp_flags) + ntlm_msg3_getdomainname(r, raw_msg, msglen, info->domain,*ntlmssp_flags); } return -1; } static int ntlm_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg) { memset(msg, 0, sizeof(struct ntlm_msg2)); strcpy(msg->protocol, "NTLMSSP"); msg->type = 0x02; set_little_endian_word(msg->msg_len, sizeof(struct ntlm_msg2)); set_little_endian_word(msg->flags, 0x8201); memcpy(msg->nonce, nonce, sizeof(msg->nonce)); return 0; } static int ntlm_encode_msg2_win9x(unsigned char *nonce, struct ntlm_msg2_win9x *msg,char *domainname,unsigned ntlmssp_flags) { unsigned int size,len,flags; memset(msg, 0, sizeof(struct ntlm_msg2_win9x)); strcpy(msg->protocol, "NTLMSSP"); msg->type = 0x02; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { /* unicode case */ len=strlen(domainname); ntlm_put_in_unicode((char *)msg->dom,domainname, len, MAX_DOMLEN); len=len*2; if (len>MAX_DOMLEN) len=MAX_DOMLEN; /* fhz: bad very bad */ flags=NTLM_NTLMSSP_NEG_FLAGS | NTLMSSP_NEGOTIATE_UNICODE; } else { /* ascii case */ len=strlen(domainname); if (len>MAX_DOMLEN) len=MAX_DOMLEN; /* fhz: bad very bad */ strncpy(msg->dom,domainname,len); flags=NTLM_NTLMSSP_NEG_FLAGS; } size=NTLM_MSG2_WIN9X_FIXED_SIZE+len; set_little_endian_word(msg->dom_off, NTLM_MSG2_WIN9X_FIXED_SIZE); set_little_endian_word(msg->dom_len1,len); set_little_endian_word(msg->dom_len2,len); set_little_endian_word(msg->msg_len,size); set_little_endian_word(msg->flags,flags); if (ntlmssp_flags & NTLMSSP_REQUEST_TARGET) set_little_endian_word(msg->zero2, 0x01); /* == set NTLMSSP_TARGET_TYPE_DOMAIN */ memcpy(msg->nonce, nonce, sizeof(msg->nonce)); return size; }