/* * HttpAuth * * Copyright (C) 2004 Stefan Walter * * 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. */ #include #include #include #include #include #include #include #include "ntlmssp.h" #include "smbval/smblib-priv.h" /* ----------------------------------------------------------------------------- * What new hell is this? From mod_ntlm2 */ #include "smbval/byteorder.h" #include "smbval/std-defines.h" #include "smbval/std-includes.h" #include "smbval/smblib-common.h" #include "smbval/smblib-priv.h" #include "smbval/rfcnb-common.h" #include "smbval/rfcnb-error.h" #include "smbval/rfcnb-priv.h" #include "smbval/rfcnb-util.h" #include "smbval/rfcnb-io.h" #include "smbval/rfcnb.h" #include "smbval/rfcnb-io.inc.c" #include "smbval/rfcnb-util.inc.c" #include "smbval/session.inc.c" #include "smbval/smbdes.inc.c" #include "smbval/smbencrypt.inc.c" #include "smbval/smblib-util.inc.c" #include "smbval/smblib.inc.c" /* -------------------------------------------------------------------------- */ #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((char*)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) { /* +csz 2003/02/20 - En algunos casos vienen \0 entremedio */ 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, unsigned n_hostname) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; if (ntlm_extract_string(hostname, (unsigned char*) msg, msglen, msg->host_off, msg->host_len, n_hostname)) return 1; return 0; } static int ntlm_msg1_getdomainname(unsigned char *raw_msg, unsigned msglen, unsigned char *domainname, unsigned n_domainname) { struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; if (ntlm_extract_string(domainname, (unsigned char*) msg, msglen, msg->dom_off, msg->dom_len, n_domainname)) return 2; return 0; } static int ntlm_msg3_getlm(unsigned char *raw_msg, unsigned msglen, unsigned char *lm, unsigned n_lm) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlm_extract_mem(lm, (unsigned char*) msg, msglen, msg->lm_off, msg->lm_len, n_lm)) return 4; return 0; } static int ntlm_msg3_getnt(unsigned char *raw_msg, unsigned msglen, unsigned char *nt, unsigned n_nt) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlm_extract_mem(nt, (unsigned char*) msg, msglen, msg->nt_off, msg->nt_len, n_nt)) /* Win9x: we can't extract nt ... so we use lm... */ if (ntlm_extract_mem(nt, (unsigned char*) msg, msglen, msg->lm_off, msg->lm_len, n_nt)) return 8; return 0; } static int ntlm_msg3_getusername(unsigned char *raw_msg, unsigned msglen, unsigned char *username, unsigned n_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, (unsigned char*)msg, msglen, msg->user_off, msg->user_len, n_username)) return 16; } else { /* ascii */ if (ntlm_extract_string(username, (unsigned char*)msg, msglen, msg->user_off, msg->user_len, n_username)) return 16; else { /* Win9x client leave username in uppercase...fix it: */ while (*username!=(unsigned char)0) { 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 n_hostname, unsigned ntlmssp_flags) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { if (ntlm_extract_unicode(hostname, (unsigned char*) msg, msglen, msg->host_off, msg->host_len, n_hostname)) 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, (unsigned char*) msg, msglen, msg->host_off, msg->host_len, n_hostname)) 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 n_domainname, unsigned ntlmssp_flags) { struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { if (ntlm_extract_unicode(domainname, (unsigned char*) msg, msglen, msg->dom_off, msg->dom_len, n_domainname)) return 64; } else { /* asii */ if (ntlm_extract_string(domainname, (unsigned char*) msg, msglen, msg->dom_off, msg->dom_len, n_domainname)) return 64; } return 0; } int ntlmssp_decode_msg(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(raw_msg,(unsigned char*)ntlmssp_flags) + ntlm_msg1_gethostname(raw_msg, msglen, info->host, sizeof (info->host) - 1) + ntlm_msg1_getdomainname(raw_msg, msglen, info->domain, sizeof (info->domain) - 1); case 3: return ntlm_msg3_getlm(raw_msg, msglen, info->lm, sizeof (info->lm)) + ntlm_msg3_getnt(raw_msg, msglen, info->nt, sizeof (info->nt)) + ntlm_msg3_getusername(raw_msg, msglen, info->user, sizeof (info->user) - 1, *ntlmssp_flags) + ntlm_msg3_gethostname(raw_msg, msglen, info->host, sizeof (info->host) - 1, *ntlmssp_flags) + ntlm_msg3_getdomainname(raw_msg, msglen, info->domain, sizeof (info->domain) - 1, *ntlmssp_flags); } return -1; } int ntlmssp_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg) { memset(msg, 0, sizeof(struct ntlm_msg2)); strcpy((char*)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((char*)msg->protocol, "NTLMSSP"); msg->type = 0x02; if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { /* unicode case */ len=strlen(domainname); ntlm_put_in_unicode((unsigned char*)msg->dom, (unsigned char*)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((char*)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, (char*)DOMAIN); if (con == NULL) { /* Error ... */ con = SMB_Connect_Server(NULL, (char*)BACKUP, (char*)DOMAIN); 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) < 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, (char*)DOMAIN); if (con == NULL) { /* Error ... */ con = SMB_Connect_Server(NULL, (char*)BACKUP, (char*)DOMAIN); 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 *USERNAME, const char *PASSWORD, int flag) { SMB_Handle_Type con = handle; if (SMB_Logon_Server(con, (char*)USERNAME, (char*)PASSWORD, flag) < 0) { return (NTV_LOGON_ERROR); } return NTV_NO_ERROR; } void ntlmssp_disconnect(void *handle) { SMB_Handle_Type con = handle; SMB_Discon(con, 0); }