1996-05-04 11:50:46 +04:00
/*
* Unix SMB / Netbios implementation . Version 1.9 . smbpasswd module . Copyright
1998-01-22 16:27:43 +03:00
* ( C ) Jeremy Allison 1995 - 1998
1996-05-04 11:50:46 +04:00
*
* 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"
1998-03-19 23:06:47 +03:00
/*
* Password changing error codes .
*/
struct
{
int err ;
char * message ;
} pw_change_errmap [ ] =
{
{ 5 , " User has insufficient privilege " } ,
{ 86 , " The specified password is invalid " } ,
{ 2226 , " Operation only permitted on a Primary Domain Controller " } ,
1998-03-31 00:12:12 +04:00
{ 2242 , " The password of this user has expired. " } ,
{ 2243 , " The password of this user cannot change. " } ,
{ 2244 , " This password cannot be used now (password history conflict). " } ,
{ 2245 , " The password is shorter than required. " } ,
{ 2246 , " The password of this user is too recent to change. " } ,
1998-03-19 23:06:47 +03:00
{ 0 , NULL }
} ;
1996-05-04 11:50:46 +04:00
1998-03-26 22:11:31 +03:00
/******************************************************
Return an error message for a remote password change .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * get_error_message ( struct cli_state * cli )
{
static fstring error_message ;
int errclass ;
int errnum ;
int i ;
/*
* Errors are of two kinds - smb errors ,
* dealt with by cli_errstr , and rap
* errors , whose error code is in cli . error .
*/
cli_error ( cli , & errclass , & errnum ) ;
if ( errclass ! = 0 )
return cli_errstr ( cli ) ;
sprintf ( error_message , " code %d " , cli - > error ) ;
for ( i = 0 ; pw_change_errmap [ i ] . message ! = NULL ; i + + ) {
if ( pw_change_errmap [ i ] . err = = cli - > error ) {
fstrcpy ( error_message , pw_change_errmap [ i ] . message ) ;
break ;
}
}
return error_message ;
}
1998-03-25 00:04:36 +03:00
/******************************************************
Convert a hex password .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1996-05-04 11:50:46 +04:00
static int gethexpwd ( char * p , char * pwd )
{
int i ;
unsigned char lonybble , hinybble ;
char * hexchars = " 0123456789ABCDEF " ;
char * p1 , * p2 ;
for ( i = 0 ; i < 32 ; i + = 2 ) {
hinybble = toupper ( p [ i ] ) ;
lonybble = toupper ( p [ i + 1 ] ) ;
p1 = strchr ( hexchars , hinybble ) ;
p2 = strchr ( hexchars , lonybble ) ;
if ( ! p1 | | ! p2 )
return ( False ) ;
hinybble = PTR_DIFF ( p1 , hexchars ) ;
lonybble = PTR_DIFF ( p2 , hexchars ) ;
pwd [ i / 2 ] = ( hinybble < < 4 ) | lonybble ;
}
return ( True ) ;
}
1998-03-25 00:04:36 +03:00
/******************************************************
Find a password entry by name .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1997-10-31 05:40:55 +03:00
static struct smb_passwd *
1996-05-04 11:50:46 +04:00
_my_get_smbpwnam ( FILE * fp , char * name , BOOL * valid_old_pwd ,
1998-04-16 00:00:41 +04:00
BOOL * got_valid_nt_entry , BOOL * got_valid_last_change_time , long * pwd_seekpos )
1996-05-04 11:50:46 +04:00
{
1998-03-19 23:06:47 +03:00
/* Static buffers we will return. */
static struct smb_passwd pw_buf ;
static pstring user_name ;
static unsigned char smbpwd [ 16 ] ;
static unsigned char smbntpwd [ 16 ] ;
1996-05-04 11:50:46 +04:00
char linebuf [ 256 ] ;
unsigned char c ;
unsigned char * p ;
long uidval ;
long linebuf_len ;
1998-03-25 00:04:36 +03:00
pw_buf . acct_ctrl = ACB_NORMAL ;
1998-04-16 00:00:41 +04:00
* got_valid_last_change_time = False ;
* got_valid_nt_entry = False ;
* valid_old_pwd = False ;
1998-03-25 00:04:36 +03:00
1996-05-04 11:50:46 +04:00
/*
* Scan the file , a line at a time and check if the name matches .
*/
while ( ! feof ( fp ) ) {
linebuf [ 0 ] = ' \0 ' ;
* pwd_seekpos = ftell ( fp ) ;
fgets ( linebuf , 256 , fp ) ;
if ( ferror ( fp ) )
return NULL ;
/*
* Check if the string is terminated with a newline - if not
* then we must keep reading and discard until we get one .
*/
linebuf_len = strlen ( linebuf ) ;
if ( linebuf [ linebuf_len - 1 ] ! = ' \n ' ) {
c = ' \0 ' ;
while ( ! ferror ( fp ) & & ! feof ( fp ) ) {
c = fgetc ( fp ) ;
if ( c = = ' \n ' )
break ;
}
} else
linebuf [ linebuf_len - 1 ] = ' \0 ' ;
if ( ( linebuf [ 0 ] = = 0 ) & & feof ( fp ) )
break ;
/*
* The line we have should be of the form : -
*
* username : uid : [ 32 hex bytes ] : . . . . other flags presently
* ignored . . . .
*
* or ,
*
* username : uid : [ 32 hex bytes ] : [ 32 hex bytes ] : . . . . ignored . . . .
*
* if Windows NT compatible passwords are also present .
*/
if ( linebuf [ 0 ] = = ' # ' | | linebuf [ 0 ] = = ' \0 ' )
continue ;
p = ( unsigned char * ) strchr ( linebuf , ' : ' ) ;
if ( p = = NULL )
continue ;
/*
* As 256 is shorter than a pstring we don ' t need to check
* length here - if this ever changes . . . .
*/
strncpy ( user_name , linebuf , PTR_DIFF ( p , linebuf ) ) ;
user_name [ PTR_DIFF ( p , linebuf ) ] = ' \0 ' ;
if ( ! strequal ( user_name , name ) )
continue ;
/* User name matches - get uid and password */
p + + ; /* Go past ':' */
if ( ! isdigit ( * p ) )
return ( False ) ;
uidval = atoi ( ( char * ) p ) ;
while ( * p & & isdigit ( * p ) )
p + + ;
if ( * p ! = ' : ' )
return ( False ) ;
/*
* Now get the password value - this should be 32 hex digits
* which are the ascii representations of a 16 byte string .
* Get two at a time and put them into the password .
*/
p + + ;
* pwd_seekpos + = PTR_DIFF ( p , linebuf ) ; /* Save exact position
* of passwd in file -
* this is used by
* smbpasswd . c */
if ( * p = = ' * ' | | * p = = ' X ' ) {
/* Password deliberately invalid - end here. */
* valid_old_pwd = False ;
* got_valid_nt_entry = False ;
pw_buf . smb_nt_passwd = NULL ; /* No NT password (yet)*/
1998-03-25 00:04:36 +03:00
pw_buf . acct_ctrl | = ACB_DISABLED ;
1996-05-04 11:50:46 +04:00
/* Now check if the NT compatible password is
available . */
p + = 33 ; /* Move to the first character of the line after
the lanman password . */
if ( ( linebuf_len > = ( PTR_DIFF ( p , linebuf ) + 33 ) ) & & ( p [ 32 ] = = ' : ' ) ) {
/* NT Entry was valid - even if 'X' or '*', can be overwritten */
* got_valid_nt_entry = True ;
if ( * p ! = ' * ' & & * p ! = ' X ' ) {
1996-05-29 11:47:47 +04:00
if ( gethexpwd ( ( char * ) p , ( char * ) smbntpwd ) )
pw_buf . smb_nt_passwd = smbntpwd ;
1996-05-04 11:50:46 +04:00
}
}
pw_buf . smb_name = user_name ;
pw_buf . smb_userid = uidval ;
pw_buf . smb_passwd = NULL ; /* No password */
return ( & pw_buf ) ;
}
if ( linebuf_len < ( PTR_DIFF ( p , linebuf ) + 33 ) )
return ( False ) ;
if ( p [ 32 ] ! = ' : ' )
return ( False ) ;
1996-05-29 11:47:47 +04:00
if ( ! strncasecmp ( ( char * ) p , " NO PASSWORD " , 11 ) ) {
pw_buf . smb_passwd = NULL ; /* No password */
1998-03-25 00:04:36 +03:00
pw_buf . acct_ctrl | = ACB_PWNOTREQ ;
1996-05-04 11:50:46 +04:00
} else {
1996-05-29 11:47:47 +04:00
if ( ! gethexpwd ( ( char * ) p , ( char * ) smbpwd ) )
return False ;
pw_buf . smb_passwd = smbpwd ;
1996-05-04 11:50:46 +04:00
}
pw_buf . smb_name = user_name ;
pw_buf . smb_userid = uidval ;
pw_buf . smb_nt_passwd = NULL ;
* got_valid_nt_entry = False ;
* valid_old_pwd = True ;
/* Now check if the NT compatible password is
available . */
p + = 33 ; /* Move to the first character of the line after
the lanman password . */
if ( ( linebuf_len > = ( PTR_DIFF ( p , linebuf ) + 33 ) ) & & ( p [ 32 ] = = ' : ' ) ) {
/* NT Entry was valid - even if 'X' or '*', can be overwritten */
* got_valid_nt_entry = True ;
if ( * p ! = ' * ' & & * p ! = ' X ' ) {
1996-05-29 11:47:47 +04:00
if ( gethexpwd ( ( char * ) p , ( char * ) smbntpwd ) )
pw_buf . smb_nt_passwd = smbntpwd ;
1996-05-04 11:50:46 +04:00
}
1998-03-25 00:04:36 +03:00
p + = 33 ; /* Move to the first character of the line after
the NT password . */
1996-05-04 11:50:46 +04:00
}
1998-03-25 00:04:36 +03:00
/*
* Check if the account type bits have been encoded after the
* NT password ( in the form [ NDHTUWSLXI ] ) .
*/
if ( * p = = ' [ ' ) {
BOOL finished = False ;
pw_buf . acct_ctrl = 0 ;
for ( p + + ; * p & & ! finished ; p + + ) {
switch ( * p ) {
1998-03-25 00:44:49 +03:00
#if 0
/*
* Hmmm . Don ' t allow these to be set / read independently
* of the actual password fields . We don ' t want a mismatch .
* JRA .
*/
1998-03-25 00:04:36 +03:00
case ' N ' :
/* 'N'o password. */
pw_buf . acct_ctrl | = ACB_PWNOTREQ ;
break ;
case ' D ' :
/* 'D'isabled. */
pw_buf . acct_ctrl | = ACB_DISABLED ;
break ;
1998-03-25 00:44:49 +03:00
# endif
1998-03-25 00:04:36 +03:00
case ' H ' :
/* 'H'omedir required. */
pw_buf . acct_ctrl | = ACB_HOMDIRREQ ;
break ;
case ' T ' :
/* 'T'emp account. */
pw_buf . acct_ctrl | = ACB_TEMPDUP ;
break ;
case ' U ' :
/* 'U'ser account (normal). */
pw_buf . acct_ctrl | = ACB_NORMAL ;
break ;
case ' M ' :
/* 'M'NS logon user account. What is this ? */
pw_buf . acct_ctrl | = ACB_MNS ;
break ;
case ' W ' :
/* 'W'orkstation account. */
pw_buf . acct_ctrl | = ACB_WSTRUST ;
break ;
case ' S ' :
/* 'S'erver account. */
pw_buf . acct_ctrl | = ACB_SVRTRUST ;
break ;
case ' L ' :
/* 'L'ocked account. */
pw_buf . acct_ctrl | = ACB_AUTOLOCK ;
break ;
case ' X ' :
/* No 'X'piry. */
pw_buf . acct_ctrl | = ACB_PWNOEXP ;
break ;
case ' I ' :
/* 'I'nterdomain trust account. */
pw_buf . acct_ctrl | = ACB_DOMTRUST ;
break ;
case ' : ' :
case ' \n ' :
case ' \0 ' :
case ' ] ' :
default :
finished = True ;
}
}
/* Must have some account type set. */
if ( pw_buf . acct_ctrl = = 0 )
pw_buf . acct_ctrl = ACB_NORMAL ;
1998-04-16 00:00:41 +04:00
/* Now try and get the last change time. */
if ( * p = = ' ] ' )
p + + ;
if ( * p = = ' : ' ) {
p + + ;
if ( * p & & StrnCaseCmp ( p , " LCT- " , 4 ) ) {
int i ;
p + = 4 ;
for ( i = 0 ; i < 8 ; i + + ) {
if ( p [ i ] = = ' \0 ' | | ! isxdigit ( p [ i ] ) )
break ;
}
if ( i = = 8 ) {
/*
* p points at 8 characters of hex digits -
* read into a time_t as the seconds since
* 1970 that the password was last changed .
*/
pw_buf . last_change_time = ( time_t ) strtol ( p , NULL , 16 ) ;
* got_valid_last_change_time = True ;
} /* i == 8 */
} /* *p && StrnCaseCmp() */
} /* *p == ':' */
1998-03-25 00:04:36 +03:00
} else {
/* 'Old' style file. Fake up based on user name. */
/*
* Currently machine accounts are kept in the same
* password file as ' normal accounts ' . If this changes
* we will have to fix this code . JRA .
*/
if ( pw_buf . smb_name [ strlen ( pw_buf . smb_name ) - 1 ] = = ' $ ' ) {
pw_buf . acct_ctrl & = ~ ACB_NORMAL ;
pw_buf . acct_ctrl | = ACB_WSTRUST ;
}
}
1996-05-04 11:50:46 +04:00
return & pw_buf ;
}
return NULL ;
}
1998-03-25 00:04:36 +03:00
/**********************************************************
Allocate an unused uid in the smbpasswd file to a new
machine account .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-03-26 05:14:40 +03:00
int get_new_machine_uid ( void )
1998-03-25 00:04:36 +03:00
{
1998-03-26 05:14:40 +03:00
int next_uid_start ;
1998-04-14 04:41:59 +04:00
void * vp ;
1998-03-26 05:14:40 +03:00
struct smb_passwd * smbpw ;
if ( sizeof ( uid_t ) = = 2 )
next_uid_start = 65533 ;
if ( sizeof ( uid_t ) = = 4 )
next_uid_start = 0x7fffffff ;
1998-04-14 04:41:59 +04:00
vp = startsmbpwent ( False ) ;
while ( ( smbpw = getsmbpwent ( vp ) ) ! = NULL ) {
1998-03-26 05:22:08 +03:00
if ( ( smbpw - > acct_ctrl & ( ACB_SVRTRUST | ACB_WSTRUST ) ) )
next_uid_start = MIN ( next_uid_start , ( smbpw - > smb_userid - 1 ) ) ;
1998-03-26 05:14:40 +03:00
}
1998-04-14 04:41:59 +04:00
endsmbpwent ( vp ) ;
1998-03-26 05:14:40 +03:00
return next_uid_start ;
1998-03-25 00:04:36 +03:00
}
/*********************************************************
Print command usage on stderr and die .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-03-19 23:06:47 +03:00
static void usage ( char * name , BOOL is_root )
1996-05-04 11:50:46 +04:00
{
1998-03-19 23:06:47 +03:00
if ( is_root )
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " Usage is : %s [-D DEBUGLEVEL] [-a] [-d] [-m] [-n] [username] [password] \n \
% s : [ - R < name resolve order > ] [ - D DEBUGLEVEL ] [ - r machine ] [ username ] [ password ] \ n % s : [ - h ] \ n " , name, name, name);
1998-03-19 23:06:47 +03:00
else
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [password] \n " , name ) ;
1996-05-04 11:50:46 +04:00
exit ( 1 ) ;
}
1998-03-25 00:04:36 +03:00
/*********************************************************
Start here .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-03-19 23:06:47 +03:00
int main ( int argc , char * * argv )
1996-05-04 11:50:46 +04:00
{
1998-03-19 23:06:47 +03:00
extern char * optarg ;
extern int optind ;
1998-03-26 22:11:31 +03:00
extern int DEBUGLEVEL ;
1998-03-19 23:06:47 +03:00
char * prog_name ;
1996-05-04 11:50:46 +04:00
int real_uid ;
struct passwd * pwd ;
1998-03-25 00:04:36 +03:00
struct passwd machine_account_pwd ;
1996-05-04 11:50:46 +04:00
fstring old_passwd ;
fstring new_passwd ;
uchar new_p16 [ 16 ] ;
uchar new_nt_p16 [ 16 ] ;
char * p ;
struct smb_passwd * smb_pwent ;
FILE * fp ;
BOOL valid_old_pwd = False ;
1998-03-19 23:06:47 +03:00
BOOL got_valid_nt_entry = False ;
1996-05-04 11:50:46 +04:00
long seekpos ;
int pwfd ;
1998-04-16 00:00:41 +04:00
pstring ascii_p16 ;
1996-05-04 11:50:46 +04:00
char c ;
1998-03-24 03:37:53 +03:00
int ch ;
1996-05-04 11:50:46 +04:00
int ret , i , err , writelen ;
int lockfd = - 1 ;
char * pfile = SMB_PASSWD_FILE ;
char readbuf [ 16 * 1024 ] ;
1998-03-19 23:06:47 +03:00
BOOL is_root = False ;
pstring user_name ;
1998-03-25 00:44:49 +03:00
pstring machine_dir_name ;
1998-03-19 23:06:47 +03:00
char * remote_machine = NULL ;
BOOL add_user = False ;
BOOL got_new_pass = False ;
1998-04-16 00:00:41 +04:00
BOOL got_valid_last_change_time = False ;
1998-03-24 03:37:53 +03:00
BOOL machine_account = False ;
1998-03-25 00:04:36 +03:00
BOOL disable_user = False ;
BOOL set_no_password = False ;
1998-03-19 23:06:47 +03:00
pstring servicesf = CONFIGFILE ;
new_passwd [ 0 ] = ' \0 ' ;
user_name [ 0 ] = ' \0 ' ;
memset ( old_passwd , ' \0 ' , sizeof ( old_passwd ) ) ;
1998-03-25 00:04:36 +03:00
memset ( new_passwd , ' \0 ' , sizeof ( new_passwd ) ) ;
1998-03-19 23:06:47 +03:00
prog_name = argv [ 0 ] ;
1996-05-31 19:13:29 +04:00
TimeInit ( ) ;
1998-03-19 23:06:47 +03:00
setup_logging ( prog_name , True ) ;
1996-05-04 11:50:46 +04:00
1997-07-19 00:21:32 +04:00
charset_initialise ( ) ;
1998-03-19 23:06:47 +03:00
if ( ! lp_load ( servicesf , True , False , False ) ) {
fprintf ( stderr , " %s: Can't load %s - run testparm to debug it \n " , prog_name , servicesf ) ;
1996-05-04 11:50:46 +04:00
}
1998-03-19 23:06:47 +03:00
codepage_initialise ( lp_client_code_page ( ) ) ;
1996-05-04 11:50:46 +04:00
/* Get the real uid */
real_uid = getuid ( ) ;
1998-03-19 23:06:47 +03:00
/* Check the effective uid */
if ( ( geteuid ( ) = = 0 ) & & ( real_uid ! = 0 ) ) {
fprintf ( stderr , " %s: Must *NOT* be setuid root. \n " , prog_name ) ;
exit ( 1 ) ;
1997-07-11 00:15:48 +04:00
}
1998-03-19 23:06:47 +03:00
is_root = ( real_uid = = 0 ) ;
1998-03-26 22:11:31 +03:00
while ( ( ch = getopt ( argc , argv , " adhmnr:R:D: " ) ) ! = EOF ) {
1998-03-24 03:37:53 +03:00
switch ( ch ) {
1998-03-19 23:06:47 +03:00
case ' a ' :
1998-03-25 00:04:36 +03:00
if ( is_root )
add_user = True ;
else
usage ( prog_name , is_root ) ;
break ;
case ' d ' :
if ( is_root ) {
disable_user = True ;
got_new_pass = True ;
strcpy ( new_passwd , " XXXXXX " ) ;
} else
usage ( prog_name , is_root ) ;
1998-03-19 23:06:47 +03:00
break ;
1998-03-26 22:11:31 +03:00
case ' D ' :
DEBUGLEVEL = atoi ( optarg ) ;
break ;
1998-03-25 00:04:36 +03:00
case ' n ' :
if ( is_root ) {
set_no_password = True ;
got_new_pass = True ;
strcpy ( new_passwd , " NO PASSWORD " ) ;
} else
usage ( prog_name , is_root ) ;
1998-03-19 23:06:47 +03:00
case ' r ' :
remote_machine = optarg ;
break ;
1998-03-26 05:14:40 +03:00
case ' R ' :
if ( is_root ) {
lp_set_name_resolve_order ( optarg ) ;
break ;
} else
usage ( prog_name , is_root ) ;
1998-03-24 03:37:53 +03:00
case ' m ' :
1998-03-26 05:22:08 +03:00
if ( is_root ) {
1998-03-25 00:04:36 +03:00
machine_account = True ;
1998-03-26 05:22:08 +03:00
} else
1998-03-25 00:04:36 +03:00
usage ( prog_name , is_root ) ;
1998-03-24 03:37:53 +03:00
break ;
1998-03-19 23:06:47 +03:00
case ' h ' :
default :
usage ( prog_name , is_root ) ;
}
}
1997-08-06 18:35:23 +04:00
1998-03-19 23:06:47 +03:00
argc - = optind ;
argv + = optind ;
1997-07-11 00:15:48 +04:00
1998-03-19 23:06:47 +03:00
/*
* Ensure add_user and remote machine are
* not both set .
*/
if ( add_user & & ( remote_machine ! = NULL ) )
usage ( prog_name , True ) ;
if ( is_root ) {
/*
* Deal with root - can add a user , but only locally .
*/
switch ( argc ) {
case 0 :
break ;
case 1 :
/* If we are root we can change another's password. */
pstrcpy ( user_name , argv [ 0 ] ) ;
break ;
case 2 :
pstrcpy ( user_name , argv [ 0 ] ) ;
fstrcpy ( new_passwd , argv [ 1 ] ) ;
got_new_pass = True ;
break ;
default :
usage ( prog_name , True ) ;
}
1997-08-06 18:35:23 +04:00
1998-04-07 21:44:02 +04:00
if ( * user_name ) {
if ( ( pwd = getpwnam ( user_name ) ) = = NULL ) {
fprintf ( stderr , " %s: User \" %s \" was not found in system password file. \n " ,
prog_name , user_name ) ;
exit ( 1 ) ;
}
} else {
1998-03-19 23:06:47 +03:00
if ( ( pwd = getpwuid ( real_uid ) ) ! = NULL )
pstrcpy ( user_name , pwd - > pw_name ) ;
1997-08-06 18:35:23 +04:00
}
1998-03-19 23:06:47 +03:00
} else {
1997-08-06 18:35:23 +04:00
1998-03-19 23:06:47 +03:00
if ( add_user ) {
fprintf ( stderr , " %s: Only root can set anothers password. \n " , prog_name ) ;
usage ( prog_name , False ) ;
1997-08-06 18:35:23 +04:00
}
1998-03-19 23:06:47 +03:00
if ( argc > 1 )
usage ( prog_name , False ) ;
1997-08-06 18:35:23 +04:00
1998-03-19 23:06:47 +03:00
if ( argc = = 1 ) {
fstrcpy ( new_passwd , argv [ 0 ] ) ;
got_new_pass = True ;
1997-08-06 18:35:23 +04:00
}
1998-03-19 23:06:47 +03:00
if ( ( pwd = getpwuid ( real_uid ) ) ! = NULL )
pstrcpy ( user_name , pwd - > pw_name ) ;
/*
* A non - root user is always setting a password
* via a remote machine ( even if that machine is
* localhost ) .
*/
if ( remote_machine = = NULL )
remote_machine = " 127.0.0.1 " ;
}
if ( * user_name = = ' \0 ' ) {
fprintf ( stderr , " %s: Unable to get a user name for password change. \n " , prog_name ) ;
exit ( 1 ) ;
1996-05-04 11:50:46 +04:00
}
1997-08-06 18:35:23 +04:00
1998-03-25 00:04:36 +03:00
/*
* If we are adding a machine account then pretend
* we already have the new password , we will be using
* the machinename as the password .
*/
if ( add_user & & machine_account ) {
got_new_pass = True ;
strncpy ( new_passwd , user_name , sizeof ( fstring ) ) ;
new_passwd [ sizeof ( fstring ) - 1 ] = ' \0 ' ;
strlower ( new_passwd ) ;
}
1998-03-19 23:06:47 +03:00
/*
* If we are root we don ' t ask for the old password ( unless it ' s on a
* remote machine .
*/
if ( remote_machine ! = NULL ) {
p = getpass ( " Old SMB password: " ) ;
fstrcpy ( old_passwd , p ) ;
1997-08-06 18:35:23 +04:00
}
1998-03-19 23:06:47 +03:00
if ( ! got_new_pass ) {
1997-08-06 18:35:23 +04:00
new_passwd [ 0 ] = ' \0 ' ;
p = getpass ( " New SMB password: " ) ;
strncpy ( new_passwd , p , sizeof ( fstring ) ) ;
new_passwd [ sizeof ( fstring ) - 1 ] = ' \0 ' ;
p = getpass ( " Retype new SMB password: " ) ;
1997-08-19 23:22:26 +04:00
if ( strncmp ( p , new_passwd , sizeof ( fstring ) - 1 ) )
1997-08-06 18:35:23 +04:00
{
1998-03-19 23:06:47 +03:00
fprintf ( stderr , " %s: Mismatch - password unchanged. \n " , prog_name ) ;
1997-08-06 18:35:23 +04:00
exit ( 1 ) ;
}
1996-05-04 11:50:46 +04:00
}
1998-03-19 23:06:47 +03:00
if ( new_passwd [ 0 ] = = ' \0 ' ) {
1996-05-04 11:50:46 +04:00
printf ( " Password not set \n " ) ;
exit ( 0 ) ;
}
1998-03-19 23:06:47 +03:00
/*
* Now do things differently depending on if we ' re changing the
* password on a remote machine . Remember - a normal user is
* always using this code , looping back to the local smbd .
*/
if ( remote_machine ! = NULL ) {
struct cli_state cli ;
struct in_addr ip ;
fstring myname ;
if ( get_myname ( myname , NULL ) = = False ) {
fprintf ( stderr , " %s: unable to get my hostname. \n " , prog_name ) ;
exit ( 1 ) ;
}
if ( ! resolve_name ( remote_machine , & ip ) ) {
fprintf ( stderr , " %s: unable to find an IP address for machine %s. \n " ,
prog_name , remote_machine ) ;
exit ( 1 ) ;
}
1998-03-26 22:11:31 +03:00
1998-04-10 00:48:48 +04:00
memset ( & cli , ' \0 ' , sizeof ( struct cli_state ) ) ;
1998-03-26 22:11:31 +03:00
1998-03-19 23:06:47 +03:00
if ( ! cli_initialise ( & cli ) | | ! cli_connect ( & cli , remote_machine , & ip ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: unable to connect to SMB server on machine %s. Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
exit ( 1 ) ;
}
if ( ! cli_session_request ( & cli , remote_machine , 0x20 , myname ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: machine %s rejected the session setup. Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
cli_shutdown ( & cli ) ;
exit ( 1 ) ;
}
cli . protocol = PROTOCOL_NT1 ;
if ( ! cli_negprot ( & cli ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: machine %s rejected the negotiate protocol. Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
cli_shutdown ( & cli ) ;
exit ( 1 ) ;
}
1996-05-04 11:50:46 +04:00
1998-03-19 23:06:47 +03:00
if ( ! cli_session_setup ( & cli , user_name , old_passwd , strlen ( old_passwd ) ,
" " , 0 , " " ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: machine %s rejected the session setup. Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
cli_shutdown ( & cli ) ;
exit ( 1 ) ;
}
if ( ! cli_send_tconX ( & cli , " IPC$ " , " IPC " , " " , 1 ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: machine %s rejected the tconX on the IPC$ share. Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
cli_shutdown ( & cli ) ;
exit ( 1 ) ;
}
if ( ! cli_oem_change_password ( & cli , user_name , new_passwd , old_passwd ) ) {
1998-03-26 22:11:31 +03:00
fprintf ( stderr , " %s: machine %s rejected the password change: Error was : %s. \n " ,
prog_name , remote_machine , get_error_message ( & cli ) ) ;
1998-03-19 23:06:47 +03:00
cli_shutdown ( & cli ) ;
exit ( 1 ) ;
}
cli_shutdown ( & cli ) ;
exit ( 0 ) ;
}
1998-03-24 03:37:53 +03:00
/*
* Check for a machine account flag - make sure the username ends in
* a ' $ ' etc . . . .
*/
if ( machine_account ) {
int username_len = strlen ( user_name ) ;
if ( username_len > = sizeof ( pstring ) - 1 ) {
fprintf ( stderr , " %s: machine account name too long. \n " , user_name ) ;
exit ( 1 ) ;
}
if ( user_name [ username_len ] ! = ' $ ' ) {
user_name [ username_len ] = ' $ ' ;
user_name [ username_len + 1 ] = ' \0 ' ;
1998-03-25 00:04:36 +03:00
}
/*
* Setup the pwd struct to point to known
* values for a machine account ( it doesn ' t
* exist in / etc / passwd ) .
*/
pwd = & machine_account_pwd ;
pwd - > pw_name = user_name ;
1998-03-26 22:11:31 +03:00
sprintf ( machine_dir_name , " Workstation machine account for %s " , user_name ) ;
1998-03-25 00:44:49 +03:00
pwd - > pw_gecos = " " ;
pwd - > pw_dir = machine_dir_name ;
1998-03-25 00:04:36 +03:00
pwd - > pw_shell = " " ;
1998-03-26 05:14:40 +03:00
pwd - > pw_uid = get_new_machine_uid ( ) ;
1998-03-25 00:04:36 +03:00
1998-03-24 03:37:53 +03:00
}
1998-03-25 00:04:36 +03:00
/* Calculate the MD4 hash (NT compatible) of the new password. */
1996-05-04 11:50:46 +04:00
memset ( new_nt_p16 , ' \0 ' , 16 ) ;
E_md4hash ( ( uchar * ) new_passwd , new_nt_p16 ) ;
1998-03-25 00:04:36 +03:00
/* Mangle the password into Lanman format */
1996-05-04 11:50:46 +04:00
new_passwd [ 14 ] = ' \0 ' ;
strupper ( new_passwd ) ;
/*
1998-03-25 00:04:36 +03:00
* Calculate the SMB ( lanman ) hash functions of the new password .
1996-05-04 11:50:46 +04:00
*/
memset ( new_p16 , ' \0 ' , 16 ) ;
E_P16 ( ( uchar * ) new_passwd , new_p16 ) ;
/*
* Open the smbpaswd file XXXX - we need to parse smb . conf to get the
* filename
*/
1997-09-15 10:36:55 +04:00
fp = fopen ( pfile , " r+ " ) ;
if ( ! fp & & errno = = ENOENT ) {
fp = fopen ( pfile , " w " ) ;
if ( fp ) {
fprintf ( fp , " # Samba SMB password file \n " ) ;
fclose ( fp ) ;
fp = fopen ( pfile , " r+ " ) ;
}
}
if ( ! fp ) {
err = errno ;
fprintf ( stderr , " %s: Failed to open password file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1997-09-15 10:36:55 +04:00
errno = err ;
1998-03-19 23:06:47 +03:00
perror ( prog_name ) ;
1997-09-15 10:36:55 +04:00
exit ( err ) ;
1996-05-04 11:50:46 +04:00
}
1997-09-15 09:43:37 +04:00
1996-05-04 11:50:46 +04:00
/* Set read buffer to 16k for effiecient reads */
setvbuf ( fp , readbuf , _IOFBF , sizeof ( readbuf ) ) ;
1997-10-13 17:49:50 +04:00
/* make sure it is only rw by the owner */
chmod ( pfile , 0600 ) ;
1997-10-13 16:21:56 +04:00
1996-05-04 11:50:46 +04:00
/* Lock the smbpasswd file for write. */
1998-03-12 00:11:04 +03:00
if ( ( lockfd = pw_file_lock ( fileno ( fp ) , F_WRLCK , 5 ) ) < 0 ) {
1996-05-04 11:50:46 +04:00
err = errno ;
fprintf ( stderr , " %s: Failed to lock password file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1996-05-04 11:50:46 +04:00
fclose ( fp ) ;
errno = err ;
1998-03-19 23:06:47 +03:00
perror ( prog_name ) ;
1996-05-04 11:50:46 +04:00
exit ( err ) ;
}
1998-03-25 00:04:36 +03:00
1996-05-04 11:50:46 +04:00
/* Get the smb passwd entry for this user */
1998-03-19 23:06:47 +03:00
smb_pwent = _my_get_smbpwnam ( fp , user_name , & valid_old_pwd ,
1998-04-16 00:00:41 +04:00
& got_valid_nt_entry , & got_valid_last_change_time , & seekpos ) ;
1996-05-04 11:50:46 +04:00
if ( smb_pwent = = NULL ) {
1997-07-11 00:15:48 +04:00
if ( add_user = = False ) {
fprintf ( stderr , " %s: Failed to find entry for user %s in file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pwd - > pw_name , pfile ) ;
1996-05-04 11:50:46 +04:00
fclose ( fp ) ;
pw_file_unlock ( lockfd ) ;
exit ( 1 ) ;
}
1997-07-11 00:15:48 +04:00
/* Create a new smb passwd entry and set it to the given password. */
{
int fd ;
int new_entry_length ;
char * new_entry ;
long offpos ;
1998-03-26 22:11:31 +03:00
uint16 acct_ctrl = ( machine_account ? ACB_WSTRUST : ACB_NORMAL ) ;
1997-07-11 00:15:48 +04:00
/* The add user write needs to be atomic - so get the fd from
the fp and do a raw write ( ) call .
*/
fd = fileno ( fp ) ;
if ( ( offpos = lseek ( fd , 0 , SEEK_END ) ) = = - 1 ) {
fprintf ( stderr , " %s: Failed to add entry for user %s to file %s. \
1998-03-24 03:37:53 +03:00
Error was % s \ n " , prog_name, user_name, pfile, strerror(errno));
1997-07-11 00:15:48 +04:00
fclose ( fp ) ;
pw_file_unlock ( lockfd ) ;
exit ( 1 ) ;
}
1998-03-24 03:37:53 +03:00
new_entry_length = strlen ( user_name ) + 1 + 15 + 1 +
1998-03-25 00:44:49 +03:00
32 + 1 + 32 + 1 + sizeof ( fstring ) +
1998-04-16 00:00:41 +04:00
1 + 13 +
1997-07-11 00:15:48 +04:00
1 + strlen ( pwd - > pw_dir ) + 1 +
strlen ( pwd - > pw_shell ) + 1 ;
if ( ( new_entry = ( char * ) malloc ( new_entry_length ) ) = = 0 ) {
fprintf ( stderr , " %s: Failed to add entry for user %s to file %s. \
1998-03-19 23:06:47 +03:00
Error was % s \ n " , prog_name, pwd->pw_name, pfile, strerror(errno));
1997-07-11 00:15:48 +04:00
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1997-07-11 00:15:48 +04:00
exit ( 1 ) ;
}
1997-10-28 17:55:47 +03:00
sprintf ( new_entry , " %s:%u: " , pwd - > pw_name , ( unsigned ) pwd - > pw_uid ) ;
1997-07-11 00:15:48 +04:00
p = & new_entry [ strlen ( new_entry ) ] ;
1998-03-25 00:04:36 +03:00
if ( disable_user ) {
memcpy ( p , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
p + = 32 ;
* p + + = ' : ' ;
memcpy ( p , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else if ( set_no_password ) {
memcpy ( p , " NO PASSWORDXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
p + = 32 ;
* p + + = ' : ' ;
memcpy ( p , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else {
for ( i = 0 ; i < 16 ; i + + )
sprintf ( & p [ i * 2 ] , " %02X " , new_p16 [ i ] ) ;
p + = 32 ;
* p + + = ' : ' ;
for ( i = 0 ; i < 16 ; i + + )
sprintf ( & p [ i * 2 ] , " %02X " , new_nt_p16 [ i ] ) ;
}
1997-07-11 00:15:48 +04:00
p + = 32 ;
* p + + = ' : ' ;
1998-04-16 00:00:41 +04:00
sprintf ( p , " %s:TLC-%08X:%s:%s \n " , encode_acct_ctrl ( acct_ctrl ) ,
( uint32 ) time ( NULL ) , pwd - > pw_dir , pwd - > pw_shell ) ;
1997-07-11 00:15:48 +04:00
if ( write ( fd , new_entry , strlen ( new_entry ) ) ! = strlen ( new_entry ) ) {
fprintf ( stderr , " %s: Failed to add entry for user %s to file %s. \
1998-03-19 23:06:47 +03:00
Error was % s \ n " , prog_name, pwd->pw_name, pfile, strerror(errno));
1997-07-11 00:15:48 +04:00
/* Remove the entry we just wrote. */
if ( ftruncate ( fd , offpos ) = = - 1 ) {
fprintf ( stderr , " %s: ERROR failed to ftruncate file %s. \
1997-09-15 09:43:37 +04:00
Error was % s . Password file may be corrupt ! Please examine by hand ! \ n " ,
1998-03-19 23:06:47 +03:00
prog_name , pwd - > pw_name , strerror ( errno ) ) ;
1997-07-11 00:15:48 +04:00
}
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1997-07-11 00:15:48 +04:00
exit ( 1 ) ;
}
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1997-07-11 00:15:48 +04:00
exit ( 0 ) ;
}
1997-09-15 10:36:55 +04:00
} else {
/* the entry already existed */
add_user = False ;
1997-07-11 00:15:48 +04:00
}
1996-05-04 11:50:46 +04:00
/*
1998-04-16 00:00:41 +04:00
* We are root - just write the new password
* and the valid last change time .
1996-05-04 11:50:46 +04:00
*/
1998-03-19 23:06:47 +03:00
1996-05-04 11:50:46 +04:00
/* Create the 32 byte representation of the new p16 */
1998-03-25 00:04:36 +03:00
if ( disable_user ) {
memcpy ( ascii_p16 , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else if ( set_no_password ) {
memcpy ( ascii_p16 , " NO PASSWORDXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else {
for ( i = 0 ; i < 16 ; i + + ) {
sprintf ( & ascii_p16 [ i * 2 ] , " %02X " , ( uchar ) new_p16 [ i ] ) ;
}
1996-05-04 11:50:46 +04:00
}
if ( got_valid_nt_entry ) {
/* Add on the NT md4 hash */
ascii_p16 [ 32 ] = ' : ' ;
1998-03-25 00:04:36 +03:00
if ( disable_user ) {
memcpy ( & ascii_p16 [ 33 ] , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else if ( set_no_password ) {
memcpy ( & ascii_p16 [ 33 ] , " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " , 32 ) ;
} else {
for ( i = 0 ; i < 16 ; i + + ) {
sprintf ( & ascii_p16 [ ( i * 2 ) + 33 ] , " %02X " , ( uchar ) new_nt_p16 [ i ] ) ;
}
1996-05-04 11:50:46 +04:00
}
}
1998-04-16 00:00:41 +04:00
ascii_p16 [ 65 ] = ' : ' ;
ascii_p16 [ 66 ] = ' \0 ' ;
if ( got_valid_last_change_time ) {
strcpy ( & ascii_p16 [ 66 ] , encode_acct_ctrl ( smb_pwent - > acct_ctrl ) ) ;
sprintf ( & ascii_p16 [ strlen ( ascii_p16 ) ] , " :TLC-%08X " , ( uint32 ) time ( NULL ) ) ;
}
1996-05-04 11:50:46 +04:00
/*
* Do an atomic write into the file at the position defined by
* seekpos .
*/
pwfd = fileno ( fp ) ;
ret = lseek ( pwfd , seekpos - 1 , SEEK_SET ) ;
if ( ret ! = seekpos - 1 ) {
err = errno ;
fprintf ( stderr , " %s: seek fail on file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1996-05-04 11:50:46 +04:00
errno = err ;
1998-03-19 23:06:47 +03:00
perror ( prog_name ) ;
1996-05-04 11:50:46 +04:00
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1996-05-04 11:50:46 +04:00
exit ( 1 ) ;
}
/* Sanity check - ensure the character is a ':' */
if ( read ( pwfd , & c , 1 ) ! = 1 ) {
err = errno ;
fprintf ( stderr , " %s: read fail on file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1996-05-04 11:50:46 +04:00
errno = err ;
1998-03-19 23:06:47 +03:00
perror ( prog_name ) ;
1996-05-04 11:50:46 +04:00
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1996-05-04 11:50:46 +04:00
exit ( 1 ) ;
}
if ( c ! = ' : ' ) {
fprintf ( stderr , " %s: sanity check on passwd file %s failed. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1996-05-04 11:50:46 +04:00
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1996-05-04 11:50:46 +04:00
exit ( 1 ) ;
}
1998-04-16 00:00:41 +04:00
writelen = ( got_valid_nt_entry ) ?
( got_valid_last_change_time ? strlen ( ascii_p16 ) : 65 ) : 32 ;
1996-05-04 11:50:46 +04:00
if ( write ( pwfd , ascii_p16 , writelen ) ! = writelen ) {
err = errno ;
fprintf ( stderr , " %s: write fail in file %s. \n " ,
1998-03-19 23:06:47 +03:00
prog_name , pfile ) ;
1996-05-04 11:50:46 +04:00
errno = err ;
1998-03-19 23:06:47 +03:00
perror ( prog_name ) ;
1996-05-04 11:50:46 +04:00
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
1996-05-04 11:50:46 +04:00
exit ( err ) ;
}
pw_file_unlock ( lockfd ) ;
1998-03-25 00:04:36 +03:00
fclose ( fp ) ;
if ( disable_user )
printf ( " User %s disabled. \n " , user_name ) ;
else if ( set_no_password )
printf ( " User %s - set to no password. \n " , user_name ) ;
else
printf ( " Password changed for user %s. \n " , user_name ) ;
1996-05-04 11:50:46 +04:00
return 0 ;
}