diff options
Diffstat (limited to 'daemon/ntlmssp.c')
-rw-r--r-- | daemon/ntlmssp.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/daemon/ntlmssp.c b/daemon/ntlmssp.c new file mode 100644 index 0000000..3edb63b --- /dev/null +++ b/daemon/ntlmssp.c @@ -0,0 +1,398 @@ +/* + * $Id$ + * + */ + +#include "ntlmssp.h" +#include "smblib/smblib-priv.h" + +#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) + +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(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(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) { + if(*src != '\0' ) { + *dst = *src; + 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(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(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(unsigned char *raw_msg, + unsigned msglen, unsigned char *hostname) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + if (ntlm_extract_string(hostname, (char *) msg, msglen, + msg->host_off, msg->host_len, MAX_HOSTLEN)) + return 1; + return 0; +} + +static int +ntlm_msg1_getdomainname(unsigned char *raw_msg, + unsigned msglen, unsigned char *domainname) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + if (ntlm_extract_string(domainname, (char *) msg, + msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 2; + return 0; +} + +static int +ntlm_msg3_getlm(unsigned char *raw_msg, unsigned msglen, + unsigned char *lm) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlm_extract_mem(lm, (char *) msg, msglen, msg->lm_off, + msg->lm_len, RESP_LEN)) + return 4; + return 0; +} + +static int +ntlm_msg3_getnt(unsigned char *raw_msg, unsigned msglen, + unsigned char *nt) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlm_extract_mem(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(nt, (char *) msg, msglen, msg->lm_off, + msg->lm_len, RESP_LEN)) + return 8; + return 0; +} + +static int +ntlm_msg3_getusername(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(username, (char *) msg, msglen, + msg->user_off, msg->user_len, MAX_USERLEN)) + return 16; + } + else { /* ascii */ + if (ntlm_extract_string(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(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(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(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(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(domainname, (char *) msg, msglen, + msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 64; + } + else { /* asii */ + if (ntlm_extract_string(domainname, (char *) msg, msglen, + msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 64; + } + return 0; +} + +int ntlmssp_decode_msg(struct ntlmssp_info *info, + unsigned char *raw_msg, unsigned msglen, + unsigned *ntlmssp_flags) +{ + unsigned char flags; + int ret; + switch (info->msg_type = ntlm_msg_type(raw_msg, msglen)) { + case 1: + ret = ntlm_msg1_getntlmssp_flags(raw_msg,&flags); + *ntlmssp_flags = (unsigned) flags; + return ntlm_msg1_gethostname(raw_msg, msglen, info->host) + + ntlm_msg1_getdomainname(raw_msg, msglen, info->domain); + case 3: + return ntlm_msg3_getlm(raw_msg, msglen, info->lm) + + ntlm_msg3_getnt(raw_msg, msglen, info->nt) + + ntlm_msg3_getusername(raw_msg, msglen, info->user,*ntlmssp_flags) + + ntlm_msg3_gethostname(raw_msg, msglen, info->host,*ntlmssp_flags) + + ntlm_msg3_getdomainname(raw_msg, msglen, info->domain,*ntlmssp_flags); + } + return -1; +} + +int ntlmssp_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; +} + +int ntlmssp_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; +} + + +int ntlmssp_validuser(const char* username, const char* password, const char* server, + const char* backup, const char* domain) +{ + char *SMB_Prots[] = + {"PC NETWORK PROGRAM 1.0", + "MICROSOFT NETWORKS 1.03", + "MICROSOFT NETWORKS 3.0", + "LANMAN1.0", + "LM1.2X002", + "Samba", + "NT LM 0.12", + "NT LANMAN 1.0", + NULL}; + SMB_Handle_Type con; + + SMB_Init(); + con = SMB_Connect_Server(NULL, (char*)server); + if (con == NULL) { /* Error ... */ + con = SMB_Connect_Server(NULL, (char*)backup); + if (con == NULL) { + return (NTV_SERVER_ERROR); + } + } + if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */ + SMB_Discon(con, 0); + return (NTV_PROTOCOL_ERROR); + } + /* Test for a server in share level mode do not authenticate against + * it */ + if (con->Security == 0) { + SMB_Discon(con, 0); + return (NTV_PROTOCOL_ERROR); + } + if (SMB_Logon_Server(con, (char*)username, (char*)password, 0, (char*)domain) < 0) { + SMB_Discon(con, 0); + return (NTV_LOGON_ERROR); + } + SMB_Discon(con, 0); + return (NTV_NO_ERROR); +} + +void* ntlmssp_connect(const char* server, const char* backup, const char* domain, char* nonce) +{ + char *SMB_Prots[] = + {"PC NETWORK PROGRAM 1.0", + "MICROSOFT NETWORKS 1.03", + "MICROSOFT NETWORKS 3.0", + "LANMAN1.0", + "LM1.2X002", + "Samba", + "NT LM 0.12", + "NT LANMAN 1.0", + NULL}; + SMB_Handle_Type con; + + SMB_Init(); + con = SMB_Connect_Server(NULL, (char*)server); + if (con == NULL) { /* Error ... */ + con = SMB_Connect_Server(NULL, (char*)backup); + if (con == NULL) { + return (NULL); + } + } + if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */ + SMB_Discon(con, 0); + return (NULL); + } + /* Test for a server in share level mode do not authenticate + * against it */ + if (con->Security == 0) { + SMB_Discon(con, 0); + return (NULL); + } + memcpy(nonce, con->Encrypt_Key, 8); + + return con; +} + +int ntlmssp_auth(void* handle, const char* user, const char* password, int flag, char* domain) +{ + if (SMB_Logon_Server(handle, (char*)user, (char*)password, flag, (char*)domain) < 0) { + return (NTV_LOGON_ERROR); + } + return NTV_NO_ERROR; +} + +void ntlmssp_disconnect(void* handle) +{ + SMB_Discon(handle, 0); +} |