/* * (C) 2000 Francesco Chemolli * Distributed freely under the terms of the GNU General Public License, * version 2. See the file COPYING for licensing details * * 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, USA. */ typedef unsigned char uchar; #include "ntlm_smb_lm_auth.h" #include "util.h" /* from Squid */ #include "valid.h" #include "smbencrypt.h" #if HAVE_STRING_H #include #endif /* HAVE_STRING_H */ #if HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #ifdef HAVE_UNISTD_H #include #endif /* these are part of rfcnb-priv.h and smblib-priv.h */ extern int SMB_Get_Error_Msg(int msg, char *msgbuf, int len); extern int SMB_Get_Last_Error(); extern int RFCNB_Get_Last_Errno(); #include "smblib-priv.h" /* for SMB_Handle_Type */ /* a few forward-declarations. Hackish, but I don't care right now */ SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle, char *server, char *NTdomain); /* this one is reallllly haackiish. We really should be using anything from smblib-priv.h */ static char const *SMB_Prots[] = {"PC NETWORK PROGRAM 1.0", "MICROSOFT NETWORKS 1.03", "MICROSOFT NETWORKS 3.0", "DOS LANMAN1.0", "LANMAN1.0", "DOS LM1.2X002", "LM1.2X002", "DOS LANMAN2.1", "LANMAN2.1", "Samba", "NT LM 0.12", "NT LANMAN 1.0", NULL }; #if 0 int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle); int SMB_Negotiate(void *Con_Handle, char *Prots[]); int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName, char *PassWord, char *Domain, int precrypted); #endif #ifdef DEBUG #define debug_dump_ntlmssp_flags dump_ntlmssp_flags #else /* DEBUG */ #define debug_dump_ntlmssp_flags(X) /* empty */ #endif /* DEBUG */ #define ENCODED_PASS_LEN 24 static unsigned char challenge[NONCE_LEN]; static unsigned char lmencoded_empty_pass[ENCODED_PASS_LEN], ntencoded_empty_pass[ENCODED_PASS_LEN]; SMB_Handle_Type handle = NULL; /* Disconnects from the DC. A reconnection will be done upon the next request */ void dc_disconnect() { if (handle != NULL) SMB_Discon(handle, 0); handle = NULL; } int connectedp() { return (handle != NULL); } /* Tries to connect to a DC. Returns 0 on failure, 1 on OK */ int is_dc_ok(char *domain, char *domain_controller) { SMB_Handle_Type h = SMB_Connect_Server(NULL, domain_controller, domain); if (h == NULL) return 0; SMB_Discon(h, 0); return 1; } static char errstr[1001]; /* returns 0 on success, > 0 on failure */ static int init_challenge(char *domain, char *domain_controller) { int smberr; if (handle != NULL) { return 0; } debug("Connecting to server %s domain %s\n", domain_controller, domain); handle = SMB_Connect_Server(NULL, domain_controller, domain); smberr = SMB_Get_Last_Error(); SMB_Get_Error_Msg(smberr, errstr, 1000); if (handle == NULL) { /* couldn't connect */ debug("Couldn't connect to SMB Server. Error:%s\n", errstr); return 1; } if (SMB_Negotiate(handle, SMB_Prots) < 0) { /* An error */ debug("Error negotiating protocol with SMB Server\n"); SMB_Discon(handle, 0); handle = NULL; return 2; } if (handle->Security == 0) { /* share-level security, unuseable */ debug("SMB Server uses share-level security .. we need user security.\n"); SMB_Discon(handle, 0); handle = NULL; return 3; } memcpy(challenge, handle->Encrypt_Key, NONCE_LEN); SMBencrypt((unsigned char *)"",challenge,lmencoded_empty_pass); SMBNTencrypt((unsigned char *)"",challenge,ntencoded_empty_pass); return 0; } static char my_domain[100], my_domain_controller[100]; const char * make_challenge(char *domain, char *domain_controller) { /* trying to circumvent some strange problem wih pointers in SMBLib */ /* Ugly as hell, but the lib is going to be dropped... */ strcpy(my_domain,domain); strcpy(my_domain_controller,domain_controller); if (init_challenge(my_domain, my_domain_controller) > 0) { return NULL; } return ntlm_make_challenge(my_domain, my_domain_controller, (char *)challenge, NONCE_LEN); } int ntlm_errno; #define MAX_USERNAME_LEN 255 #define MAX_DOMAIN_LEN 255 #define MAX_PASSWD_LEN 31 static char credentials[MAX_USERNAME_LEN+MAX_DOMAIN_LEN+2]; /* we can afford to waste */ /* Fetches the user's credentials from the challenge. * Returns NULL if domain or user is not defined * No identity control is performed. * WARNING! The result is static storage, shared with ntlm_check_auth */ char * fetch_credentials(ntlm_authenticate * auth, int auth_length) { char *p = credentials; lstring tmp; tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain); *p = '\0'; if (tmp.str == NULL) return NULL; memcpy(p, tmp.str, tmp.l); p += tmp.l; *p++ = '\\'; *p = '\0'; tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user); if (tmp.str == NULL) return NULL; memcpy(p, tmp.str, tmp.l); *(p + tmp.l) = '\0'; return credentials; } /* returns NULL on failure, or a pointer to * the user's credentials (domain\\username) * upon success. WARNING. It's pointing to static storage. * In case of problem sets as side-effect ntlm_errno to one of the * codes defined in ntlm.h */ char * ntlm_check_auth(ntlm_authenticate * auth, int auth_length) { int rv; char pass[MAX_PASSWD_LEN+1]; char *domain = credentials; char *user; lstring tmp; if (handle == NULL) { /*if null we aren't connected, but it shouldn't happen */ debug("Weird, we've been disconnected\n"); ntlm_errno = NTLM_NOT_CONNECTED; return NULL; } /* debug("fetching domain\n"); */ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain); if (tmp.str == NULL || tmp.l == 0) { debug("No domain supplied. Returning no-auth\n"); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } if (tmp.l > MAX_DOMAIN_LEN) { debug("Domain string exceeds %d bytes, rejecting\n", MAX_DOMAIN_LEN); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } memcpy(domain, tmp.str, tmp.l); user = domain + tmp.l; *user++ = '\0'; /* debug("fetching user name\n"); */ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user); if (tmp.str == NULL || tmp.l == 0) { debug("No username supplied. Returning no-auth\n"); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } if (tmp.l > MAX_USERNAME_LEN) { debug("Username string exceeds %d bytes, rejecting\n", MAX_USERNAME_LEN); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } memcpy(user, tmp.str, tmp.l); *(user + tmp.l) = '\0'; /* Authenticating against the NT response doesn't seem to work... */ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->lmresponse); if (tmp.str == NULL || tmp.l == 0) { fprintf(stderr, "No auth at all. Returning no-auth\n"); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } if (tmp.l > MAX_PASSWD_LEN) { debug("Password string exceeds %d bytes, rejecting\n", MAX_PASSWD_LEN); ntlm_errno = NTLM_LOGON_ERROR; return NULL; } memcpy(pass, tmp.str, tmp.l); pass[min(MAX_PASSWD_LEN,tmp.l)] = '\0'; #if 1 debug ("Empty LM pass detection: user: '%s', ours:'%s', his: '%s'" "(length: %d)\n", user,lmencoded_empty_pass,tmp.str,tmp.l); if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { fprintf(stderr,"Empty LM password supplied for user %s\\%s. " "No-auth\n",domain,user); ntlm_errno=NTLM_LOGON_ERROR; return NULL; } tmp = ntlm_fetch_string ((char *) auth, auth_length, &auth->ntresponse); if (tmp.str != NULL && tmp.l != 0) { debug ("Empty NT pass detection: user: '%s', ours:'%s', his: '%s'" "(length: %d)\n", user,ntencoded_empty_pass,tmp.str,tmp.l); if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { fprintf(stderr,"Empty NT password supplied for user %s\\%s. " "No-auth\n",domain,user); ntlm_errno=NTLM_LOGON_ERROR; return NULL; } } #endif /* TODO: check against empty password!!!!! */ debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass); rv = SMB_Logon_Server(handle, user, pass, domain, 1); debug("Login attempt had result %d\n", rv); if (rv != NTV_NO_ERROR) { /* failed */ ntlm_errno = rv; return NULL; } *(user - 1) = '\\'; /* hack. Performing, but ugly. */ debug("credentials: %s\n", credentials); return credentials; }