2001-04-25 00:00:12 +04:00
/* Unix NT password database implementation, version 0.6.
*
* 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 .
*/
# include "includes.h"
# include "general.h"
# include "support.h"
# define _pam_overwrite(x) \
do { \
register char * __xx__ ; \
if ( ( __xx__ = ( x ) ) ) \
while ( * __xx__ ) \
* __xx__ + + = ' \0 ' ; \
} while ( 0 )
/*
* Don ' t just free it , forget it too .
*/
# define _pam_drop(X) \
do { \
if ( X ) { \
free ( X ) ; \
X = NULL ; \
} \
} while ( 0 )
# define _pam_drop_reply( /* struct pam_response * */ reply, /* int */ replies) \
do { \
int reply_i ; \
\
for ( reply_i = 0 ; reply_i < replies ; + + reply_i ) { \
if ( reply [ reply_i ] . resp ) { \
_pam_overwrite ( reply [ reply_i ] . resp ) ; \
free ( reply [ reply_i ] . resp ) ; \
} \
} \
if ( reply ) \
free ( reply ) ; \
} while ( 0 )
int converse ( pam_handle_t * , int , int , struct pam_message * * ,
struct pam_response * * ) ;
int make_remark ( pam_handle_t * , unsigned int , int , const char * ) ;
void _cleanup ( pam_handle_t * , void * , int ) ;
char * _pam_delete ( register char * ) ;
/* syslogging function for errors and other information */
void _log_err ( int err , const char * format , . . . )
{
va_list args ;
va_start ( args , format ) ;
openlog ( " PAM_smbpass " , LOG_CONS | LOG_PID , LOG_AUTH ) ;
vsyslog ( err , format , args ) ;
va_end ( args ) ;
closelog ( ) ;
}
/* this is a front-end for module-application conversations */
int converse ( pam_handle_t * pamh , int ctrl , int nargs
, struct pam_message * * message
, struct pam_response * * response )
{
int retval ;
struct pam_conv * conv ;
retval = pam_get_item ( pamh , PAM_CONV , ( const void * * ) & conv ) ;
if ( retval = = PAM_SUCCESS ) {
retval = conv - > conv ( nargs , ( const struct pam_message * * ) message
, response , conv - > appdata_ptr ) ;
if ( retval ! = PAM_SUCCESS & & on ( SMB_DEBUG , ctrl ) ) {
_log_err ( LOG_DEBUG , " conversation failure [%s] "
, pam_strerror ( pamh , retval ) ) ;
}
} else {
_log_err ( LOG_ERR , " couldn't obtain coversation function [%s] "
, pam_strerror ( pamh , retval ) ) ;
}
return retval ; /* propagate error status */
}
int make_remark ( pam_handle_t * pamh , unsigned int ctrl
, int type , const char * text )
{
if ( off ( SMB__QUIET , ctrl ) ) {
struct pam_message * pmsg [ 1 ] , msg [ 1 ] ;
struct pam_response * resp ;
pmsg [ 0 ] = & msg [ 0 ] ;
msg [ 0 ] . msg = text ;
msg [ 0 ] . msg_style = type ;
resp = NULL ;
return converse ( pamh , ctrl , 1 , pmsg , & resp ) ;
}
return PAM_SUCCESS ;
}
/* set the control flags for the SMB module. */
int set_ctrl ( int flags , int argc , const char * * argv )
{
int i = 0 ;
2001-12-12 02:44:33 +03:00
const char * service_file = dyn_CONFIGFILE ;
2001-04-25 00:00:12 +04:00
unsigned int ctrl ;
ctrl = SMB_DEFAULTS ; /* the default selection of options */
/* set some flags manually */
/* A good, sane default (matches Samba's behavior). */
set ( SMB__NONULL , ctrl ) ;
if ( flags & PAM_SILENT ) {
set ( SMB__QUIET , ctrl ) ;
}
/* Run through the arguments once, looking for an alternate smb config
file location */
while ( i < argc ) {
int j ;
for ( j = 0 ; j < SMB_CTRLS_ ; + + j ) {
if ( smb_args [ j ] . token
& & ! strncmp ( argv [ i ] , smb_args [ j ] . token , strlen ( smb_args [ j ] . token ) ) )
{
break ;
}
}
if ( j = = SMB_CONF_FILE ) {
service_file = argv [ i ] + 8 ;
}
i + + ;
}
/* Read some options from the Samba config. Can be overridden by
the PAM config . */
if ( lp_load ( service_file , True , False , False ) = = False ) {
_log_err ( LOG_ERR , " Error loading service file %s " , service_file ) ;
}
if ( lp_null_passwords ( ) ) {
set ( SMB__NULLOK , ctrl ) ;
}
/* now parse the rest of the arguments to this module */
while ( argc - - > 0 ) {
int j ;
for ( j = 0 ; j < SMB_CTRLS_ ; + + j ) {
if ( smb_args [ j ] . token
& & ! strncmp ( * argv , smb_args [ j ] . token , strlen ( smb_args [ j ] . token ) ) )
{
break ;
}
}
if ( j > = SMB_CTRLS_ ) {
_log_err ( LOG_ERR , " unrecognized option [%s] " , * argv ) ;
} else {
ctrl & = smb_args [ j ] . mask ; /* for turning things off */
ctrl | = smb_args [ j ] . flag ; /* for turning things on */
}
+ + argv ; /* step to next argument */
}
/* auditing is a more sensitive version of debug */
if ( on ( SMB_AUDIT , ctrl ) ) {
set ( SMB_DEBUG , ctrl ) ;
}
/* return the set of flags */
return ctrl ;
}
/* use this to free strings. ESPECIALLY password strings */
char * _pam_delete ( register char * xx )
{
_pam_overwrite ( xx ) ;
_pam_drop ( xx ) ;
return NULL ;
}
void _cleanup ( pam_handle_t * pamh , void * x , int error_status )
{
x = _pam_delete ( ( char * ) x ) ;
}
/* ************************************************************** *
* Useful non - trivial functions *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void _cleanup_failures ( pam_handle_t * pamh , void * fl , int err )
{
int quiet ;
const char * service = NULL ;
struct _pam_failed_auth * failure ;
# ifdef PAM_DATA_SILENT
quiet = err & PAM_DATA_SILENT ; /* should we log something? */
# else
quiet = 0 ;
# endif
# ifdef PAM_DATA_REPLACE
err & = PAM_DATA_REPLACE ; /* are we just replacing data? */
# endif
failure = ( struct _pam_failed_auth * ) fl ;
if ( failure ! = NULL ) {
# ifdef PAM_DATA_SILENT
if ( ! quiet & & ! err ) { /* under advisement from Sun,may go away */
# else
if ( ! quiet ) { /* under advisement from Sun,may go away */
# endif
/* log the number of authentication failures */
if ( failure - > count ! = 0 ) {
pam_get_item ( pamh , PAM_SERVICE , ( const void * * ) & service ) ;
_log_err ( LOG_NOTICE
, " %d authentication %s "
" from %s for service %s as %s(%d) "
, failure - > count
, failure - > count = = 1 ? " failure " : " failures "
, failure - > agent
, service = = NULL ? " **unknown** " : service
, failure - > user , failure - > id ) ;
if ( failure - > count > SMB_MAX_RETRIES ) {
_log_err ( LOG_ALERT
, " service(%s) ignoring max retries; %d > %d "
, service = = NULL ? " **unknown** " : service
, failure - > count
, SMB_MAX_RETRIES ) ;
}
}
}
_pam_delete ( failure - > agent ) ; /* tidy up */
_pam_delete ( failure - > user ) ; /* tidy up */
free ( failure ) ;
}
}
int _smb_verify_password ( pam_handle_t * pamh
, const struct smb_passwd * smb_pwent
, const char * p , unsigned int ctrl )
{
uchar hash_pass [ 16 ] ;
uchar lm_pw [ 16 ] ;
uchar nt_pw [ 16 ] ;
int retval ;
char * data_name ;
const char * name ;
if ( ! smb_pwent )
return PAM_ABORT ;
name = smb_pwent - > smb_name ;
# ifdef HAVE_PAM_FAIL_DELAY
if ( off ( SMB_NODELAY , ctrl ) ) {
( void ) pam_fail_delay ( pamh , 1000000 ) ; /* 1 sec delay for on failure */
}
# endif
if ( ! smb_pwent - > smb_passwd )
{
_log_err ( LOG_DEBUG , " user %s has null SMB password "
, name ) ;
if ( off ( SMB__NONULL , ctrl )
& & ( smb_pwent - > acct_ctrl & ACB_PWNOTREQ ) )
{ /* this means we've succeeded */
return PAM_SUCCESS ;
} else {
const char * service ;
pam_get_item ( pamh , PAM_SERVICE , ( const void * * ) & service ) ;
_log_err ( LOG_NOTICE
, " failed auth request by %s for service %s as %s(%d) "
, uidtoname ( getuid ( ) )
, service ? service : " **unknown** " , name
, smb_pwent - > smb_userid ) ;
return PAM_AUTH_ERR ;
}
}
data_name = ( char * ) malloc ( sizeof ( FAIL_PREFIX )
+ strlen ( name ) ) ;
if ( data_name = = NULL ) {
_log_err ( LOG_CRIT , " no memory for data-name " ) ;
}
strncpy ( data_name , FAIL_PREFIX , sizeof ( FAIL_PREFIX ) ) ;
strncpy ( data_name + sizeof ( FAIL_PREFIX ) - 1 , name , strlen ( name ) + 1 ) ;
/* First we check whether we've been given the password in already
encrypted form . */
if ( strlen ( p ) = = 16 | | ( strlen ( p ) = = 32
& & pdb_gethexpwd ( p , ( char * ) hash_pass ) ) ) {
if ( ! memcmp ( hash_pass , smb_pwent - > smb_passwd , 16 )
| | ( smb_pwent - > smb_nt_passwd
& & ! memcmp ( hash_pass , smb_pwent - > smb_nt_passwd , 16 ) ) )
{
retval = PAM_SUCCESS ;
if ( data_name ) { /* reset failures */
pam_set_data ( pamh , data_name , NULL , _cleanup_failures ) ;
}
_pam_delete ( data_name ) ;
memset ( hash_pass , ' \0 ' , 16 ) ;
smb_pwent = NULL ;
return retval ;
}
}
/*
* The password we were given wasn ' t an encrypted password , or it
* didn ' t match the one we have . We encrypt the password now and try
* again .
*/
nt_lm_owf_gen ( p , nt_pw , lm_pw ) ;
/* the moment of truth -- do we agree with the password? */
if ( ! memcmp ( nt_pw , smb_pwent - > smb_nt_passwd , 16 ) ) {
retval = PAM_SUCCESS ;
if ( data_name ) { /* reset failures */
pam_set_data ( pamh , data_name , NULL , _cleanup_failures ) ;
}
} else {
const char * service ;
pam_get_item ( pamh , PAM_SERVICE , ( const void * * ) & service ) ;
if ( data_name ! = NULL ) {
struct _pam_failed_auth * new = NULL ;
const struct _pam_failed_auth * old = NULL ;
/* get a failure recorder */
new = ( struct _pam_failed_auth * )
malloc ( sizeof ( struct _pam_failed_auth ) ) ;
if ( new ! = NULL ) {
/* any previous failures for this user ? */
pam_get_data ( pamh , data_name , ( const void * * ) & old ) ;
if ( old ! = NULL ) {
new - > count = old - > count + 1 ;
if ( new - > count > = SMB_MAX_RETRIES ) {
retval = PAM_MAXTRIES ;
}
} else {
_log_err ( LOG_NOTICE
, " failed auth request by %s for service %s as %s(%d) "
, uidtoname ( getuid ( ) )
, service ? service : " **unknown** " , name
, smb_pwent - > smb_userid ) ;
new - > count = 1 ;
}
2001-10-22 06:43:20 +04:00
new - > user = smb_xstrdup ( name ) ;
2001-04-25 00:00:12 +04:00
new - > id = smb_pwent - > smb_userid ;
2001-10-22 06:43:20 +04:00
new - > agent = smb_xstrdup ( uidtoname ( getuid ( ) ) ) ;
2001-04-25 00:00:12 +04:00
pam_set_data ( pamh , data_name , new , _cleanup_failures ) ;
} else {
_log_err ( LOG_CRIT , " no memory for failure recorder " ) ;
_log_err ( LOG_NOTICE
, " failed auth request by %s for service %s as %s(%d) "
, uidtoname ( getuid ( ) )
, service ? service : " **unknown** " , name
, smb_pwent - > smb_userid ) ;
}
} else {
_log_err ( LOG_NOTICE
, " failed auth request by %s for service %s as %s(%d) "
, uidtoname ( getuid ( ) )
, service ? service : " **unknown** " , name
, smb_pwent - > smb_userid ) ;
retval = PAM_AUTH_ERR ;
}
}
_pam_delete ( data_name ) ;
smb_pwent = NULL ;
return retval ;
}
/*
* _smb_blankpasswd ( ) is a quick check for a blank password
*
* returns TRUE if user does not have a password
* - to avoid prompting for one in such cases ( CG )
*/
int _smb_blankpasswd ( unsigned int ctrl , const struct smb_passwd * smb_pwent )
{
int retval ;
/*
* This function does not have to be too smart if something goes
* wrong , return FALSE and let this case to be treated somewhere
* else ( CG )
*/
if ( on ( SMB__NONULL , ctrl ) )
return 0 ; /* will fail but don't let on yet */
if ( smb_pwent - > smb_passwd = = NULL )
retval = 1 ;
else
retval = 0 ;
return retval ;
}
/*
* obtain a password from the user
*/
int _smb_read_password ( pam_handle_t * pamh , unsigned int ctrl
, const char * comment , const char * prompt1
, const char * prompt2 , const char * data_name
, const char * * pass )
{
int authtok_flag ;
int retval ;
const char * item = NULL ;
char * token ;
struct pam_message msg [ 3 ] , * pmsg [ 3 ] ;
struct pam_response * resp ;
int i , expect ;
/* make sure nothing inappropriate gets returned */
* pass = token = NULL ;
/* which authentication token are we getting? */
authtok_flag = on ( SMB__OLD_PASSWD , ctrl ) ? PAM_OLDAUTHTOK : PAM_AUTHTOK ;
/* should we obtain the password from a PAM item ? */
if ( on ( SMB_TRY_FIRST_PASS , ctrl ) | | on ( SMB_USE_FIRST_PASS , ctrl ) ) {
retval = pam_get_item ( pamh , authtok_flag , ( const void * * ) & item ) ;
if ( retval ! = PAM_SUCCESS ) {
/* very strange. */
_log_err ( LOG_ALERT
, " pam_get_item returned error to smb_read_password " ) ;
return retval ;
} else if ( item ! = NULL ) { /* we have a password! */
* pass = item ;
item = NULL ;
return PAM_SUCCESS ;
} else if ( on ( SMB_USE_FIRST_PASS , ctrl ) ) {
return PAM_AUTHTOK_RECOVER_ERR ; /* didn't work */
} else if ( on ( SMB_USE_AUTHTOK , ctrl )
& & off ( SMB__OLD_PASSWD , ctrl ) )
{
return PAM_AUTHTOK_RECOVER_ERR ;
}
}
/*
* getting here implies we will have to get the password from the
* user directly .
*/
/* prepare to converse */
if ( comment ! = NULL & & off ( SMB__QUIET , ctrl ) ) {
pmsg [ 0 ] = & msg [ 0 ] ;
msg [ 0 ] . msg_style = PAM_TEXT_INFO ;
msg [ 0 ] . msg = comment ;
i = 1 ;
} else {
i = 0 ;
}
pmsg [ i ] = & msg [ i ] ;
msg [ i ] . msg_style = PAM_PROMPT_ECHO_OFF ;
msg [ i + + ] . msg = prompt1 ;
if ( prompt2 ! = NULL ) {
pmsg [ i ] = & msg [ i ] ;
msg [ i ] . msg_style = PAM_PROMPT_ECHO_OFF ;
msg [ i + + ] . msg = prompt2 ;
expect = 2 ;
} else
expect = 1 ;
resp = NULL ;
retval = converse ( pamh , ctrl , i , pmsg , & resp ) ;
if ( resp ! = NULL ) {
int j = comment ? 1 : 0 ;
/* interpret the response */
if ( retval = = PAM_SUCCESS ) { /* a good conversation */
2001-10-22 06:43:20 +04:00
token = smb_xstrdup ( resp [ j + + ] . resp ) ;
2001-04-25 00:00:12 +04:00
if ( token ! = NULL ) {
if ( expect = = 2 ) {
/* verify that password entered correctly */
if ( ! resp [ j ] . resp | | strcmp ( token , resp [ j ] . resp ) ) {
_pam_delete ( token ) ;
retval = PAM_AUTHTOK_RECOVER_ERR ;
make_remark ( pamh , ctrl , PAM_ERROR_MSG
, MISTYPED_PASS ) ;
}
}
} else {
_log_err ( LOG_NOTICE , " could not recover authentication token " ) ;
}
}
/* tidy up */
_pam_drop_reply ( resp , expect ) ;
} else {
retval = ( retval = = PAM_SUCCESS ) ? PAM_AUTHTOK_RECOVER_ERR : retval ;
}
if ( retval ! = PAM_SUCCESS ) {
if ( on ( SMB_DEBUG , ctrl ) )
_log_err ( LOG_DEBUG , " unable to obtain a password " ) ;
return retval ;
}
/* 'token' is the entered password */
if ( off ( SMB_NOT_SET_PASS , ctrl ) ) {
/* we store this password as an item */
retval = pam_set_item ( pamh , authtok_flag , ( const void * ) token ) ;
_pam_delete ( token ) ; /* clean it up */
if ( retval ! = PAM_SUCCESS
| | ( retval = pam_get_item ( pamh , authtok_flag
, ( const void * * ) & item ) ) ! = PAM_SUCCESS )
{
_log_err ( LOG_CRIT , " error manipulating password " ) ;
return retval ;
}
} else {
/*
* then store it as data specific to this module . pam_end ( )
* will arrange to clean it up .
*/
retval = pam_set_data ( pamh , data_name , ( void * ) token , _cleanup ) ;
if ( retval ! = PAM_SUCCESS
| | ( retval = pam_get_data ( pamh , data_name , ( const void * * ) & item ) )
! = PAM_SUCCESS )
{
_log_err ( LOG_CRIT , " error manipulating password data [%s] "
, pam_strerror ( pamh , retval ) ) ;
_pam_delete ( token ) ;
item = NULL ;
return retval ;
}
token = NULL ; /* break link to password */
}
* pass = item ;
item = NULL ; /* break link to password */
return PAM_SUCCESS ;
}
int _pam_smb_approve_pass ( pam_handle_t * pamh
, unsigned int ctrl
, const char * pass_old
, const char * pass_new )
{
/* Further checks should be handled through module stacking. -SRL */
if ( pass_new = = NULL | | ( pass_old & & ! strcmp ( pass_old , pass_new ) ) )
{
if ( on ( SMB_DEBUG , ctrl ) ) {
_log_err ( LOG_DEBUG ,
" passwd: bad authentication token (null or unchanged) " ) ;
}
make_remark ( pamh , ctrl , PAM_ERROR_MSG , pass_new = = NULL ?
" No password supplied " : " Password unchanged " ) ;
return PAM_AUTHTOK_ERR ;
}
return PAM_SUCCESS ;
}