1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/utils/smbpasswd.c

658 lines
15 KiB
C
Raw Normal View History

/*
* Unix SMB/Netbios implementation.
* Copyright (C) Jeremy Allison 1995-1998
* Copyright (C) Tim Potter 2001
*
* 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. */
#include "includes.h"
extern pstring global_myname;
/*
* Next two lines needed for SunOS and don't
* hurt anything else...
*/
extern char *optarg;
extern int optind;
/** forced running in root-mode **/
static BOOL local_mode;
/**
* Print command usage on stderr and die.
**/
static void usage(void)
{
printf("When run by root:\n");
printf(" smbpasswd [options] [username] [password]\n");
printf("otherwise:\n");
printf(" smbpasswd [options] [password]\n\n");
printf("options:\n");
printf(" -s use stdin for password prompt\n");
printf(" -D LEVEL debug level\n");
printf(" -U USER remote username\n");
printf(" -r MACHINE remote machine\n");
printf("extra options when run by root or in local mode:\n");
printf(" -L local mode (must be first option)\n");
printf(" -R ORDER name resolve order\n");
printf(" -j DOMAIN join domain name\n");
printf(" -a add user\n");
printf(" -x delete user\n");
printf(" -d disable user\n");
printf(" -e enable user\n");
printf(" -n set no password\n");
printf(" -m machine trust account\n");
exit(1);
}
/*********************************************************
Join a domain.
**********************************************************/
static int join_domain(char *domain, char *remote)
{
pstring remote_machine;
fstring trust_passwd;
unsigned char orig_trust_passwd_hash[16];
BOOL ret;
pstrcpy(remote_machine, remote ? remote : "");
fstrcpy(trust_passwd, global_myname);
strlower(trust_passwd);
E_md4hash( (uchar *)trust_passwd, orig_trust_passwd_hash);
/* Ensure that we are not trying to join a
domain if we are locally set up as a domain
controller. */
if(strequal(remote, global_myname)) {
fprintf(stderr, "Cannot join domain %s as the domain controller name is our own. We cannot be a domain controller for a domain and also be a domain member.\n", domain);
return 1;
}
/*
* Write the old machine account password.
*/
if(!secrets_store_trust_account_password(domain, orig_trust_passwd_hash)) {
fprintf(stderr, "Unable to write the machine account password for \
machine %s in domain %s.\n", global_myname, domain);
return 1;
}
/*
* If we are given a remote machine assume this is the PDC.
*/
if(remote == NULL) {
pstrcpy(remote_machine, lp_passwordserver());
}
if(!*remote_machine) {
fprintf(stderr, "No password server list given in smb.conf - \
unable to join domain.\n");
return 1;
}
ret = change_trust_account_password( domain, remote_machine);
if(!ret) {
trust_password_delete(domain);
fprintf(stderr,"Unable to join domain %s.\n",domain);
} else {
printf("Joined domain %s.\n",domain);
}
return (int)ret;
}
static void set_line_buffering(FILE *f)
{
setvbuf(f, NULL, _IOLBF, 0);
}
/*************************************************************
Utility function to prompt for passwords from stdin. Each
password entered must end with a newline.
*************************************************************/
static char *stdin_new_passwd(void)
{
static fstring new_passwd;
size_t len;
ZERO_ARRAY(new_passwd);
/*
* if no error is reported from fgets() and string at least contains
* the newline that ends the password, then replace the newline with
* a null terminator.
*/
if ( fgets(new_passwd, sizeof(new_passwd), stdin) != NULL) {
if ((len = strlen(new_passwd)) > 0) {
if(new_passwd[len-1] == '\n')
new_passwd[len - 1] = 0;
}
}
return(new_passwd);
}
/*************************************************************
Utility function to get passwords via tty or stdin
Used if the '-s' option is set to silently get passwords
to enable scripting.
*************************************************************/
static char *get_pass( char *prompt, BOOL stdin_get)
{
char *p;
if (stdin_get) {
p = stdin_new_passwd();
} else {
p = getpass(prompt);
}
return smb_xstrdup(p);
}
/*************************************************************
Utility function to prompt for new password.
*************************************************************/
static char *prompt_for_new_password(BOOL stdin_get)
{
char *p;
fstring new_passwd;
ZERO_ARRAY(new_passwd);
p = get_pass("New SMB password:", stdin_get);
fstrcpy(new_passwd, p);
SAFE_FREE(p);
p = get_pass("Retype new SMB password:", stdin_get);
if (strcmp(p, new_passwd)) {
fprintf(stderr, "Mismatch - password unchanged.\n");
ZERO_ARRAY(new_passwd);
SAFE_FREE(p);
return NULL;
}
return p;
}
/*************************************************************
Change a password either locally or remotely.
*************************************************************/
static BOOL password_change(const char *remote_machine, char *user_name,
char *old_passwd, char *new_passwd, int local_flags)
{
BOOL ret;
pstring err_str;
pstring msg_str;
if (remote_machine != NULL) {
if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
/* these things can't be done remotely yet */
return False;
}
ret = remote_password_change(remote_machine, user_name,
Fix up a number of intertwined issues: The big one is a global change to allow us to NULLify the free'ed pointer to a former passdb object. This was done to allow idra's SAFE_FREE() macro to do its magic, and to satisfy the input test in pdb_init_sam() for a NULL pointer to start with. This NULL pointer test was what was breaking the adding of accounts up until now, and this code has been reworked to avoid duplicating work - I hope this will avoid a similar mess-up in future. Finally, I fixed a few nasty bugs where the pdb_ fuctions's return codes were being ignored. Some of these functions malloc() and are permitted to fail. Also, this caught a nasty bug where pdb_set_lanman_password(sam, NULL) acheived precisely didilly-squat, just returning False. Now that we check the returns this bug was spotted. This could allow different LM and NT passwords. - the pdbedit code needs to start checking these too, but I havn't had a chance to fix it. I have also fixed up where some of the password changing code was using the pdb_set functions to store *internal* data. I assume this is from a previous lot of mass conversion work... Most likally (and going on past experience) I have missed somthing, probably in the LanMan password change code which I havn't yet been able to test, but this lot is in much better shape than it was before. If all this is too much to swallow (particularly for 2.2.2) then just adding a sam_pass = NULL to the particular line of passdb.c should do the trick for the ovbious bug. Andrew Bartlett (This used to be commit 762c8758a7869809d89b4da9c2a5249678942930)
2001-09-29 17:08:26 +04:00
old_passwd, new_passwd, err_str, sizeof(err_str));
if(*err_str)
fprintf(stderr, err_str);
return ret;
}
ret = local_password_change(user_name, local_flags, new_passwd,
err_str, sizeof(err_str), msg_str, sizeof(msg_str));
if(*msg_str)
printf(msg_str);
if(*err_str)
fprintf(stderr, err_str);
return ret;
}
/*************************************************************
Handle password changing for root.
*************************************************************/
static int process_root(int argc, char *argv[])
{
struct passwd *pwd;
int result = 0, ch;
BOOL joining_domain = False, got_pass = False, got_username = False;
int local_flags = LOCAL_SET_PASSWORD;
BOOL stdin_passwd_get = False;
fstring user_name, user_password;
char *new_domain = NULL;
char *new_passwd = NULL;
char *old_passwd = NULL;
char *remote_machine = NULL;
ZERO_STRUCT(user_name);
ZERO_STRUCT(user_password);
user_name[0] = '\0';
while ((ch = getopt(argc, argv, "axdehmnj:r:sR:D:U:L")) != EOF) {
switch(ch) {
case 'L':
local_mode = True;
break;
case 'a':
local_flags |= LOCAL_ADD_USER;
break;
case 'x':
local_flags |= LOCAL_DELETE_USER;
local_flags &= ~LOCAL_SET_PASSWORD;
break;
case 'd':
local_flags |= LOCAL_DISABLE_USER;
local_flags &= ~LOCAL_SET_PASSWORD;
break;
case 'e':
local_flags |= LOCAL_ENABLE_USER;
local_flags &= ~LOCAL_SET_PASSWORD;
break;
case 'm':
local_flags |= LOCAL_TRUST_ACCOUNT;
break;
case 'n':
local_flags |= LOCAL_SET_NO_PASSWORD;
local_flags &= ~LOCAL_SET_PASSWORD;
break;
case 'j':
new_domain = optarg;
strupper(new_domain);
joining_domain = True;
break;
case 'r':
remote_machine = optarg;
break;
case 's':
set_line_buffering(stdin);
set_line_buffering(stdout);
set_line_buffering(stderr);
stdin_passwd_get = True;
break;
case 'R':
lp_set_name_resolve_order(optarg);
break;
case 'D':
DEBUGLEVEL = atoi(optarg);
break;
case 'U': {
char *lp;
got_username = True;
fstrcpy(user_name, optarg);
if ((lp = strchr_m(user_name, '%'))) {
*lp = 0;
fstrcpy(user_password, lp + 1);
got_pass = True;
memset(strchr_m(optarg, '%') + 1, 'X',
strlen(user_password));
}
break;
}
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
/*
* Ensure both add/delete user are not set
* Ensure add/delete user and either remote machine or join domain are
* not both set.
*/
if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) ||
((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) &&
((remote_machine != NULL) || joining_domain))) {
usage();
}
/* Only load interfaces if we are doing network operations. */
if (joining_domain || remote_machine) {
load_interfaces();
}
/* Join a domain */
if (joining_domain) {
if (argc != 0)
usage();
/* Are we joining by specifing an admin username and
password? */
if (user_name[0]) {
/* Get administrator password if not specified */
if (!got_pass) {
char *pass = getpass("Password: ");
if (pass)
pstrcpy(user_password, pass);
}
d_printf("use net rpc join to do this now.\n");
return 1;
} else {
/* Or just with the server manager? */
return join_domain(new_domain, remote_machine);
}
}
/*
* Deal with root - can add a user, but only locally.
*/
switch(argc) {
case 0:
if (!got_username)
fstrcpy(user_name, "");
break;
case 1:
if (got_username)
usage();
fstrcpy(user_name, argv[0]);
break;
case 2:
if (got_username || got_pass)
usage();
fstrcpy(user_name, argv[0]);
new_passwd = smb_xstrdup(argv[1]);
break;
default:
usage();
}
Fix up a number of intertwined issues: The big one is a global change to allow us to NULLify the free'ed pointer to a former passdb object. This was done to allow idra's SAFE_FREE() macro to do its magic, and to satisfy the input test in pdb_init_sam() for a NULL pointer to start with. This NULL pointer test was what was breaking the adding of accounts up until now, and this code has been reworked to avoid duplicating work - I hope this will avoid a similar mess-up in future. Finally, I fixed a few nasty bugs where the pdb_ fuctions's return codes were being ignored. Some of these functions malloc() and are permitted to fail. Also, this caught a nasty bug where pdb_set_lanman_password(sam, NULL) acheived precisely didilly-squat, just returning False. Now that we check the returns this bug was spotted. This could allow different LM and NT passwords. - the pdbedit code needs to start checking these too, but I havn't had a chance to fix it. I have also fixed up where some of the password changing code was using the pdb_set functions to store *internal* data. I assume this is from a previous lot of mass conversion work... Most likally (and going on past experience) I have missed somthing, probably in the LanMan password change code which I havn't yet been able to test, but this lot is in much better shape than it was before. If all this is too much to swallow (particularly for 2.2.2) then just adding a sam_pass = NULL to the particular line of passdb.c should do the trick for the ovbious bug. Andrew Bartlett (This used to be commit 762c8758a7869809d89b4da9c2a5249678942930)
2001-09-29 17:08:26 +04:00
if (!user_name[0] && (pwd = sys_getpwuid(geteuid()))) {
fstrcpy(user_name, pwd->pw_name);
}
if (!user_name[0]) {
fprintf(stderr,"You must specify a username\n");
exit(1);
}
if (local_flags & LOCAL_TRUST_ACCOUNT) {
/* add the $ automatically */
static fstring buf;
/*
* Remove any trailing '$' before we
* generate the initial machine password.
*/
if (user_name[strlen(user_name)-1] == '$') {
user_name[strlen(user_name)-1] = 0;
}
if (local_flags & LOCAL_ADD_USER) {
SAFE_FREE(new_passwd);
new_passwd = smb_xstrdup(user_name);
strlower(new_passwd);
}
/*
* Now ensure the username ends in '$' for
* the machine add.
*/
slprintf(buf, sizeof(buf)-1, "%s$", user_name);
fstrcpy(user_name, buf);
}
if (remote_machine != NULL) {
old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
}
if (!(local_flags & LOCAL_SET_PASSWORD)) {
/*
* If we are trying to enable a user, first we need to find out
* if they are using a modern version of the smbpasswd file that
* disables a user by just writing a flag into the file. If so
* then we can re-enable a user without prompting for a new
* password. If not (ie. they have a no stored password in the
* smbpasswd file) then we need to prompt for a new password.
*/
if(local_flags & LOCAL_ENABLE_USER) {
SAM_ACCOUNT *sampass = NULL;
BOOL ret;
pdb_init_sam(&sampass);
ret = pdb_getsampwnam(sampass, user_name);
if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) {
local_flags |= LOCAL_SET_PASSWORD;
}
Fix up a number of intertwined issues: The big one is a global change to allow us to NULLify the free'ed pointer to a former passdb object. This was done to allow idra's SAFE_FREE() macro to do its magic, and to satisfy the input test in pdb_init_sam() for a NULL pointer to start with. This NULL pointer test was what was breaking the adding of accounts up until now, and this code has been reworked to avoid duplicating work - I hope this will avoid a similar mess-up in future. Finally, I fixed a few nasty bugs where the pdb_ fuctions's return codes were being ignored. Some of these functions malloc() and are permitted to fail. Also, this caught a nasty bug where pdb_set_lanman_password(sam, NULL) acheived precisely didilly-squat, just returning False. Now that we check the returns this bug was spotted. This could allow different LM and NT passwords. - the pdbedit code needs to start checking these too, but I havn't had a chance to fix it. I have also fixed up where some of the password changing code was using the pdb_set functions to store *internal* data. I assume this is from a previous lot of mass conversion work... Most likally (and going on past experience) I have missed somthing, probably in the LanMan password change code which I havn't yet been able to test, but this lot is in much better shape than it was before. If all this is too much to swallow (particularly for 2.2.2) then just adding a sam_pass = NULL to the particular line of passdb.c should do the trick for the ovbious bug. Andrew Bartlett (This used to be commit 762c8758a7869809d89b4da9c2a5249678942930)
2001-09-29 17:08:26 +04:00
pdb_free_sam(&sampass);
}
}
if(local_flags & LOCAL_SET_PASSWORD) {
new_passwd = prompt_for_new_password(stdin_passwd_get);
if(!new_passwd) {
fprintf(stderr, "Unable to get new password.\n");
exit(1);
}
}
if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) {
fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
result = 1;
goto done;
}
if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
SAM_ACCOUNT *sampass = NULL;
BOOL ret;
pdb_init_sam(&sampass);
ret = pdb_getsampwnam(sampass, user_name);
printf("Password changed for user %s.", user_name );
if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) )
printf(" User has disabled flag set.");
if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) )
printf(" User has no password flag set.");
printf("\n");
Fix up a number of intertwined issues: The big one is a global change to allow us to NULLify the free'ed pointer to a former passdb object. This was done to allow idra's SAFE_FREE() macro to do its magic, and to satisfy the input test in pdb_init_sam() for a NULL pointer to start with. This NULL pointer test was what was breaking the adding of accounts up until now, and this code has been reworked to avoid duplicating work - I hope this will avoid a similar mess-up in future. Finally, I fixed a few nasty bugs where the pdb_ fuctions's return codes were being ignored. Some of these functions malloc() and are permitted to fail. Also, this caught a nasty bug where pdb_set_lanman_password(sam, NULL) acheived precisely didilly-squat, just returning False. Now that we check the returns this bug was spotted. This could allow different LM and NT passwords. - the pdbedit code needs to start checking these too, but I havn't had a chance to fix it. I have also fixed up where some of the password changing code was using the pdb_set functions to store *internal* data. I assume this is from a previous lot of mass conversion work... Most likally (and going on past experience) I have missed somthing, probably in the LanMan password change code which I havn't yet been able to test, but this lot is in much better shape than it was before. If all this is too much to swallow (particularly for 2.2.2) then just adding a sam_pass = NULL to the particular line of passdb.c should do the trick for the ovbious bug. Andrew Bartlett (This used to be commit 762c8758a7869809d89b4da9c2a5249678942930)
2001-09-29 17:08:26 +04:00
pdb_free_sam(&sampass);
}
done:
SAFE_FREE(new_passwd);
return result;
}
/**
handle password changing for non-root
**/
static int process_nonroot(int argc, char *argv[])
{
struct passwd *pwd = NULL;
int result = 0, ch;
BOOL stdin_passwd_get = False;
char *old_passwd = NULL;
char *remote_machine = NULL;
char *user_name = NULL;
char *new_passwd = NULL;
while ((ch = getopt(argc, argv, "hD:r:sU:")) != EOF) {
switch(ch) {
case 'D':
DEBUGLEVEL = atoi(optarg);
break;
case 'r':
remote_machine = optarg;
break;
case 's':
set_line_buffering(stdin);
set_line_buffering(stdout);
set_line_buffering(stderr);
stdin_passwd_get = True;
break;
case 'U':
user_name = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if(argc > 1) {
usage();
}
if (argc == 1) {
new_passwd = argv[0];
}
if (!user_name) {
pwd = sys_getpwuid(getuid());
if (pwd) {
user_name = smb_xstrdup(pwd->pw_name);
} else {
fprintf(stderr, "smbpasswd: you don't exist - go away\n");
exit(1);
}
}
/*
* A non-root user is always setting a password
* via a remote machine (even if that machine is
* localhost).
*/
load_interfaces(); /* Delayed from main() */
if (remote_machine == NULL) {
remote_machine = "127.0.0.1";
}
if (remote_machine != NULL) {
old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
}
if (!new_passwd) {
new_passwd = prompt_for_new_password(stdin_passwd_get);
}
if (!new_passwd) {
fprintf(stderr, "Unable to get new password.\n");
exit(1);
}
if (!password_change(remote_machine, user_name, old_passwd, new_passwd, 0)) {
fprintf(stderr,"Failed to change password for %s\n", user_name);
result = 1;
goto done;
}
printf("Password changed for user %s\n", user_name);
done:
SAFE_FREE(old_passwd);
SAFE_FREE(new_passwd);
return result;
}
/*********************************************************
Start here.
**********************************************************/
int main(int argc, char **argv)
{
#if defined(HAVE_SET_AUTH_PARAMETERS)
set_auth_parameters(argc, argv);
#endif /* HAVE_SET_AUTH_PARAMETERS */
setup_logging("smbpasswd", True);
Large commit which restructures the local password storage API. Currently the only backend which works is smbpasswd (tdb, LDAP, and NIS+) are broken, but they were somewhat broken before. :) The following functions implement the storage manipulation interface /*The following definitions come from passdb/pdb_smbpasswd.c */ BOOL pdb_setsampwent (BOOL update); void pdb_endsampwent (void); SAM_ACCOUNT* pdb_getsampwent (void); SAM_ACCOUNT* pdb_getsampwnam (char *username); SAM_ACCOUNT* pdb_getsampwuid (uid_t uid); SAM_ACCOUNT* pdb_getsampwrid (uint32 rid); BOOL pdb_add_sam_account (SAM_ACCOUNT *sampass); BOOL pdb_update_sam_account (SAM_ACCOUNT *sampass, BOOL override); BOOL pdb_delete_sam_account (char* username); There is also a host of pdb_set..() and pdb_get..() functions for manipulating SAM_ACCOUNT struct members. Note that the struct passdb_ops {} has gone away. Also notice that struct smb_passwd (formally in smb.h) has been moved to passdb/pdb_smbpasswd.c and is not accessed outisde of static internal functions in this file. All local password searches should make use of the the SAM_ACCOUNT struct and the previously mentioned functions. I'll write some documentation for this later. The next step is to fix the TDB passdb backend, then work on spliting the backends out into share libraries, and finally get the LDAP backend going. What works and may not: o domain logons from Win9x works o domain logons from WinNT 4 works o user and group enumeration as implemented by Tim works o file and print access works o changing password from Win9x & NT ummm...i'll fix this tonight :) If I broke anything else, just yell and I'll fix it. I think it should be fairly quite. -- jerry (This used to be commit 0b92d0838ebdbe24f34f17e313ecbf61a0301389)
2000-11-14 02:03:34 +03:00
if(!initialize_password_db(True)) {
fprintf(stderr, "Can't setup password database vectors.\n");
exit(1);
}
if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
fprintf(stderr, "Can't load %s - run testparm to debug it\n",
dyn_CONFIGFILE);
exit(1);
}
/*
* Set the machine NETBIOS name if not already
* set from the config file.
*/
if (!*global_myname) {
char *p;
fstrcpy(global_myname, myhostname());
p = strchr_m(global_myname, '.' );
if (p) *p = 0;
}
strupper(global_myname);
/* Check the effective uid - make sure we are not setuid */
if (is_setuid_root()) {
fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
exit(1);
}
/* pre-check for local mode option as first option. We can't
do this via normal getopt as getopt can't be called
twice. */
if (argc > 1 && strcmp(argv[1], "-L") == 0) {
local_mode = True;
}
if (local_mode || getuid() == 0) {
secrets_init();
return process_root(argc, argv);
}
return process_nonroot(argc, argv);
}