mirror of
https://github.com/samba-team/samba.git
synced 2025-02-23 09:57:40 +03:00
Samba now features a pluggable passdb interface, along the same lines as the one in use in the auth subsystem. In this case, only one backend may be active at a time by the 'normal' interface, and only one backend per passdb_context is permitted outside that. This pluggable interface is designed to allow any number of passdb backends to be compiled in, with the selection at runtime. The 'passdb backend' paramater has been created (and documented!) to support this. As such, configure has been modfied to allow (for example) --with-ldap and the old smbpasswd to be selected at the same time. This patch also introduces two new backends: smbpasswd_nua and tdbsam_nua. These two backends accept 'non unix accounts', where the user does *not* exist in /etc/passwd. These accounts' don't have UIDs in the unix sense, but to avoid conflicts in the algroitmic mapping of RIDs, they use the values specified in the 'non unix account range' paramter - in the same way as the winbind ranges are specifed. While I was at it, I cleaned up some of the code in pdb_tdb (code copied directly from smbpasswd and not really considered properly). Most of this was to do with % macro expansion on stored data. It isn't easy to get the macros into the tdb, and the first password change will 'expand' them. tdbsam needs to use a similar system to pdb_ldap in this regard. This patch only makes minor adjustments to pdb_nisplus and pdb_ldap, becouse I don't have the test facilities for these. I plan to incoroprate at least pdb_ldap into this scheme after consultation with Jerry. Each (converted) passdb module now no longer has any 'static' variables, and only exports 1 init function outside its .c file. The non-unix-account support in this patch has been proven! It is now possible to join a win2k machine to a Samba PDC without an account in /etc/passwd! Other changes: Minor interface adjustments: pdb_delete_sam_account() now takes a SAM_ACCOUNT, not a char*. pdb_update_sam_account() no longer takes the 'override' argument that was being ignored so often (every other passdb backend). Extra checks have been added in some places. Minor code changes: smbpasswd no longer attempts to initialise the passdb at startup, this is now done on first use. pdbedit has lost some of its 'machine account' logic, as this behaviour is now controlled by the passdb subsystem directly. The samr subsystem no longer calls 'local password change', but does the pdb interactions directly. This allow the ACB_ flags specifed to be transferred direct to the backend, without interference. Doco: I've updated the doco to reflect some of the changes, and removed some paramters no longer applicable to HEAD.
-
617 lines
15 KiB
C
617 lines
15 KiB
C
/*
|
|
* 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;
|
|
extern BOOL AllowDebugChange;
|
|
|
|
/*
|
|
* 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(" -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");
|
|
printf(" -i interdomain trust account\n");
|
|
#ifdef WITH_LDAP_SAM
|
|
printf(" -w ldap admin password\n");
|
|
#endif
|
|
|
|
exit(1);
|
|
}
|
|
|
|
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,
|
|
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;
|
|
}
|
|
|
|
#ifdef WITH_LDAP_SAM
|
|
/*******************************************************************
|
|
Store the LDAP admin password in secrets.tdb
|
|
******************************************************************/
|
|
static BOOL store_ldap_admin_pw (char* pw)
|
|
{
|
|
if (!pw)
|
|
return False;
|
|
|
|
if (!secrets_init())
|
|
return False;
|
|
|
|
return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************
|
|
Handle password changing for root.
|
|
*************************************************************/
|
|
|
|
static int process_root(int argc, char *argv[])
|
|
{
|
|
struct passwd *pwd;
|
|
int result = 0, ch;
|
|
BOOL got_pass = False, got_username = False;
|
|
int local_flags = LOCAL_SET_PASSWORD;
|
|
BOOL stdin_passwd_get = False;
|
|
fstring user_name, user_password;
|
|
char *new_passwd = NULL;
|
|
char *old_passwd = NULL;
|
|
char *remote_machine = NULL;
|
|
#ifdef WITH_LDAP_SAM
|
|
fstring ldap_secret;
|
|
#endif
|
|
|
|
ZERO_STRUCT(user_name);
|
|
ZERO_STRUCT(user_password);
|
|
|
|
user_name[0] = '\0';
|
|
|
|
while ((ch = getopt(argc, argv, "axdehmnijr:sw:R: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 'i':
|
|
local_flags |= LOCAL_INTERDOM_ACCOUNT;
|
|
break;
|
|
case 'j':
|
|
d_printf("See 'net rpc join' for this functionality\n");
|
|
exit(1);
|
|
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 'w':
|
|
#ifdef WITH_LDAP_SAM
|
|
local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
|
|
fstrcpy(ldap_secret, optarg);
|
|
break;
|
|
#else
|
|
printf("-w not available unless configured --with-ldap\n");
|
|
goto done;
|
|
#endif
|
|
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;
|
|
|
|
#ifdef WITH_LDAP_SAM
|
|
if (local_flags & LOCAL_SET_LDAP_ADMIN_PW)
|
|
{
|
|
printf("Setting stored password for \"%s\" in secrets.tdb\n",
|
|
lp_ldap_admin_dn());
|
|
if (!store_ldap_admin_pw(ldap_secret))
|
|
DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
|
|
goto done;
|
|
}
|
|
#endif
|
|
/*
|
|
* 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))) {
|
|
usage();
|
|
}
|
|
|
|
/* Only load interfaces if we are doing network operations. */
|
|
|
|
if (remote_machine) {
|
|
load_interfaces();
|
|
}
|
|
|
|
/*
|
|
* 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();
|
|
}
|
|
|
|
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);
|
|
} else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
|
|
static fstring buf;
|
|
|
|
if (local_flags & LOCAL_ADD_USER) {
|
|
/*
|
|
* Prompt for trusting domain's account password
|
|
*/
|
|
new_passwd = prompt_for_new_password(stdin_passwd_get);
|
|
if(!new_passwd) {
|
|
fprintf(stderr, "Unable to get newpassword.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
|
|
fstrcpy(user_name, buf);
|
|
|
|
} else {
|
|
|
|
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;
|
|
}
|
|
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");
|
|
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)
|
|
{
|
|
AllowDebugChange = False;
|
|
|
|
#if defined(HAVE_SET_AUTH_PARAMETERS)
|
|
set_auth_parameters(argc, argv);
|
|
#endif /* HAVE_SET_AUTH_PARAMETERS */
|
|
|
|
setup_logging("smbpasswd", True);
|
|
|
|
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);
|
|
}
|