1998-08-10 11:04:53 +04:00
/*
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 0213 9 , 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 ] = " " ;
# ifdef HAVE_PAM
/*******************************************************************
check on PAM authentication
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* We first need some helper functions */
# include <security/pam_appl.h>
/* 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
} ;
1998-08-15 07:31:57 +04:00
static BOOL pam_auth ( char * user , char * password )
1998-08-10 11:04:53 +04:00
{
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 ;
1998-08-15 07:31:57 +04:00
PAM_username = user ;
pam_error = pam_start ( " samba " , user , & PAM_conversation , & pamh ) ;
1998-08-10 11:04:53 +04:00
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-08-15 07:31:57 +04:00
static BOOL afs_auth ( char * user , char * password )
1998-08-10 11:04:53 +04:00
{
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 ,
1998-08-15 07:31:57 +04:00
user ,
1998-08-10 11:04:53 +04:00
( 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
< muuss @ or . uni - bonn . de > . 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-08-15 07:31:57 +04:00
static BOOL dfs_auth ( char * user , char * password )
1998-08-10 11:04:53 +04:00
{
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 ( & current_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 ) ) ;
}
}
1998-08-15 07:31:57 +04:00
if ( sec_login_setup_identity ( ( unsigned char * ) user ,
1998-08-10 11:04:53 +04:00
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 " ,
1998-08-15 07:31:57 +04:00
user , dce_errstr ) ) ;
1998-08-10 11:04:53 +04:00
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 ;
}
1998-08-15 07:31:57 +04:00
if ( sec_login_setup_identity ( ( unsigned char * ) user ,
1998-08-10 11:04:53 +04:00
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 " ,
1998-08-15 07:31:57 +04:00
user , dce_errstr ) ) ;
1998-08-10 11:04:53 +04:00
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 " ,
1998-08-15 07:31:57 +04:00
user , dce_errstr ) ) ;
1998-08-10 11:04:53 +04:00
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 " ,
1998-08-15 07:31:57 +04:00
user , dce_errstr ) ) ;
1998-08-10 11:04:53 +04:00
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 " ,
1998-08-15 07:31:57 +04:00
user , getpid ( ) ) ) ;
1998-08-10 11:04:53 +04:00
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-08-15 07:31:57 +04:00
static BOOL krb5_auth ( char * user , char * password )
1998-08-10 11:04:53 +04:00
{
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 ) ;
}
1998-08-15 07:31:57 +04:00
if ( retval = krb5_parse_name ( kcontext , user , & kprinc ) ) {
1998-08-10 11:04:53 +04:00
return ( False ) ;
}
1998-09-05 17:24:20 +04:00
ZERO_STRUCT ( kcreds ) ;
1998-08-10 11:04:53 +04:00
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
1998-09-26 07:30:15 +04:00
# include <krb.h>
1998-08-10 11:04:53 +04:00
/*******************************************************************
check on Kerberos authentication
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-08-15 07:31:57 +04:00
static BOOL krb4_auth ( char * user , char * password )
1998-08-10 11:04:53 +04:00
{
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 " ,
1998-09-26 07:30:15 +04:00
( int ) getpid ( ) ) ;
1998-08-10 11:04:53 +04:00
krb_set_tkt_string ( tkfile ) ;
1998-08-15 07:31:57 +04:00
if ( krb_verify_user ( user , " " , realm ,
1998-08-10 11:04:53 +04:00
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 < parts ; i + + ) {
p1 = crypt ( p2 , salt ) ;
strncat ( result , p1 + 2 , AUTH_MAX_PASSWD_LENGTH - strlen ( p1 + 2 ) - 1 ) ;
StrnCpy ( salt , & result [ 2 + i * AUTH_CIPHERTEXT_SEG_CHARS ] , 2 ) ;
p2 + = AUTH_CLEARTEXT_SEG_CHARS ;
}
return ( result ) ;
}
# 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 .
1998-08-15 07:31:57 +04:00
if ( pam_auth ( user , password ) ) return ( True ) ;
1998-08-10 11:04:53 +04:00
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 ( ) ;
1999-06-13 08:14:24 +04:00
const struct passwd * pass ;
1998-08-10 11:04:53 +04:00
if ( password ) password [ pwlen ] = 0 ;
# if DEBUG_PASSWORD
1999-03-08 21:43:50 +03:00
DEBUG ( 100 , ( " checking user=[%s] pass= " , user ) ) ;
dump_data ( 100 , password , strlen ( password ) ) ;
1998-08-10 11:04:53 +04:00
# 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 ) ;
}
/* 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 ) ) {
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 ) ) {
if ( fn ) fn ( user , password ) ;
return ( True ) ;
}
/* give up? */
if ( level < 1 ) {
/* 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 ) ) {
if ( fn ) fn ( user , password ) ;
return ( True ) ;
}
/* restore it */
fstrcpy ( password , pass2 ) ;
return ( False ) ;
}