/* * $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); }