From fa88efbbacbbe74b72ddb9e316265de2aab31260 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 10 Aug 1998 07:04:53 +0000 Subject: [PATCH] split the system password checking routines out of smbd/password.c and into passdb/pass_check.c. This means SWAT no longer needs to link to smbd/password.c (This used to be commit 90d93889d722670cbb517017531264630af759bf) --- source3/Makefile.in | 12 +- source3/auth/pass_check.c | 950 ++++++++++++++++++++++++++++++++++ source3/include/proto.h | 24 +- source3/passdb/pass_check.c | 950 ++++++++++++++++++++++++++++++++++ source3/smbd/password.c | 1182 ++++++------------------------------------- source3/smbd/server.c | 3 +- source3/web/cgi.c | 2 +- 7 files changed, 2063 insertions(+), 1060 deletions(-) create mode 100644 source3/auth/pass_check.c create mode 100644 source3/passdb/pass_check.c diff --git a/source3/Makefile.in b/source3/Makefile.in index 7a31b2b4425..1f975d48c59 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -116,15 +116,15 @@ RPC_CLIENT_OBJ = rpc_client/cli_login.o rpc_client/cli_netlogon.o \ LOCKING_OBJ = locking/locking.o locking/locking_shm.o locking/locking_slow.o \ locking/shmem.o locking/shmem_sysv.o -PASSDB_OBJ = passdb/passdb.o passdb/smbpassfile.o passdb/smbpass.o +PASSDB_OBJ = passdb/passdb.o passdb/smbpassfile.o passdb/smbpass.o \ + passdb/pass_check.o SMBD_OBJ1 = smbd/server.o smbd/access.o smbd/chgpasswd.o smbd/connection.o \ smbd/dfree.o smbd/dir.o smbd/password.o \ smbd/groupname.o smbd/ipc.o smbd/ldap.o smbd/mangle.o \ smbd/message.o smbd/nispass.o smbd/nttrans.o \ - smbd/pipes.o smbd/predict.o \ - smbd/print_svid.o smbd/printing.o smbd/quotas.o smbd/reply.o \ - smbd/ssl.o smbd/trans2.o smbd/uid.o + smbd/pipes.o smbd/predict.o smbd/print_svid.o smbd/printing.o \ + smbd/quotas.o smbd/reply.o smbd/ssl.o smbd/trans2.o smbd/uid.o SMBD_OBJ = $(SMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \ $(RPC_SERVER_OBJ) $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) \ @@ -148,9 +148,7 @@ NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \ $(PASSDB_OBJ) $(LIB_OBJ) SWAT_OBJ = web/cgi.o web/diagnose.o web/startstop.o web/statuspage.o \ - web/swat.o \ - smbd/password.o \ - $(LIBSMB_OBJ) $(LOCKING_OBJ) \ + web/swat.o $(LIBSMB_OBJ) $(LOCKING_OBJ) \ $(PARAM_OBJ) $(PASSDB_OBJ) $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) \ $(UBIQX_OBJ) $(LIB_OBJ) diff --git a/source3/auth/pass_check.c b/source3/auth/pass_check.c new file mode 100644 index 00000000000..b5aa832f48b --- /dev/null +++ b/source3/auth/pass_check.c @@ -0,0 +1,950 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password checking + Copyright (C) Andrew Tridgell 1992-1998 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this module is for checking a username/password against a system + password database. The SMB encrypted password support is elsewhere */ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/* these are kept here to keep the string_combinations function simple */ +static char this_user[100]=""; +static char this_salt[100]=""; +static char this_crypted[100]=""; + + +/**************************************************************************** +update the enhanced security database. Only relevant for OSF1 at the moment. +****************************************************************************/ +static void update_protected_database(char *user, BOOL result) +{ +#ifdef OSF1_ENH_SEC + struct pr_passwd *mypasswd; + time_t starttime; + + mypasswd = getprpwnam (user); + starttime = time (NULL); + + if (result) { + mypasswd->ufld.fd_slogin = starttime; + mypasswd->ufld.fd_nlogins = 0; + + putprpwnam(user,mypasswd); + } else { + mypasswd->ufld.fd_ulogin = starttime; + mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; + if (mypasswd->ufld.fd_max_tries != 0 && + mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries) { + mypasswd->uflg.fg_lock = 0; + DEBUG(3,("Account %s is disabled\n", user)); + } + putprpwnam(user ,mypasswd); + } +#endif +} + + +#ifdef HAVE_PAM +/******************************************************************* +check on PAM authentication +********************************************************************/ + +/* We first need some helper functions */ +#include +/* Static variables used to communicate between the conversation function + * and the server_login function + */ +static char *PAM_username; +static char *PAM_password; + +/* PAM conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ +static int PAM_conv (int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) { + int replies = 0; + struct pam_response *reply = NULL; + + #define COPY_STRING(s) (s) ? strdup(s) : NULL + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) return PAM_CONV_ERR; + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_username); + /* PAM frees resp */ + break; + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_password); + /* PAM frees resp */ + break; + case PAM_TEXT_INFO: + /* fall through */ + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + default: + /* Must be an error of some sort... */ + free (reply); + return PAM_CONV_ERR; + } + } + if (reply) *resp = reply; + return PAM_SUCCESS; +} +static struct pam_conv PAM_conversation = { + &PAM_conv, + NULL +}; + + +static BOOL pam_auth(char *this_user,char *password) +{ + pam_handle_t *pamh; + int pam_error; + + /* Now use PAM to do authentication. For now, we won't worry about + * session logging, only authentication. Bail out if there are any + * errors. Since this is a limited protocol, and an even more limited + * function within a server speaking this protocol, we can't be as + * verbose as would otherwise make sense. + * Query: should we be using PAM_SILENT to shut PAM up? + */ + #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \ + pam_end(pamh, 0); return False; \ + } + PAM_password = password; + PAM_username = this_user; + pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh); + PAM_BAIL; +/* Setting PAM_SILENT stops generation of error messages to syslog + * to enable debugging on Red Hat Linux set: + * /etc/pam.d/samba: + * auth required /lib/security/pam_pwdb.so nullok shadow audit + * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging) + */ + pam_error = pam_authenticate(pamh, PAM_SILENT); + PAM_BAIL; + /* It is not clear to me that account management is the right thing + * to do, but it is not clear that it isn't, either. This can be + * removed if no account management should be done. Alternately, + * put a pam_allow.so entry in /etc/pam.conf for account handling. */ + pam_error = pam_acct_mgmt(pamh, PAM_SILENT); + PAM_BAIL; + pam_end(pamh, PAM_SUCCESS); + /* If this point is reached, the user has been authenticated. */ + return(True); +} +#endif + + +#ifdef WITH_AFS +/******************************************************************* +check on AFS authentication +********************************************************************/ +static BOOL afs_auth(char *this_user,char *password) +{ + long password_expires = 0; + char *reason; + + /* For versions of AFS prior to 3.3, this routine has few arguments, */ + /* but since I can't find the old documentation... :-) */ + setpag(); + if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG, + this_user, + (char *) 0, /* instance */ + (char *) 0, /* cell */ + password, + 0, /* lifetime, default */ + &password_expires, /*days 'til it expires */ + 0, /* spare 2 */ + &reason) == 0) { + return(True); + } + return(False); +} +#endif + + +#ifdef WITH_DFS + +/***************************************************************** + This new version of the DFS_AUTH code was donated by Karsten Muuss + . It fixes the following problems with the + old code : + + - Server credentials may expire + - Client credential cache files have wrong owner + - purge_context() function is called with invalid argument + + This new code was modified to ensure that on exit the uid/gid is + still root, and the original directory is restored. JRA. +******************************************************************/ + +sec_login_handle_t my_dce_sec_context; +int dcelogin_atmost_once = 0; + +/******************************************************************* +check on a DCE/DFS authentication +********************************************************************/ +static BOOL dfs_auth(char *this_user,char *password) +{ + error_status_t err; + int err2; + int prterr; + signed32 expire_time, current_time; + boolean32 password_reset; + struct passwd *pw; + sec_passwd_rec_t passwd_rec; + sec_login_auth_src_t auth_src = sec_login_auth_src_network; + unsigned char dce_errstr[dce_c_error_string_len]; + + if (dcelogin_atmost_once) return(False); + +#ifdef HAVE_CRYPT + /* + * We only go for a DCE login context if the given password + * matches that stored in the local password file.. + * Assumes local passwd file is kept in sync w/ DCE RGY! + */ + + if (strcmp((char *)crypt(password,this_salt),this_crypted)) { + return(False); + } +#endif + + sec_login_get_current_context(&my_dce_sec_context, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); + + return(False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); + + return(False); + } + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); + + return(False); + } + + time(¤t_time); + + if (expire_time < (current_time + 60)) { + struct passwd *pw; + sec_passwd_rec_t *key; + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + sec_login_refresh_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't refresh identity. %s\n", + dce_errstr)); + + return(False); + } + + sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL, + (unsigned char *)pw->pw_name, + sec_c_key_version_none, + (void**)&key, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get key for %s. %s\n", + pw->pw_name, dce_errstr)); + + return(False); + } + + sec_login_valid_and_cert_ident(my_dce_sec_context, key, + &password_reset, &auth_src, + &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't validate and certify identity for %s. %s\n", + pw->pw_name, dce_errstr)); + } + + sec_key_mgmt_free_key(key, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't free key.\n", dce_errstr)); + } + } + + if (sec_login_setup_identity((unsigned char *)this_user, + sec_login_no_flags, + &my_dce_sec_context, + &err) == 0) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE Setup Identity for %s failed: %s\n", + this_user,dce_errstr)); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't purge context. %s\n", dce_errstr)); + + return(False); + } + + /* + * NB. I'd like to change these to call something like become_user() + * instead but currently we don't have a connection + * context to become the correct user. This is already + * fairly platform specific code however, so I think + * this should be ok. I have added code to go + * back to being root on error though. JRA. + */ + + if (setregid(-1, pw->pw_gid) != 0) { + DEBUG(0,("Can't set egid to %d (%s)\n", + pw->pw_gid, strerror(errno))); + return False; + } + + if (setreuid(-1, pw->pw_uid) != 0) { + setgid(0); + DEBUG(0,("Can't set euid to %d (%s)\n", + pw->pw_uid, strerror(errno))); + return False; + } + + if (sec_login_setup_identity((unsigned char *)this_user, + sec_login_no_flags, + &my_dce_sec_context, + &err) == 0) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE Setup Identity for %s failed: %s\n", + this_user,dce_errstr)); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + passwd_rec.version_number = sec_passwd_c_version_none; + passwd_rec.pepper = NULL; + passwd_rec.key.key_type = sec_passwd_plain; + passwd_rec.key.tagged_union.plain = (idl_char *)password; + + sec_login_validate_identity(my_dce_sec_context, + &passwd_rec, &password_reset, + &auth_src, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n", + this_user,dce_errstr)); + + return(False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr)); + + return(False); + } + + if (auth_src != sec_login_auth_src_network) { + DEBUG(0,("DCE context has no network credentials.\n")); + } + + sec_login_set_context(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n", + this_user,dce_errstr)); + + sec_login_purge_context(&my_dce_sec_context, &err); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + return(False); + } + + DEBUG(0,("DCE login succeeded for principal %s on pid %d\n", + this_user, getpid())); + + DEBUG(3,("DCE principal: %s\n" + " uid: %d\n" + " gid: %d\n", + pw->pw_name, pw->pw_uid, pw->pw_gid)); + DEBUG(3,(" info: %s\n" + " dir: %s\n" + " shell: %s\n", + pw->pw_gecos, pw->pw_dir, pw->pw_shell)); + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); + + return(False); + } + + setuid(0); + setgid(0); + + DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time)))); + + dcelogin_atmost_once = 1; + return (True); +} + +void dfs_unlogin(void) +{ + error_status_t err; + int err2; + unsigned char dce_errstr[dce_c_error_string_len]; + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE purge login context failed for server instance %d: %s\n", + getpid(), dce_errstr)); + } +} +#endif + +#ifdef KRB5_AUTH +/******************************************************************* +check on Kerberos authentication +********************************************************************/ +static BOOL krb5_auth(char *this_user,char *password) +{ + krb5_data tgtname = { + 0, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME + }; + krb5_context kcontext; + krb5_principal kprinc; + krb5_principal server; + krb5_creds kcreds; + int options = 0; + krb5_address **addrs = (krb5_address **)0; + krb5_preauthtype *preauth = NULL; + krb5_keytab keytab = NULL; + krb5_timestamp now; + krb5_ccache ccache = NULL; + int retval; + char *name; + + if (retval=krb5_init_context(&kcontext)) { + return(False); + } + + if (retval = krb5_timeofday(kcontext, &now)) { + return(False); + } + + if (retval = krb5_cc_default(kcontext, &ccache)) { + return(False); + } + + if (retval = krb5_parse_name(kcontext, this_user, &kprinc)) { + return(False); + } + + memset((char *)&kcreds, 0, sizeof(kcreds)); + + kcreds.client = kprinc; + + if ((retval = krb5_build_principal_ext(kcontext, &server, + krb5_princ_realm(kcontext, kprinc)->length, + krb5_princ_realm(kcontext, kprinc)->data, + tgtname.length, + tgtname.data, + krb5_princ_realm(kcontext, kprinc)->length, + krb5_princ_realm(kcontext, kprinc)->data, + 0))) { + return(False); + } + + kcreds.server = server; + + retval = krb5_get_in_tkt_with_password(kcontext, + options, + addrs, + NULL, + preauth, + password, + 0, + &kcreds, + 0); + + if (retval) { + return(False); + } + + return(True); +} +#endif /* KRB5_AUTH */ + +#ifdef KRB4_AUTH +/******************************************************************* +check on Kerberos authentication +********************************************************************/ +static BOOL krb4_auth(char *this_user,char *password) +{ + char realm[REALM_SZ]; + char tkfile[MAXPATHLEN]; + + if (krb_get_lrealm(realm, 1) != KSUCCESS) { + (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1); + } + + (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", + getpid()); + + krb_set_tkt_string(tkfile); + if (krb_verify_user(this_user, "", realm, + password, 0, + "rmcd") == KSUCCESS) { + unlink(tkfile); + return 1; + } + unlink(tkfile); + return 0; +} +#endif /* KRB4_AUTH */ + +#ifdef LINUX_BIGCRYPT +/**************************************************************************** +an enhanced crypt for Linux to handle password longer than 8 characters +****************************************************************************/ +static int linux_bigcrypt(char *password,char *salt1, char *crypted) +{ +#define LINUX_PASSWORD_SEG_CHARS 8 + char salt[3]; + int i; + + StrnCpy(salt,salt1,2); + crypted +=2; + + for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { + char * p = crypt(password,salt) + 2; + if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) + return(0); + password += LINUX_PASSWORD_SEG_CHARS; + crypted += strlen(p); + } + + return(1); +} +#endif + +#ifdef OSF1_ENH_SEC +/**************************************************************************** +an enhanced crypt for OSF1 +****************************************************************************/ +static char *osf1_bigcrypt(char *password,char *salt1) +{ + static char result[AUTH_MAX_PASSWD_LENGTH] = ""; + char *p1; + char *p2=password; + char salt[3]; + int i; + int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; + if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) { + parts++; + } + + StrnCpy(salt,salt1,2); + StrnCpy(result,salt1,2); + + for (i=0; i= len) { + return(fn(s)); + } + + for (i=offset;i<(len-(N-1));i++) { + char c = s[i]; + if (!islower(c)) continue; + s[i] = toupper(c); + if (string_combinations2(s,i+1,fn,N-1)) + return(True); + s[i] = c; + } + return(False); +} + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with up to N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N) +{ + int n; + for (n=1;n<=N;n++) + if (string_combinations2(s,0,fn,n)) return(True); + return(False); +} + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static BOOL password_check(char *password) +{ + +#ifdef HAVE_PAM + /* This falls through if the password check fails + - if HAVE_CRYPT is not defined this causes an error msg + saying Warning - no crypt available + - if HAVE_CRYPT is defined this is a potential security hole + as it may authenticate via the crypt call when PAM + settings say it should fail. + if (pam_auth(this_user,password)) return(True); + Hence we make a direct return to avoid a second chance!!! + */ + return (pam_auth(this_user,password)); +#endif + +#ifdef WITH_AFS + if (afs_auth(this_user,password)) return(True); +#endif + +#ifdef WITH_DFS + if (dfs_auth(this_user,password)) return(True); +#endif + +#ifdef KRB5_AUTH + if (krb5_auth(this_user,password)) return(True); +#endif + +#ifdef KRB4_AUTH + if (krb4_auth(this_user,password)) return(True); +#endif + +#ifdef OSF1_ENH_SEC + { + BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); + if(!ret) { + DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0); + } + return ret; + } +#endif + +#ifdef ULTRIX_AUTH + return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); +#endif + +#ifdef LINUX_BIGCRYPT + return(linux_bigcrypt(password,this_salt,this_crypted)); +#endif + +#ifdef HAVE_BIGCRYPT + return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0); +#endif + +#ifndef HAVE_CRYPT + DEBUG(1,("Warning - no crypt available\n")); + return(False); +#else + return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); +#endif +} + + + +/**************************************************************************** +check if a username/password is OK +the function pointer fn() points to a function to call when a successful +match is found and is used to update the encrypted password file +return True on correct match, False otherwise +****************************************************************************/ +BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd, + BOOL (*fn)(char *, char *)) +{ + pstring pass2; + int level = lp_passwordlevel(); + struct passwd *pass; + + if (password) password[pwlen] = 0; + +#if DEBUG_PASSWORD + DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password)); +#endif + + if (!password) { + return(False); + } + + if (((!*password) || (!pwlen)) && !lp_null_passwords()) { + return(False); + } + + if (pwd && !user) { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } else { + pass = Get_Pwnam(user,True); + } + + + DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen)); + + if (!pass) { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); + } + +#ifdef HAVE_GETSPNAM + { + struct spwd *spass; + + /* many shadow systems require you to be root to get + the password, in most cases this should already be + the case when this function is called, except + perhaps for IPC password changing requests */ + + spass = getspnam(pass->pw_name); + if (spass && spass->sp_pwdp) { + pass->pw_passwd = spass->sp_pwdp; + } + } +#elif defined(IA_UINFO) + { + /* Need to get password with SVR4.2's ia_ functions + instead of get{sp,pw}ent functions. Required by + UnixWare 2.x, tested on version + 2.1. (tangent@cyberport.com) */ + uinfo_t uinfo; + if (ia_openinfo(pass->pw_name, &uinfo) != -1) { + ia_get_logpwd(uinfo, &(pass->pw_passwd)); + } + } +#endif + +#ifdef HAVE_GETPRPWNAM + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pass->pw_passwd = pr_pw->ufld.fd_encrypt; + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", + user)); + mypasswd = getprpwnam (user); + if (mypasswd) { + fstrcpy(pass->pw_name,mypasswd->ufld.fd_name); + fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt); + } else { + DEBUG(5,("No entry for user %s in protected database !\n", + user)); + return(False); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid(pass->pw_uid); + if (ap) { + fstrcpy(pass->pw_passwd, ap->a_password); + endauthent(); + } + } +#endif + + /* extract relevant info */ + fstrcpy(this_user,pass->pw_name); + fstrcpy(this_salt,pass->pw_passwd); + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + this_salt[2] = 0; + + fstrcpy(this_crypted,pass->pw_passwd); + + if (!*this_crypted) { + if (!lp_null_passwords()) { + DEBUG(2,("Disallowing %s with null password\n", + this_user)); + return(False); + } + if (!*password) { + DEBUG(3,("Allowing access to %s with null password\n", + this_user)); + return(True); + } + } + + /* try it as it came to us */ + if (password_check(password)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + /* if the password was given to us with mixed case then we don't + need to proceed as we know it hasn't been case modified by the + client */ + if (strhasupper(password) && strhaslower(password)) { + return(False); + } + + /* make a copy of it */ + StrnCpy(pass2,password,sizeof(pstring)-1); + + /* try all lowercase */ + strlower(password); + if (password_check(password)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + /* give up? */ + if (level < 1) { + update_protected_database(user,False); + + /* restore it */ + fstrcpy(password,pass2); + + return(False); + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(password); + + if (string_combinations(password,password_check,level)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + update_protected_database(user,False); + + /* restore it */ + fstrcpy(password,pass2); + + return(False); +} diff --git a/source3/include/proto.h b/source3/include/proto.h index 558e17acabf..d9a35aa33f4 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -185,6 +185,13 @@ time_t get_create_time(struct stat *st,BOOL fake_dirs); char *ufc_crypt(char *key,char *salt); +/*The following definitions come from lib/username.c */ + +char *get_home_dir(char *user); +BOOL map_username(char *user); +struct passwd *Get_Pwnam(char *user,BOOL allow_change); +BOOL user_in_list(char *user,char *list); + /*The following definitions come from lib/util.c */ char *tmpdir(void); @@ -1067,6 +1074,12 @@ BOOL pm_process( char *FileName, BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname); void pcap_printer_fn(void (*fn)(char *, char *)); +/*The following definitions come from passdb/pass_check.c */ + +void dfs_unlogin(void); +BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd, + BOOL (*fn)(char *, char *)); + /*The following definitions come from passdb/passdb.c */ BOOL initialize_password_db(void); @@ -1134,13 +1147,6 @@ BOOL trust_password_delete( char *domain, char *name ); BOOL get_trust_account_password( unsigned char *ret_pwd, time_t *pass_last_set_time); BOOL set_trust_account_password( unsigned char *md4_new_pwd); -/*The following definitions come from passdb/username.c */ - -char *get_home_dir(char *user); -BOOL map_username(char *user); -struct passwd *Get_Pwnam(char *user,BOOL allow_change); -BOOL user_in_list(char *user,char *list); - /*The following definitions come from rpc_client/cli_login.c */ BOOL cli_nt_setup_creds(struct cli_state *cli, unsigned char mach_pwd[16]); @@ -1947,9 +1953,7 @@ char *validated_username(uint16 vuid); int setup_groups(char *user, int uid, int gid, int *p_ngroups, GID_T **p_groups); uint16 register_vuid(int uid,int gid, char *unix_name, char *requested_name, BOOL guest); void add_session_user(char *user); -BOOL update_smbpassword_file( char *user, fstring password); -void dfs_unlogin(void); -BOOL password_check(char *password); +BOOL update_smbpassword_file(char *user, char *password); BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8); BOOL smb_password_ok(struct smb_passwd *smb_pass, uchar lm_pass[24], uchar nt_pass[24]); diff --git a/source3/passdb/pass_check.c b/source3/passdb/pass_check.c new file mode 100644 index 00000000000..b5aa832f48b --- /dev/null +++ b/source3/passdb/pass_check.c @@ -0,0 +1,950 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password checking + Copyright (C) Andrew Tridgell 1992-1998 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this module is for checking a username/password against a system + password database. The SMB encrypted password support is elsewhere */ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/* these are kept here to keep the string_combinations function simple */ +static char this_user[100]=""; +static char this_salt[100]=""; +static char this_crypted[100]=""; + + +/**************************************************************************** +update the enhanced security database. Only relevant for OSF1 at the moment. +****************************************************************************/ +static void update_protected_database(char *user, BOOL result) +{ +#ifdef OSF1_ENH_SEC + struct pr_passwd *mypasswd; + time_t starttime; + + mypasswd = getprpwnam (user); + starttime = time (NULL); + + if (result) { + mypasswd->ufld.fd_slogin = starttime; + mypasswd->ufld.fd_nlogins = 0; + + putprpwnam(user,mypasswd); + } else { + mypasswd->ufld.fd_ulogin = starttime; + mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; + if (mypasswd->ufld.fd_max_tries != 0 && + mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries) { + mypasswd->uflg.fg_lock = 0; + DEBUG(3,("Account %s is disabled\n", user)); + } + putprpwnam(user ,mypasswd); + } +#endif +} + + +#ifdef HAVE_PAM +/******************************************************************* +check on PAM authentication +********************************************************************/ + +/* We first need some helper functions */ +#include +/* Static variables used to communicate between the conversation function + * and the server_login function + */ +static char *PAM_username; +static char *PAM_password; + +/* PAM conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ +static int PAM_conv (int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) { + int replies = 0; + struct pam_response *reply = NULL; + + #define COPY_STRING(s) (s) ? strdup(s) : NULL + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) return PAM_CONV_ERR; + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_username); + /* PAM frees resp */ + break; + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(PAM_password); + /* PAM frees resp */ + break; + case PAM_TEXT_INFO: + /* fall through */ + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + default: + /* Must be an error of some sort... */ + free (reply); + return PAM_CONV_ERR; + } + } + if (reply) *resp = reply; + return PAM_SUCCESS; +} +static struct pam_conv PAM_conversation = { + &PAM_conv, + NULL +}; + + +static BOOL pam_auth(char *this_user,char *password) +{ + pam_handle_t *pamh; + int pam_error; + + /* Now use PAM to do authentication. For now, we won't worry about + * session logging, only authentication. Bail out if there are any + * errors. Since this is a limited protocol, and an even more limited + * function within a server speaking this protocol, we can't be as + * verbose as would otherwise make sense. + * Query: should we be using PAM_SILENT to shut PAM up? + */ + #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \ + pam_end(pamh, 0); return False; \ + } + PAM_password = password; + PAM_username = this_user; + pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh); + PAM_BAIL; +/* Setting PAM_SILENT stops generation of error messages to syslog + * to enable debugging on Red Hat Linux set: + * /etc/pam.d/samba: + * auth required /lib/security/pam_pwdb.so nullok shadow audit + * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging) + */ + pam_error = pam_authenticate(pamh, PAM_SILENT); + PAM_BAIL; + /* It is not clear to me that account management is the right thing + * to do, but it is not clear that it isn't, either. This can be + * removed if no account management should be done. Alternately, + * put a pam_allow.so entry in /etc/pam.conf for account handling. */ + pam_error = pam_acct_mgmt(pamh, PAM_SILENT); + PAM_BAIL; + pam_end(pamh, PAM_SUCCESS); + /* If this point is reached, the user has been authenticated. */ + return(True); +} +#endif + + +#ifdef WITH_AFS +/******************************************************************* +check on AFS authentication +********************************************************************/ +static BOOL afs_auth(char *this_user,char *password) +{ + long password_expires = 0; + char *reason; + + /* For versions of AFS prior to 3.3, this routine has few arguments, */ + /* but since I can't find the old documentation... :-) */ + setpag(); + if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG, + this_user, + (char *) 0, /* instance */ + (char *) 0, /* cell */ + password, + 0, /* lifetime, default */ + &password_expires, /*days 'til it expires */ + 0, /* spare 2 */ + &reason) == 0) { + return(True); + } + return(False); +} +#endif + + +#ifdef WITH_DFS + +/***************************************************************** + This new version of the DFS_AUTH code was donated by Karsten Muuss + . It fixes the following problems with the + old code : + + - Server credentials may expire + - Client credential cache files have wrong owner + - purge_context() function is called with invalid argument + + This new code was modified to ensure that on exit the uid/gid is + still root, and the original directory is restored. JRA. +******************************************************************/ + +sec_login_handle_t my_dce_sec_context; +int dcelogin_atmost_once = 0; + +/******************************************************************* +check on a DCE/DFS authentication +********************************************************************/ +static BOOL dfs_auth(char *this_user,char *password) +{ + error_status_t err; + int err2; + int prterr; + signed32 expire_time, current_time; + boolean32 password_reset; + struct passwd *pw; + sec_passwd_rec_t passwd_rec; + sec_login_auth_src_t auth_src = sec_login_auth_src_network; + unsigned char dce_errstr[dce_c_error_string_len]; + + if (dcelogin_atmost_once) return(False); + +#ifdef HAVE_CRYPT + /* + * We only go for a DCE login context if the given password + * matches that stored in the local password file.. + * Assumes local passwd file is kept in sync w/ DCE RGY! + */ + + if (strcmp((char *)crypt(password,this_salt),this_crypted)) { + return(False); + } +#endif + + sec_login_get_current_context(&my_dce_sec_context, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); + + return(False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); + + return(False); + } + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); + + return(False); + } + + time(¤t_time); + + if (expire_time < (current_time + 60)) { + struct passwd *pw; + sec_passwd_rec_t *key; + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + sec_login_refresh_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't refresh identity. %s\n", + dce_errstr)); + + return(False); + } + + sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL, + (unsigned char *)pw->pw_name, + sec_c_key_version_none, + (void**)&key, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get key for %s. %s\n", + pw->pw_name, dce_errstr)); + + return(False); + } + + sec_login_valid_and_cert_ident(my_dce_sec_context, key, + &password_reset, &auth_src, + &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't validate and certify identity for %s. %s\n", + pw->pw_name, dce_errstr)); + } + + sec_key_mgmt_free_key(key, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't free key.\n", dce_errstr)); + } + } + + if (sec_login_setup_identity((unsigned char *)this_user, + sec_login_no_flags, + &my_dce_sec_context, + &err) == 0) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE Setup Identity for %s failed: %s\n", + this_user,dce_errstr)); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't purge context. %s\n", dce_errstr)); + + return(False); + } + + /* + * NB. I'd like to change these to call something like become_user() + * instead but currently we don't have a connection + * context to become the correct user. This is already + * fairly platform specific code however, so I think + * this should be ok. I have added code to go + * back to being root on error though. JRA. + */ + + if (setregid(-1, pw->pw_gid) != 0) { + DEBUG(0,("Can't set egid to %d (%s)\n", + pw->pw_gid, strerror(errno))); + return False; + } + + if (setreuid(-1, pw->pw_uid) != 0) { + setgid(0); + DEBUG(0,("Can't set euid to %d (%s)\n", + pw->pw_uid, strerror(errno))); + return False; + } + + if (sec_login_setup_identity((unsigned char *)this_user, + sec_login_no_flags, + &my_dce_sec_context, + &err) == 0) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE Setup Identity for %s failed: %s\n", + this_user,dce_errstr)); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + return(False); + } + + passwd_rec.version_number = sec_passwd_c_version_none; + passwd_rec.pepper = NULL; + passwd_rec.key.key_type = sec_passwd_plain; + passwd_rec.key.tagged_union.plain = (idl_char *)password; + + sec_login_validate_identity(my_dce_sec_context, + &passwd_rec, &password_reset, + &auth_src, &err); + if (err != error_status_ok ) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n", + this_user,dce_errstr)); + + return(False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr)); + + return(False); + } + + if (auth_src != sec_login_auth_src_network) { + DEBUG(0,("DCE context has no network credentials.\n")); + } + + sec_login_set_context(my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n", + this_user,dce_errstr)); + + sec_login_purge_context(&my_dce_sec_context, &err); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + return(False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t*)&pw, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); + + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + return(False); + } + + DEBUG(0,("DCE login succeeded for principal %s on pid %d\n", + this_user, getpid())); + + DEBUG(3,("DCE principal: %s\n" + " uid: %d\n" + " gid: %d\n", + pw->pw_name, pw->pw_uid, pw->pw_gid)); + DEBUG(3,(" info: %s\n" + " dir: %s\n" + " shell: %s\n", + pw->pw_gecos, pw->pw_dir, pw->pw_shell)); + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + /* Go back to root, JRA. */ + setuid(0); + setgid(0); + DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); + + return(False); + } + + setuid(0); + setgid(0); + + DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time)))); + + dcelogin_atmost_once = 1; + return (True); +} + +void dfs_unlogin(void) +{ + error_status_t err; + int err2; + unsigned char dce_errstr[dce_c_error_string_len]; + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE purge login context failed for server instance %d: %s\n", + getpid(), dce_errstr)); + } +} +#endif + +#ifdef KRB5_AUTH +/******************************************************************* +check on Kerberos authentication +********************************************************************/ +static BOOL krb5_auth(char *this_user,char *password) +{ + krb5_data tgtname = { + 0, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME + }; + krb5_context kcontext; + krb5_principal kprinc; + krb5_principal server; + krb5_creds kcreds; + int options = 0; + krb5_address **addrs = (krb5_address **)0; + krb5_preauthtype *preauth = NULL; + krb5_keytab keytab = NULL; + krb5_timestamp now; + krb5_ccache ccache = NULL; + int retval; + char *name; + + if (retval=krb5_init_context(&kcontext)) { + return(False); + } + + if (retval = krb5_timeofday(kcontext, &now)) { + return(False); + } + + if (retval = krb5_cc_default(kcontext, &ccache)) { + return(False); + } + + if (retval = krb5_parse_name(kcontext, this_user, &kprinc)) { + return(False); + } + + memset((char *)&kcreds, 0, sizeof(kcreds)); + + kcreds.client = kprinc; + + if ((retval = krb5_build_principal_ext(kcontext, &server, + krb5_princ_realm(kcontext, kprinc)->length, + krb5_princ_realm(kcontext, kprinc)->data, + tgtname.length, + tgtname.data, + krb5_princ_realm(kcontext, kprinc)->length, + krb5_princ_realm(kcontext, kprinc)->data, + 0))) { + return(False); + } + + kcreds.server = server; + + retval = krb5_get_in_tkt_with_password(kcontext, + options, + addrs, + NULL, + preauth, + password, + 0, + &kcreds, + 0); + + if (retval) { + return(False); + } + + return(True); +} +#endif /* KRB5_AUTH */ + +#ifdef KRB4_AUTH +/******************************************************************* +check on Kerberos authentication +********************************************************************/ +static BOOL krb4_auth(char *this_user,char *password) +{ + char realm[REALM_SZ]; + char tkfile[MAXPATHLEN]; + + if (krb_get_lrealm(realm, 1) != KSUCCESS) { + (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1); + } + + (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", + getpid()); + + krb_set_tkt_string(tkfile); + if (krb_verify_user(this_user, "", realm, + password, 0, + "rmcd") == KSUCCESS) { + unlink(tkfile); + return 1; + } + unlink(tkfile); + return 0; +} +#endif /* KRB4_AUTH */ + +#ifdef LINUX_BIGCRYPT +/**************************************************************************** +an enhanced crypt for Linux to handle password longer than 8 characters +****************************************************************************/ +static int linux_bigcrypt(char *password,char *salt1, char *crypted) +{ +#define LINUX_PASSWORD_SEG_CHARS 8 + char salt[3]; + int i; + + StrnCpy(salt,salt1,2); + crypted +=2; + + for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { + char * p = crypt(password,salt) + 2; + if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) + return(0); + password += LINUX_PASSWORD_SEG_CHARS; + crypted += strlen(p); + } + + return(1); +} +#endif + +#ifdef OSF1_ENH_SEC +/**************************************************************************** +an enhanced crypt for OSF1 +****************************************************************************/ +static char *osf1_bigcrypt(char *password,char *salt1) +{ + static char result[AUTH_MAX_PASSWD_LENGTH] = ""; + char *p1; + char *p2=password; + char salt[3]; + int i; + int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; + if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) { + parts++; + } + + StrnCpy(salt,salt1,2); + StrnCpy(result,salt1,2); + + for (i=0; i= len) { + return(fn(s)); + } + + for (i=offset;i<(len-(N-1));i++) { + char c = s[i]; + if (!islower(c)) continue; + s[i] = toupper(c); + if (string_combinations2(s,i+1,fn,N-1)) + return(True); + s[i] = c; + } + return(False); +} + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with up to N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N) +{ + int n; + for (n=1;n<=N;n++) + if (string_combinations2(s,0,fn,n)) return(True); + return(False); +} + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static BOOL password_check(char *password) +{ + +#ifdef HAVE_PAM + /* This falls through if the password check fails + - if HAVE_CRYPT is not defined this causes an error msg + saying Warning - no crypt available + - if HAVE_CRYPT is defined this is a potential security hole + as it may authenticate via the crypt call when PAM + settings say it should fail. + if (pam_auth(this_user,password)) return(True); + Hence we make a direct return to avoid a second chance!!! + */ + return (pam_auth(this_user,password)); +#endif + +#ifdef WITH_AFS + if (afs_auth(this_user,password)) return(True); +#endif + +#ifdef WITH_DFS + if (dfs_auth(this_user,password)) return(True); +#endif + +#ifdef KRB5_AUTH + if (krb5_auth(this_user,password)) return(True); +#endif + +#ifdef KRB4_AUTH + if (krb4_auth(this_user,password)) return(True); +#endif + +#ifdef OSF1_ENH_SEC + { + BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); + if(!ret) { + DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0); + } + return ret; + } +#endif + +#ifdef ULTRIX_AUTH + return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); +#endif + +#ifdef LINUX_BIGCRYPT + return(linux_bigcrypt(password,this_salt,this_crypted)); +#endif + +#ifdef HAVE_BIGCRYPT + return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0); +#endif + +#ifndef HAVE_CRYPT + DEBUG(1,("Warning - no crypt available\n")); + return(False); +#else + return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); +#endif +} + + + +/**************************************************************************** +check if a username/password is OK +the function pointer fn() points to a function to call when a successful +match is found and is used to update the encrypted password file +return True on correct match, False otherwise +****************************************************************************/ +BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd, + BOOL (*fn)(char *, char *)) +{ + pstring pass2; + int level = lp_passwordlevel(); + struct passwd *pass; + + if (password) password[pwlen] = 0; + +#if DEBUG_PASSWORD + DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password)); +#endif + + if (!password) { + return(False); + } + + if (((!*password) || (!pwlen)) && !lp_null_passwords()) { + return(False); + } + + if (pwd && !user) { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } else { + pass = Get_Pwnam(user,True); + } + + + DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen)); + + if (!pass) { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); + } + +#ifdef HAVE_GETSPNAM + { + struct spwd *spass; + + /* many shadow systems require you to be root to get + the password, in most cases this should already be + the case when this function is called, except + perhaps for IPC password changing requests */ + + spass = getspnam(pass->pw_name); + if (spass && spass->sp_pwdp) { + pass->pw_passwd = spass->sp_pwdp; + } + } +#elif defined(IA_UINFO) + { + /* Need to get password with SVR4.2's ia_ functions + instead of get{sp,pw}ent functions. Required by + UnixWare 2.x, tested on version + 2.1. (tangent@cyberport.com) */ + uinfo_t uinfo; + if (ia_openinfo(pass->pw_name, &uinfo) != -1) { + ia_get_logpwd(uinfo, &(pass->pw_passwd)); + } + } +#endif + +#ifdef HAVE_GETPRPWNAM + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pass->pw_passwd = pr_pw->ufld.fd_encrypt; + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", + user)); + mypasswd = getprpwnam (user); + if (mypasswd) { + fstrcpy(pass->pw_name,mypasswd->ufld.fd_name); + fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt); + } else { + DEBUG(5,("No entry for user %s in protected database !\n", + user)); + return(False); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid(pass->pw_uid); + if (ap) { + fstrcpy(pass->pw_passwd, ap->a_password); + endauthent(); + } + } +#endif + + /* extract relevant info */ + fstrcpy(this_user,pass->pw_name); + fstrcpy(this_salt,pass->pw_passwd); + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + this_salt[2] = 0; + + fstrcpy(this_crypted,pass->pw_passwd); + + if (!*this_crypted) { + if (!lp_null_passwords()) { + DEBUG(2,("Disallowing %s with null password\n", + this_user)); + return(False); + } + if (!*password) { + DEBUG(3,("Allowing access to %s with null password\n", + this_user)); + return(True); + } + } + + /* try it as it came to us */ + if (password_check(password)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + /* if the password was given to us with mixed case then we don't + need to proceed as we know it hasn't been case modified by the + client */ + if (strhasupper(password) && strhaslower(password)) { + return(False); + } + + /* make a copy of it */ + StrnCpy(pass2,password,sizeof(pstring)-1); + + /* try all lowercase */ + strlower(password); + if (password_check(password)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + /* give up? */ + if (level < 1) { + update_protected_database(user,False); + + /* restore it */ + fstrcpy(password,pass2); + + return(False); + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(password); + + if (string_combinations(password,password_check,level)) { + update_protected_database(user,True); + if (fn) fn(user,password); + return(True); + } + + update_protected_database(user,False); + + /* restore it */ + fstrcpy(password,pass2); + + return(False); +} diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 0f8e33940f5..055c53d009d 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -36,11 +36,6 @@ static pstring session_users=""; extern pstring global_myname; extern fstring global_myworkgroup; -/* these are kept here to keep the string_combinations function simple */ -static char this_user[100]=""; -static char this_salt[100]=""; -static char this_crypted[100]=""; - /* Data to do lanman1/2 password challenge. */ static unsigned char saved_challenge[8]; static BOOL challenge_sent=False; @@ -305,779 +300,36 @@ void add_session_user(char *user) } -#ifdef OSF1_ENH_SEC -/**************************************************************************** -an enhanced crypt for OSF1 -****************************************************************************/ -static char *osf1_bigcrypt(char *password,char *salt1) -{ - static char result[AUTH_MAX_PASSWD_LENGTH] = ""; - char *p1; - char *p2=password; - char salt[3]; - int i; - int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; - if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) - parts++; - - StrnCpy(salt,salt1,2); - StrnCpy(result,salt1,2); - - for (i=0; iufld.fd_slogin = starttime; - mypasswd->ufld.fd_nlogins = 0; - - putprpwnam(user,mypasswd); - - DEBUG(3,("Update protected database for Account %s after succesful connection\n",user)); - } - else - { - mypasswd->ufld.fd_ulogin = starttime; - mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; - if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries ) - { - mypasswd->uflg.fg_lock = 0; - DEBUG(3,("Account is disabled -- see Account Administrator.\n")); - } - putprpwnam ( user , mypasswd ); - DEBUG(3,("Update protected database for Account %s after refusing connection\n",user)); - } -#else - DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result))); -#endif -} - - -#ifdef HAVE_PAM -/******************************************************************* -check on PAM authentication -********************************************************************/ - -/* We first need some helper functions */ -#include -/* Static variables used to communicate between the conversation function - * and the server_login function - */ -static char *PAM_username; -static char *PAM_password; - -/* PAM conversation function - * Here we assume (for now, at least) that echo on means login name, and - * echo off means password. - */ -static int PAM_conv (int num_msg, - const struct pam_message **msg, - struct pam_response **resp, - void *appdata_ptr) { - int replies = 0; - struct pam_response *reply = NULL; - - #define COPY_STRING(s) (s) ? strdup(s) : NULL - - reply = malloc(sizeof(struct pam_response) * num_msg); - if (!reply) return PAM_CONV_ERR; - - for (replies = 0; replies < num_msg; replies++) { - switch (msg[replies]->msg_style) { - case PAM_PROMPT_ECHO_ON: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = COPY_STRING(PAM_username); - /* PAM frees resp */ - break; - case PAM_PROMPT_ECHO_OFF: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = COPY_STRING(PAM_password); - /* PAM frees resp */ - break; - case PAM_TEXT_INFO: - /* fall through */ - case PAM_ERROR_MSG: - /* ignore it... */ - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = NULL; - break; - default: - /* Must be an error of some sort... */ - free (reply); - return PAM_CONV_ERR; - } - } - if (reply) *resp = reply; - return PAM_SUCCESS; -} -static struct pam_conv PAM_conversation = { - &PAM_conv, - NULL -}; - - -static BOOL pam_auth(char *this_user,char *password) +BOOL update_smbpassword_file(char *user, char *password) { - pam_handle_t *pamh; - int pam_error; - - /* Now use PAM to do authentication. For now, we won't worry about - * session logging, only authentication. Bail out if there are any - * errors. Since this is a limited protocol, and an even more limited - * function within a server speaking this protocol, we can't be as - * verbose as would otherwise make sense. - * Query: should we be using PAM_SILENT to shut PAM up? - */ - #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \ - pam_end(pamh, 0); return False; \ - } - PAM_password = password; - PAM_username = this_user; - pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh); - PAM_BAIL; -/* Setting PAM_SILENT stops generation of error messages to syslog - * to enable debugging on Red Hat Linux set: - * /etc/pam.d/samba: - * auth required /lib/security/pam_pwdb.so nullok shadow audit - * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging) - */ - pam_error = pam_authenticate(pamh, PAM_SILENT); - PAM_BAIL; - /* It is not clear to me that account management is the right thing - * to do, but it is not clear that it isn't, either. This can be - * removed if no account management should be done. Alternately, - * put a pam_allow.so entry in /etc/pam.conf for account handling. */ - pam_error = pam_acct_mgmt(pamh, PAM_SILENT); - PAM_BAIL; - pam_end(pamh, PAM_SUCCESS); - /* If this point is reached, the user has been authenticated. */ - return(True); -} -#endif - - -#ifdef WITH_AFS -/******************************************************************* -check on AFS authentication -********************************************************************/ -static BOOL afs_auth(char *this_user,char *password) -{ - long password_expires = 0; - char *reason; - - /* For versions of AFS prior to 3.3, this routine has few arguments, */ - /* but since I can't find the old documentation... :-) */ - setpag(); - if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG, - this_user, - (char *) 0, /* instance */ - (char *) 0, /* cell */ - password, - 0, /* lifetime, default */ - &password_expires, /*days 'til it expires */ - 0, /* spare 2 */ - &reason) == 0) - return(True); - return(False); -} -#endif - - -#ifdef WITH_DFS - -/***************************************************************** - This new version of the DFS_AUTH code was donated by Karsten Muuss - . It fixes the following problems with the - old code : - - - Server credentials may expire - - Client credential cache files have wrong owner - - purge_context() function is called with invalid argument - - This new code was modified to ensure that on exit the uid/gid is - still root, and the original directory is restored. JRA. -******************************************************************/ - -sec_login_handle_t my_dce_sec_context; -int dcelogin_atmost_once = 0; - -/******************************************************************* -check on a DCE/DFS authentication -********************************************************************/ -static BOOL dfs_auth(char *this_user,char *password) -{ - error_status_t err; - int err2; - int prterr; - signed32 expire_time, current_time; - boolean32 password_reset; - struct passwd *pw; - sec_passwd_rec_t passwd_rec; - sec_login_auth_src_t auth_src = sec_login_auth_src_network; - unsigned char dce_errstr[dce_c_error_string_len]; - - if (dcelogin_atmost_once) return(False); - -#ifdef HAVE_CRYPT - /* - * We only go for a DCE login context if the given password - * matches that stored in the local password file.. - * Assumes local passwd file is kept in sync w/ DCE RGY! - */ - - if ( strcmp((char *)crypt(password,this_salt),this_crypted) ) - return(False); -#endif - - sec_login_get_current_context(&my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); - - return(False); - } - - sec_login_certify_identity(my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get current context. %s\n", dce_errstr)); - - return(False); - } - - sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); - - return(False); - } - - time(¤t_time); - - if (expire_time < (current_time + 60)) { - struct passwd *pw; - sec_passwd_rec_t *key; - - sec_login_get_pwent(my_dce_sec_context, - (sec_login_passwd_t*)&pw, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); - - return(False); - } - - sec_login_refresh_identity(my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't refresh identity. %s\n", dce_errstr)); - - return(False); - } - - sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL, - (unsigned char *)pw->pw_name, - sec_c_key_version_none, - (void**)&key, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get key for %s. %s\n", pw->pw_name, dce_errstr)); - - return(False); - } - - sec_login_valid_and_cert_ident(my_dce_sec_context, key, - &password_reset, &auth_src, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't validate and certify identity for %s. %s\n", - pw->pw_name, dce_errstr)); - } - - sec_key_mgmt_free_key(key, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't free key.\n", dce_errstr)); - } - } - - if (sec_login_setup_identity((unsigned char *)this_user, - sec_login_no_flags, - &my_dce_sec_context, - &err) == 0) - - { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE Setup Identity for %s failed: %s\n", - this_user,dce_errstr)); - return(False); - } - - sec_login_get_pwent(my_dce_sec_context, - (sec_login_passwd_t*)&pw, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); - - return(False); - } - - sec_login_purge_context(&my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't purge context. %s\n", dce_errstr)); - - return(False); - } - - /* - * NB. I'd like to change these to call something like become_user() - * instead but currently we don't have a connection - * context to become the correct user. This is already - * fairly platform specific code however, so I think - * this should be ok. I have added code to go - * back to being root on error though. JRA. - */ - - if (setregid(-1, pw->pw_gid) != 0) { - DEBUG(0,("Can't set egid to %d (%s)\n", pw->pw_gid, strerror(errno))); - return False; - } - - if (setreuid(-1, pw->pw_uid) != 0) { - setgid(0); - DEBUG(0,("Can't set euid to %d (%s)\n", pw->pw_uid, strerror(errno))); - return False; - } - - if (sec_login_setup_identity((unsigned char *)this_user, - sec_login_no_flags, - &my_dce_sec_context, - &err) == 0) - - { - dce_error_inq_text(err, dce_errstr, &err2); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - DEBUG(0,("DCE Setup Identity for %s failed: %s\n", - this_user,dce_errstr)); - return(False); - } - - sec_login_get_pwent(my_dce_sec_context, - (sec_login_passwd_t*)&pw, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); - - return(False); - } - - passwd_rec.version_number = sec_passwd_c_version_none; - passwd_rec.pepper = NULL; - passwd_rec.key.key_type = sec_passwd_plain; - passwd_rec.key.tagged_union.plain = (idl_char *)password; - - sec_login_validate_identity(my_dce_sec_context, - &passwd_rec, &password_reset, - &auth_src, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n", - this_user,dce_errstr)); - - return(False); - } - - sec_login_certify_identity(my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr)); - - return(False); - } - - if (auth_src != sec_login_auth_src_network) { - DEBUG(0,("DCE context has no network credentials.\n")); - } - - sec_login_set_context(my_dce_sec_context, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n", - this_user,dce_errstr)); - - sec_login_purge_context(&my_dce_sec_context, &err); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - return(False); - } - - sec_login_get_pwent(my_dce_sec_context, - (sec_login_passwd_t*)&pw, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr)); - - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - return(False); - } - - DEBUG(0,("DCE login succeeded for principal %s on pid %d\n", - this_user, getpid())); - - DEBUG(3,("DCE principal: %s\n" - " uid: %d\n" - " gid: %d\n", - pw->pw_name, pw->pw_uid, pw->pw_gid)); - DEBUG(3,(" info: %s\n" - " dir: %s\n" - " shell: %s\n", - pw->pw_gecos, pw->pw_dir, pw->pw_shell)); - - sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); - if (err != error_status_ok ) { - dce_error_inq_text(err, dce_errstr, &err2); - /* Go back to root, JRA. */ - setuid(0); - setgid(0); - DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr)); - - return(False); - } - - setuid(0); - setgid(0); - - DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time)))); - - dcelogin_atmost_once = 1; - return (True); -} - -void dfs_unlogin(void) -{ - error_status_t err; - int err2; - unsigned char dce_errstr[dce_c_error_string_len]; - - sec_login_purge_context(&my_dce_sec_context, &err); - if (err != error_status_ok ) - { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE purge login context failed for server instance %d: %s\n", - getpid(), dce_errstr)); - } -} -#endif - -#ifdef KRB5_AUTH -/******************************************************************* -check on Kerberos authentication -********************************************************************/ -static BOOL krb5_auth(char *this_user,char *password) -{ - krb5_data tgtname = { - 0, - KRB5_TGS_NAME_SIZE, - KRB5_TGS_NAME - }; - krb5_context kcontext; - krb5_principal kprinc; - krb5_principal server; - krb5_creds kcreds; - int options = 0; - krb5_address **addrs = (krb5_address **)0; - krb5_preauthtype *preauth = NULL; - krb5_keytab keytab = NULL; - krb5_timestamp now; - krb5_ccache ccache = NULL; - int retval; - char *name; - - if ( retval=krb5_init_context(&kcontext)) - { - return(False); - } - - if ( retval = krb5_timeofday(kcontext, &now) ) - { - return(False); - } - - if ( retval = krb5_cc_default(kcontext, &ccache) ) - { - return(False); - } + struct smb_passwd *smbpw; + BOOL ret; - if ( retval = krb5_parse_name(kcontext, this_user, &kprinc) ) - { - return(False); - } - - memset((char *)&kcreds, 0, sizeof(kcreds)); - - kcreds.client = kprinc; + become_root(0); + smbpw = getsmbpwnam(user); + unbecome_root(0); - if ((retval = krb5_build_principal_ext(kcontext, &server, - krb5_princ_realm(kcontext, kprinc)->length, - krb5_princ_realm(kcontext, kprinc)->data, - tgtname.length, - tgtname.data, - krb5_princ_realm(kcontext, kprinc)->length, - krb5_princ_realm(kcontext, kprinc)->data, - 0))) - { - return(False); + if(smbpw == NULL) { + DEBUG(0,("getsmbpwnam returned NULL\n")); + return False; } - - kcreds.server = server; - - retval = krb5_get_in_tkt_with_password(kcontext, - options, - addrs, - NULL, - preauth, - password, - 0, - &kcreds, - 0); - - if ( retval ) - { - return(False); + + /* Here, the flag is one, because we want to ignore the + XXXXXXX'd out password */ + ret = change_oem_password( smbpw, password, True); + if (ret == False) { + DEBUG(3,("change_oem_password returned False\n")); } - return(True); -} -#endif /* KRB5_AUTH */ - -#ifdef KRB4_AUTH -/******************************************************************* -check on Kerberos authentication -********************************************************************/ -static BOOL krb4_auth(char *this_user,char *password) -{ - char realm[REALM_SZ]; - char tkfile[MAXPATHLEN]; - - if (krb_get_lrealm(realm, 1) != KSUCCESS) - (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1); - - (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", getpid()); - - krb_set_tkt_string(tkfile); - if (krb_verify_user(this_user, "", realm, - password, 0, - "rmcd") == KSUCCESS) { - unlink(tkfile); - return 1; - } - unlink(tkfile); - return 0; -} -#endif /* KRB4_AUTH */ - -#ifdef LINUX_BIGCRYPT -/**************************************************************************** -an enhanced crypt for Linux to handle password longer than 8 characters -****************************************************************************/ -static int linux_bigcrypt(char *password,char *salt1, char *crypted) -{ -#define LINUX_PASSWORD_SEG_CHARS 8 - char salt[3]; - int i; - - StrnCpy(salt,salt1,2); - crypted +=2; - - for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { - char * p = crypt(password,salt) + 2; - if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) - return(0); - password += LINUX_PASSWORD_SEG_CHARS; - crypted += strlen(p); - } - - return(1); + return ret; } -#endif - - -/**************************************************************************** -apply a function to upper/lower case combinations -of a string and return true if one of them returns true. -try all combinations with N uppercase letters. -offset is the first char to try and change (start with 0) -it assumes the string starts lowercased -****************************************************************************/ -static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N) -{ - int len = strlen(s); - int i; - -#ifdef PASSWORD_LENGTH - len = MIN(len,PASSWORD_LENGTH); -#endif - - if (N <= 0 || offset >= len) - return(fn(s)); - - for (i=offset;i<(len-(N-1));i++) - { - char c = s[i]; - if (!islower(c)) continue; - s[i] = toupper(c); - if (string_combinations2(s,i+1,fn,N-1)) - return(True); - s[i] = c; - } - return(False); -} - -/**************************************************************************** -apply a function to upper/lower case combinations -of a string and return true if one of them returns true. -try all combinations with up to N uppercase letters. -offset is the first char to try and change (start with 0) -it assumes the string starts lowercased -****************************************************************************/ -static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N) -{ - int n; - for (n=1;n<=N;n++) - if (string_combinations2(s,0,fn,n)) return(True); - return(False); -} - - - -/**************************************************************************** -core of password checking routine -****************************************************************************/ -BOOL password_check(char *password) -{ - -#ifdef HAVE_PAM -/* This falls through if the password check fails - - if HAVE_CRYPT is not defined this causes an error msg - saying Warning - no crypt available - - if HAVE_CRYPT is defined this is a potential security hole - as it may authenticate via the crypt call when PAM - settings say it should fail. - if (pam_auth(this_user,password)) return(True); - Hence we make a direct return to avoid a second chance!!! -*/ - return (pam_auth(this_user,password)); -#endif - -#ifdef WITH_AFS - if (afs_auth(this_user,password)) return(True); -#endif -#ifdef WITH_DFS - if (dfs_auth(this_user,password)) return(True); -#endif -#ifdef KRB5_AUTH - if (krb5_auth(this_user,password)) return(True); -#endif -#ifdef KRB4_AUTH - if (krb4_auth(this_user,password)) return(True); -#endif -#ifdef OSF1_ENH_SEC - { - BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); - if(!ret) { - DEBUG(2,("password_check: OSF1_ENH_SEC failed. Trying normal crypt.\n")); - ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0); - } - return ret; - } -#endif - -#ifdef ULTRIX_AUTH - return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); -#endif - -#ifdef LINUX_BIGCRYPT - return(linux_bigcrypt(password,this_salt,this_crypted)); -#endif - -#ifdef HAVE_BIGCRYPT - return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0); -#endif - -#ifndef HAVE_CRYPT - DEBUG(1,("Warning - no crypt available\n")); - return(False); -#else - return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); -#endif -} /**************************************************************************** core of smb password checking routine. @@ -1125,327 +377,175 @@ BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned cha Do a specific test for an smb password being correct, given a smb_password and the lanman and NT responses. ****************************************************************************/ - BOOL smb_password_ok(struct smb_passwd *smb_pass, uchar lm_pass[24], uchar nt_pass[24]) { - uchar challenge[8]; + uchar challenge[8]; - if (!lm_pass || !smb_pass) return(False); + if (!lm_pass || !smb_pass) return(False); - if(smb_pass->acct_ctrl & ACB_DISABLED) - { - DEBUG(3,("smb_password_ok: account for user %s was disabled.\n", smb_pass->smb_name)); - return(False); - } - - if (!last_challenge(challenge)) - { - DEBUG(1,("smb_password_ok: no challenge done - password failed\n")); - return False; - } + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(3,("account for user %s was disabled.\n", + smb_pass->smb_name)); + return(False); + } - DEBUG(4,("smb_password_ok: Checking SMB password for user %s\n", smb_pass->smb_name)); + if (!last_challenge(challenge)) { + DEBUG(1,("no challenge done - password failed\n")); + return False; + } - if ((Protocol >= PROTOCOL_NT1) && (smb_pass->smb_nt_passwd != NULL)) - { - /* We have the NT MD4 hash challenge available - see if we can - use it (ie. does it exist in the smbpasswd file). - */ - DEBUG(4,("smb_password_ok: Checking NT MD4 password\n")); - if (smb_password_check((char *)nt_pass, (uchar *)smb_pass->smb_nt_passwd, challenge)) - { - DEBUG(4,("smb_password_ok: NT MD4 password check succeeded\n")); - return(True); - } - DEBUG(4,("smb_password_ok: NT MD4 password check failed\n")); - } + DEBUG(4,("Checking SMB password for user %s\n", + smb_pass->smb_name)); + + if ((Protocol >= PROTOCOL_NT1) && (smb_pass->smb_nt_passwd != NULL)) { + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + DEBUG(4,("smb_password_ok: Checking NT MD4 password\n")); + if (smb_password_check((char *)nt_pass, + (uchar *)smb_pass->smb_nt_passwd, + challenge)) { + DEBUG(4,("NT MD4 password check succeeded\n")); + return(True); + } + DEBUG(4,("NT MD4 password check failed\n")); + } - /* Try against the lanman password. smb_pass->smb_passwd == NULL means - no password, allow access. */ + /* Try against the lanman password. smb_pass->smb_passwd == NULL means + no password, allow access. */ - DEBUG(4,("Checking LM MD4 password\n")); + DEBUG(4,("Checking LM MD4 password\n")); - if((smb_pass->smb_passwd == NULL) && (smb_pass->acct_ctrl & ACB_PWNOTREQ)) - { - DEBUG(4,("smb_password_ok: no password required for user %s\n", smb_pass->smb_name)); - return True; - } + if((smb_pass->smb_passwd == NULL) && + (smb_pass->acct_ctrl & ACB_PWNOTREQ)) { + DEBUG(4,("no password required for user %s\n", + smb_pass->smb_name)); + return True; + } - if((smb_pass->smb_passwd != NULL) && smb_password_check((char *)lm_pass, (uchar *)smb_pass->smb_passwd, challenge)) - { - DEBUG(4,("smb_password_ok: LM MD4 password check succeeded\n")); - return(True); - } + if((smb_pass->smb_passwd != NULL) && + smb_password_check((char *)lm_pass, + (uchar *)smb_pass->smb_passwd, challenge)) { + DEBUG(4,("LM MD4 password check succeeded\n")); + return(True); + } - DEBUG(4,("smb_password_ok: LM MD4 password check failed\n")); + DEBUG(4,("LM MD4 password check failed\n")); - return False; + return False; } + /**************************************************************************** -check if a username/password is OK +check if a username/password is OK assuming the password is a 24 byte +SMB hash +return True if the password is correct, False otherwise ****************************************************************************/ -BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd) +static BOOL pass_check_smb(char *user,char *password, struct passwd *pwd) { - pstring pass2; - int level = lp_passwordlevel(); - struct passwd *pass; - uchar challenge[8]; - struct smb_passwd *smb_pass; - BOOL update_encrypted = lp_update_encrypted(); - BOOL challenge_done = False; + struct passwd *pass; + uchar challenge[8]; + struct smb_passwd *smb_pass; + BOOL challenge_done; - if (password) password[pwlen] = 0; - - if (pwlen == 24) - challenge_done = last_challenge(challenge); - -#if DEBUG_PASSWORD - if (challenge_done) - { - int i; - DEBUG(100,("checking user=[%s] pass=[",user)); - for( i = 0; i < 24; i++) - DEBUG(100,("%0x ", (unsigned char)password[i])); - DEBUG(100,("]\n")); - } else { - DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password)); - } -#endif - - if (!password) - return(False); - - if (((!*password) || (!pwlen)) && !lp_null_passwords()) - return(False); - - if (pwd && !user) - { - pass = (struct passwd *) pwd; - user = pass->pw_name; - } - else - pass = Get_Pwnam(user,True); - - DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done)); + if (!password) { + return(False); + } - if ((pwlen == 24) && challenge_done) - { - DEBUG(4,("Checking SMB password for user %s (l=24)\n",user)); + challenge_done = last_challenge(challenge); - if (!pass) - { - DEBUG(3,("Couldn't find user %s\n",user)); - return(False); + if (!challenge_done) { + DEBUG(0,("Error: challenge not done for user=%s\n", user)); + return False; } - smb_pass = getsmbpwnam(user); + if (pwd && !user) { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } else { + pass = Get_Pwnam(user,True); + } - if (!smb_pass) - { - DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); - return(False); + if (!pass) { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); } - /* Quit if the account was disabled. */ - if(smb_pass->acct_ctrl & ACB_DISABLED) - { - DEBUG(3,("password_ok: account for user %s was disabled.\n", user)); - return(False); - } + smb_pass = getsmbpwnam(user); - /* Ensure the uid's match */ - if (smb_pass->smb_userid != pass->pw_uid) - { - DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n")); - return(False); + if (!smb_pass) { + DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); + return(False); } - if(smb_password_ok( smb_pass, (unsigned char *)password,(uchar *)password)) - { - update_protected_database(user,True); - return(True); + /* Quit if the account was disabled. */ + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(3,("account for user %s was disabled.\n", user)); + return(False); } - DEBUG(3,("Error smb_password_check failed\n")); - } - - DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen)); - - if (!pass) - { - DEBUG(3,("Couldn't find user %s\n",user)); - return(False); - } - -#ifdef HAVE_GETSPNAM - { - struct spwd *spass; - - /* many shadow systems require you to be root to get the password, - in most cases this should already be the case when this - function is called, except perhaps for IPC password changing - requests */ - - spass = getspnam(pass->pw_name); - if (spass && spass->sp_pwdp) - pass->pw_passwd = spass->sp_pwdp; - } -#elif defined(IA_UINFO) - { - /* Need to get password with SVR4.2's ia_ functions instead of - get{sp,pw}ent functions. Required by UnixWare 2.x, tested on - version 2.1. (tangent@cyberport.com) */ - uinfo_t uinfo; - if (ia_openinfo(pass->pw_name, &uinfo) != -1) - ia_get_logpwd(uinfo, &(pass->pw_passwd)); - } -#endif - -#ifdef HAVE_GETPRPWNAM - { - struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); - if (pr_pw && pr_pw->ufld.fd_encrypt) - pass->pw_passwd = pr_pw->ufld.fd_encrypt; - } -#endif - -#ifdef OSF1_ENH_SEC - { - struct pr_passwd *mypasswd; - DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",user)); - mypasswd = getprpwnam (user); - if ( mypasswd ) - { - fstrcpy(pass->pw_name,mypasswd->ufld.fd_name); - fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt); - } - else - { - DEBUG(5,("No entry for user %s in protected database !\n",user)); - return(False); - } - } -#endif - -#ifdef ULTRIX_AUTH - { - AUTHORIZATION *ap = getauthuid( pass->pw_uid ); - if (ap) - { - fstrcpy( pass->pw_passwd, ap->a_password ); - endauthent(); - } - } -#endif - - /* extract relevant info */ - fstrcpy(this_user,pass->pw_name); - fstrcpy(this_salt,pass->pw_passwd); - /* crypt on some platforms (HPUX in particular) - won't work with more than 2 salt characters. */ - this_salt[2] = 0; - - fstrcpy(this_crypted,pass->pw_passwd); - - if (!*this_crypted) { - if (!lp_null_passwords()) { - DEBUG(2,("Disallowing access to %s due to null password\n",this_user)); - return(False); - } - if (!*password) { - DEBUG(3,("Allowing access to %s with null password\n",this_user)); - return(True); - } - } - - /* try it as it came to us */ - if (password_check(password)) - { - update_protected_database(user,True); - if (update_encrypted) - update_smbpassword_file(user,password); - return(True); - } - - /* if the password was given to us with mixed case then we don't - need to proceed as we know it hasn't been case modified by the - client */ - if (strhasupper(password) && strhaslower(password)) - return(False); - - /* make a copy of it */ - StrnCpy(pass2,password,sizeof(pstring)-1); - - /* try all lowercase */ - strlower(password); - if (password_check(password)) - { - update_protected_database(user,True); - if (update_encrypted) - update_smbpassword_file(user,password); - return(True); - } - - /* give up? */ - if (level < 1) - { - update_protected_database(user,False); - - /* restore it */ - fstrcpy(password,pass2); - - return(False); - } - - /* last chance - all combinations of up to level chars upper! */ - strlower(password); - - if (string_combinations(password,password_check,level)) - { - update_protected_database(user,True); - if (update_encrypted) - update_smbpassword_file(user,password); - return(True); - } + /* Ensure the uid's match */ + if (smb_pass->smb_userid != pass->pw_uid) { + DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n")); + return(False); + } - update_protected_database(user,False); - - /* restore it */ - fstrcpy(password,pass2); - - return(False); + if (smb_password_ok(smb_pass, + (unsigned char *)password, + (uchar *)password)) { + return(True); + } + + DEBUG(3,("Error smb_password_check failed\n")); + return False; } - +/**************************************************************************** +check if a username/password pair is OK either via the system password +database or the encrypted SMB password database +return True if the password is correct, False otherwise +****************************************************************************/ +BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd) +{ + if (pwlen == 24) { + /* if it is 24 bytes long then assume it is an encrypted + password */ + return pass_check_smb(user, password, pwd); + } + + return pass_check(user, password, pwlen, pwd, + lp_update_encrypted() ? + update_smbpassword_file : NULL); +} /**************************************************************************** check if a username is valid ****************************************************************************/ BOOL user_ok(char *user,int snum) { - pstring valid, invalid; - BOOL ret; - - StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)); - StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)); + pstring valid, invalid; + BOOL ret; - string_sub(valid,"%S",lp_servicename(snum)); - string_sub(invalid,"%S",lp_servicename(snum)); + StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)); + StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)); - ret = !user_in_list(user,invalid); - - if (ret && valid && *valid) - ret = user_in_list(user,valid); + string_sub(valid,"%S",lp_servicename(snum)); + string_sub(invalid,"%S",lp_servicename(snum)); + + ret = !user_in_list(user,invalid); + + if (ret && valid && *valid) { + ret = user_in_list(user,valid); + } - if (ret && lp_onlyuser(snum)) { - char *user_list = lp_username(snum); - string_sub(user_list,"%S",lp_servicename(snum)); - ret = user_in_list(user,user_list); - } + if (ret && lp_onlyuser(snum)) { + char *user_list = lp_username(snum); + string_sub(user_list,"%S",lp_servicename(snum)); + ret = user_in_list(user,user_list); + } - return(ret); + return(ret); } diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 97c54cde047..8f9349f3fc5 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -4426,8 +4426,9 @@ void exit_server(char *reason) if (Connections[i].open) close_cnum(i,(uint16)-1); #ifdef WITH_DFS - if (dcelogin_atmost_once) + if (dcelogin_atmost_once) { dfs_unlogin(); + } #endif if (!reason) { int oldlevel = DEBUGLEVEL; diff --git a/source3/web/cgi.c b/source3/web/cgi.c index 5958b0a4195..1a9d34d0047 100644 --- a/source3/web/cgi.c +++ b/source3/web/cgi.c @@ -483,7 +483,7 @@ static int cgi_handle_authorization(char *line) } - return password_ok(user, pass, strlen(pass), NULL); + return pass_check(user, pass, strlen(pass), NULL, NULL); } -- 2.11.4.GIT