/*
- * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
- * (C) Jeremy Allison 1995-1998
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * smbpasswd module.
+ * 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 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
*
* 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.
- */
+ * Mass Ave, Cambridge, MA 02139, USA. */
#include "includes.h"
-/*
- * Password changing error codes.
+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;
-struct
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
{
- int err;
- char *message;
-} pw_change_errmap[] =
+ if (getuid() == 0) {
+ printf("smbpasswd [options] [username] [password]\n");
+ } else {
+ printf("smbpasswd [options] [password]\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");
+
+ if (getuid() == 0 || local_mode) {
+ 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)
{
- {5, "User has insufficient privilege" },
- {86, "The specified password is invalid" },
- {2226, "Operation only permitted on a Primary Domain Controller" },
- {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."},
- {0, NULL}
-};
-
-/******************************************************
- Return an error message for a remote password change.
-*******************************************************/
-
-char *get_error_message(struct cli_state *cli)
+ 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;
+}
+
+/* Initialise client credentials for authenticated pipe access */
+
+void init_rpcclient_creds(struct ntuser_creds *creds, char* username,
+ char* domain, char* password)
{
- 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;
+ ZERO_STRUCTP(creds);
+
+ if (lp_encrypted_passwords()) {
+ pwd_make_lm_nt_16(&creds->pwd, password);
+ } else {
+ pwd_set_cleartext(&creds->pwd, password);
+ }
+
+ fstrcpy(creds->user_name, username);
+ fstrcpy(creds->domain, domain);
}
-/******************************************************
- Convert a hex password.
-*******************************************************/
+/*********************************************************
+Join a domain using the administrator username and password
+**********************************************************/
+
+/* Macro for checking RPC error codes to make things more readable */
+
+#define CHECK_RPC_ERR(rpc, msg) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, (msg ": %s\n", get_nt_error_msg(result))); \
+ goto done; \
+ }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, debug_args); \
+ goto done; \
+ }
-static int gethexpwd(char *p, char *pwd)
+static int join_domain_byuser(char *domain, char *remote_machine,
+ char *username, char *password)
{
- 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]);
+ /* libsmb variables */
+
+ struct nmb_name calling, called;
+ struct ntuser_creds creds;
+ struct cli_state cli;
+ fstring dest_host, acct_name;
+ struct in_addr dest_ip;
+ TALLOC_CTX *mem_ctx;
+ uint32 acb_info;
+
+ /* rpc variables */
+
+ POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol;
+ DOM_SID domain_sid;
+ uint32 user_rid;
+
+ /* Password stuff */
+
+ char *machine_pwd;
+ int plen = 0;
+ uchar pwbuf[516], ntpw[16], sess_key[16];
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ SAM_USER_INFO_10 p10;
+
+ /* Misc */
+
+ NTSTATUS result;
+ int retval = 1;
- p1 = strchr(hexchars, hinybble);
- p2 = strchr(hexchars, lonybble);
- if (!p1 || !p2)
- return (False);
+ /* Connect to remote machine */
- hinybble = PTR_DIFF(p1, hexchars);
- lonybble = PTR_DIFF(p2, hexchars);
+ ZERO_STRUCT(cli);
+ ZERO_STRUCT(creds);
- pwd[i / 2] = (hinybble << 4) | lonybble;
+ if (!(mem_ctx = talloc_init())) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
+ goto done;
}
- return (True);
-}
-/******************************************************
- Find a password entry by name.
-*******************************************************/
+ if (!cli_initialise(&cli)) {
+ DEBUG(0, ("Could not initialise client structure\n"));
+ goto done;
+ }
+
+ init_rpcclient_creds(&creds, username, domain, password);
+ cli_init_creds(&cli, &creds);
+
+ if (!resolve_srv_name(remote_machine, dest_host, &dest_ip)) {
+ DEBUG(0, ("Could not resolve name %s\n", remote_machine));
+ goto done;
+ }
+
+ make_nmb_name(&called, dns_to_netbios_name(dest_host), 0x20);
+ make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
+
+ if (!cli_establish_connection(&cli, dest_host, &dest_ip, &calling,
+ &called, "IPC$", "IPC", False, True)) {
+ DEBUG(0, ("Error connecting to %s\n", dest_host));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ if (!cli_nt_session_open(&cli, PIPE_LSARPC)) {
+ DEBUG(0, ("Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+
+ CHECK_RPC_ERR(cli_lsa_open_policy(&cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &lsa_pol),
+ "error opening lsa policy handle");
+
+ CHECK_RPC_ERR(cli_lsa_query_info_policy(&cli, mem_ctx, &lsa_pol,
+ 5, domain, &domain_sid),
+ "error querying info policy");
+
+ cli_lsa_close(&cli, mem_ctx, &lsa_pol);
-static struct smb_passwd *
-_my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd,
- BOOL *got_valid_nt_entry, long *pwd_seekpos)
+ cli_nt_session_close(&cli); /* Done with this pipe */
+
+ /* Create domain user */
+
+ if (!cli_nt_session_open(&cli, PIPE_SAMR)) {
+ DEBUG(0, ("Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+ CHECK_RPC_ERR(cli_samr_connect(&cli, mem_ctx,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol),
+ "could not connect to SAM database");
+
+
+ CHECK_RPC_ERR(cli_samr_open_domain(&cli, mem_ctx, &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &domain_sid, &domain_pol),
+ "could not open domain");
+
+ /* Create domain user */
+
+ fstrcpy(acct_name, global_myname);
+ fstrcat(acct_name, "$");
+
+ strlower(acct_name);
+
+ acb_info = (lp_server_role() == ROLE_DOMAIN_BDC) ? ACB_SVRTRUST :
+ ACB_WSTRUST;
+
+ {
+ uint32 unknown = 0xe005000b;
+
+ result = cli_samr_create_dom_user(&cli, mem_ctx, &domain_pol,
+ acct_name, acb_info,
+ unknown, &user_pol,
+ &user_rid);
+
+ /* We *must* do this.... don't ask... */
+
+ CHECK_RPC_ERR_DEBUG(cli_samr_close(&cli, mem_ctx, &user_pol), ("error closing user policy"));
+ result = NT_STATUS_USER_EXISTS;
+ }
+
+ if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_USER_EXISTS)) {
+ uint32 num_rids, *name_types, *user_rids;
+ uint32 flags = 0x3e8;
+ char *names;
+
+ /* Look up existing rid */
+
+ names = (char *)&acct_name[0];
+
+ CHECK_RPC_ERR_DEBUG(
+ cli_samr_lookup_names(&cli, mem_ctx,
+ &domain_pol, flags,
+ 1, &names, &num_rids,
+ &user_rids, &name_types),
+ ("error looking up rid for user %s: %s\n",
+ acct_name, get_nt_error_msg(result)));
+
+ if (name_types[0] != SID_NAME_USER) {
+ DEBUG(0, ("%s is not a user account\n", acct_name));
+ goto done;
+ }
+
+ user_rid = user_rids[0];
+
+ /* Open handle on user */
+
+ CHECK_RPC_ERR_DEBUG(
+ cli_samr_open_user(&cli, mem_ctx, &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid, &user_pol),
+ ("could not re-open existing user %s: %s\n",
+ acct_name, get_nt_error_msg(result)));
+
+ } else if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("error creating domain user: %s\n",
+ get_nt_error_msg(result)));
+ goto done;
+ }
+
+ /* Create a random machine account password */
+
+ {
+ UNISTR2 upw; /* Unicode password */
+
+ upw.buffer = (uint16 *)talloc_zero(mem_ctx, 0xc *
+ sizeof(uint16));
+
+ upw.uni_str_len = 0xc;
+ upw.uni_max_len = 0xc;
+
+ machine_pwd = (char *)upw.buffer;
+ plen = upw.uni_str_len * 2;
+ generate_random_buffer((unsigned char *)machine_pwd, plen, True);
+
+ encode_pw_buffer((char *)pwbuf, machine_pwd, plen, False);
+
+ nt_owf_genW(&upw, ntpw);
+ }
+
+ /* Set password on machine account */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ /* I don't think this is quite the right place for this
+ calculation. It should be moved somewhere where the credentials
+ are calculated. )-: */
+
+ mdfour(sess_key, cli.pwd.smb_nt_pwd, 16);
+
+ CHECK_RPC_ERR(cli_samr_set_userinfo(&cli, mem_ctx, &user_pol, 24,
+ sess_key, &ctr),
+ "error setting trust account password");
+
+ /* Why do we have to try to (re-)set the ACB to be the same as what
+ we passed in the samr_create_dom_user() call? When a NT
+ workstation is joined to a domain by an administrator the
+ acb_info is set to 0x80. For a normal user with "Add
+ workstations to the domain" rights the acb_info is 0x84. I'm
+ not sure whether it is supposed to make a difference or not. NT
+ seems to cope with either value so don't bomb out if the set
+ userinfo2 level 0x10 fails. -tpot */
+
+ ZERO_STRUCT(ctr);
+ ctr.switch_value = 0x10;
+ ctr.info.id10 = &p10;
+
+ init_sam_user_info10(&p10, acb_info);
+
+ /* Ignoring the return value is necessary for joining a domain
+ as a normal user with "Add workstation to domain" privilege. */
+
+ result = cli_samr_set_userinfo2(&cli, mem_ctx, &user_pol, 0x10,
+ sess_key, &ctr);
+
+ /* Now store the secret in the secrets database */
+
+ strupper(domain);
+
+ if (!secrets_store_domain_sid(domain, &domain_sid) ||
+ !secrets_store_trust_account_password(domain, ntpw)) {
+ DEBUG(0, ("error storing domain secrets\n"));
+ goto done;
+ }
+
+ retval = 0; /* Success! */
+
+ done:
+ /* Close down pipe - this will clean up open policy handles */
+
+ if (cli.nt_pipe_fnum)
+ cli_nt_session_close(&cli);
+
+ /* Display success or failure */
+
+ if (retval != 0) {
+ trust_password_delete(domain);
+ fprintf(stderr,"Unable to join domain %s.\n",domain);
+ } else {
+ printf("Joined domain %s.\n",domain);
+ }
+
+ return retval;
+}
+
+static void set_line_buffering(FILE *f)
{
- /* 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];
+ setvbuf(f, NULL, _IOLBF, 0);
+}
- char linebuf[256];
- unsigned char c;
- unsigned char *p;
- long uidval;
- long linebuf_len;
+/*************************************************************
+ 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;
- pw_buf.acct_ctrl = ACB_NORMAL;
+ ZERO_ARRAY(new_passwd);
/*
- * Scan the file, a line at a time and check if the name matches.
+ * 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.
*/
- while (!feof(fp)) {
- linebuf[0] = '\0';
- *pwd_seekpos = ftell(fp);
+ 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);
+}
- 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;
+/*************************************************************
+ 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 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;
+}
+
+
+/*************************************************************
+ 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 = 0;
+ 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;
+ new_passwd = xstrdup("XXXXXX");
+ break;
+ case 'd':
+ local_flags |= LOCAL_DISABLE_USER;
+ new_passwd = xstrdup("XXXXXX");
+ break;
+ case 'e':
+ local_flags |= LOCAL_ENABLE_USER;
+ break;
+ case 'm':
+ local_flags |= LOCAL_TRUST_ACCOUNT;
+ break;
+ case 'n':
+ local_flags |= LOCAL_SET_NO_PASSWORD;
+ new_passwd = xstrdup("NO 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));
}
- } else
- linebuf[linebuf_len - 1] = '\0';
- if ((linebuf[0] == 0) && feof(fp))
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);
+ }
+
+ return join_domain_byuser(new_domain, remote_machine,
+ user_name, user_password);
+ } 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 = 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;
+
/*
- * The line we have should be of the form :-
- *
- * username:uid:[32hex bytes]:....other flags presently
- * ignored....
- *
- * or,
- *
- * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
- *
- * if Windows NT compatible passwords are also present.
+ * Remove any trailing '$' before we
+ * generate the initial machine password.
*/
- if (linebuf[0] == '#' || linebuf[0] == '\0')
- continue;
- p = (unsigned char *) strchr(linebuf, ':');
- if (p == NULL)
- continue;
+ 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 = xstrdup(user_name);
+ strlower(new_passwd);
+ }
+
/*
- * As 256 is shorter than a pstring we don't need to check
- * length here - if this ever changes....
+ * Now ensure the username ends in '$' for
+ * the machine add.
*/
- 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++;
+ slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+ }
- if (*p != ':')
- return (False);
+ if (remote_machine != NULL) {
+ old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ if (!new_passwd) {
/*
- * 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.
+ * 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.
*/
- 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)*/
-
- pw_buf.acct_ctrl |= ACB_DISABLED;
-
- /* 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') {
- if (gethexpwd((char *)p,(char *)smbntpwd))
- pw_buf.smb_nt_passwd = smbntpwd;
- }
+
+ 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)) {
+ new_passwd = xstrdup("XXXX"); /* Don't care. */
}
- pw_buf.smb_name = user_name;
- pw_buf.smb_userid = uidval;
- pw_buf.smb_passwd = NULL; /* No password */
- return (&pw_buf);
+ pdb_free_sam(&sampass);
}
- if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
- return (False);
- if (p[32] != ':')
- return (False);
+ if(!new_passwd)
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
- if (!strncasecmp((char *)p, "NO PASSWORD", 11)) {
- pw_buf.smb_passwd = NULL; /* No password */
- pw_buf.acct_ctrl |= ACB_PWNOTREQ;
- } else {
- if(!gethexpwd((char *)p,(char *)smbpwd))
- return False;
- pw_buf.smb_passwd = smbpwd;
+ 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))) {
+ 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);
+ }
- 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') {
- if (gethexpwd((char *)p,(char *)smbntpwd))
- pw_buf.smb_nt_passwd = smbntpwd;
- }
+ done:
+ SAFE_FREE(new_passwd);
+ return result;
+}
- p += 33; /* Move to the first character of the line after
- the NT password. */
+
+/*************************************************************
+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;
- /*
- * 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) {
-#if 0
- /*
- * Hmmm. Don't allow these to be set/read independently
- * of the actual password fields. We don't want a mismatch.
- * JRA.
- */
- case 'N':
- /* 'N'o password. */
- pw_buf.acct_ctrl |= ACB_PWNOTREQ;
- break;
- case 'D':
- /* 'D'isabled. */
- pw_buf.acct_ctrl |= ACB_DISABLED;
- break;
-#endif
- 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;
-
- } 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;
- }
- }
- return &pw_buf;
- }
- return NULL;
-}
+ if(argc > 1) {
+ usage();
+ }
+
+ if (argc == 1) {
+ new_passwd = argv[0];
+ }
+
+ if (!user_name) {
+ pwd = sys_getpwuid(getuid());
+ if (pwd) {
+ user_name = xstrdup(pwd->pw_name);
+ } else {
+ fprintf(stderr,"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).
+ */
-/**********************************************************
- Encode the account control bits into a string.
-**********************************************************/
+ load_interfaces(); /* Delayed from main() */
-char *encode_acct_ctrl(uint16 acct_ctrl)
-{
- static fstring acct_str;
- char *p = acct_str;
-
- *p++ = '[';
-
- if(acct_ctrl & ACB_HOMDIRREQ)
- *p++ = 'H';
- if(acct_ctrl & ACB_TEMPDUP)
- *p++ = 'T';
- if(acct_ctrl & ACB_NORMAL)
- *p++ = 'U';
- if(acct_ctrl & ACB_MNS)
- *p++ = 'M';
- if(acct_ctrl & ACB_WSTRUST)
- *p++ = 'W';
- if(acct_ctrl & ACB_SVRTRUST)
- *p++ = 'S';
- if(acct_ctrl & ACB_AUTOLOCK)
- *p++ = 'L';
- if(acct_ctrl & ACB_PWNOEXP)
- *p++ = 'X';
- if(acct_ctrl & ACB_DOMTRUST)
- *p++ = 'I';
-
- *p++ = ']';
- *p = '\0';
- return acct_str;
-}
+ if (remote_machine == NULL) {
+ remote_machine = "127.0.0.1";
+ }
-/**********************************************************
- Allocate an unused uid in the smbpasswd file to a new
- machine account.
-***********************************************************/
+ 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);
+ }
-int get_new_machine_uid(void)
-{
- int next_uid_start;
- void *vp;
- struct smb_passwd *smbpw;
-
- if(sizeof(uid_t) == 2)
- next_uid_start = 65533;
-
- if(sizeof(uid_t) == 4)
- next_uid_start = 0x7fffffff;
-
- vp = startsmbpwent(False);
- while((smbpw = getsmbpwent(vp)) != NULL) {
- if((smbpw->acct_ctrl & (ACB_SVRTRUST|ACB_WSTRUST)))
- next_uid_start = MIN(next_uid_start, (smbpw->smb_userid-1));
- }
- endsmbpwent(vp);
- return next_uid_start;
-}
+ 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;
+ }
-/*********************************************************
- Print command usage on stderr and die.
-**********************************************************/
+ printf("Password changed for user %s\n", user_name);
-static void usage(char *name, BOOL is_root)
-{
- if(is_root)
- 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);
- else
- fprintf(stderr, "Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [password]\n", name);
- exit(1);
+ done:
+ SAFE_FREE(old_passwd);
+ SAFE_FREE(new_passwd);
+
+ return result;
}
+
+
/*********************************************************
Start here.
**********************************************************/
-
int main(int argc, char **argv)
-{
- extern char *optarg;
- extern int optind;
- extern int DEBUGLEVEL;
- char *prog_name;
- int real_uid;
- struct passwd *pwd;
- struct passwd machine_account_pwd;
- 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;
- BOOL got_valid_nt_entry = False;
- long seekpos;
- int pwfd;
- char ascii_p16[66];
- char c;
- int ch;
- int ret, i, err, writelen;
- int lockfd = -1;
- char *pfile = SMB_PASSWD_FILE;
- char readbuf[16 * 1024];
- BOOL is_root = False;
- pstring user_name;
- pstring machine_dir_name;
- char *remote_machine = NULL;
- BOOL add_user = False;
- BOOL got_new_pass = False;
- BOOL machine_account = False;
- BOOL disable_user = False;
- BOOL set_no_password = False;
- pstring servicesf = CONFIGFILE;
-
- new_passwd[0] = '\0';
- user_name[0] = '\0';
-
- memset(old_passwd, '\0', sizeof(old_passwd));
- memset(new_passwd, '\0', sizeof(new_passwd));
-
- prog_name = argv[0];
-
- TimeInit();
-
- setup_logging(prog_name,True);
-
- charset_initialise();
-
- if (!lp_load(servicesf,True,False,False)) {
- fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
- }
-
- codepage_initialise(lp_client_code_page());
-
- /* Get the real uid */
- real_uid = getuid();
-
- /* Check the effective uid */
- if ((geteuid() == 0) && (real_uid != 0)) {
- fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
- exit(1);
- }
-
- is_root = (real_uid == 0);
-
- while ((ch = getopt(argc, argv, "adhmnr:R:D:")) != EOF) {
- switch(ch) {
- case 'a':
- 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);
- break;
- case 'D':
- DEBUGLEVEL = atoi(optarg);
- break;
- case 'n':
- if(is_root) {
- set_no_password = True;
- got_new_pass = True;
- strcpy(new_passwd, "NO PASSWORD");
- } else
- usage(prog_name, is_root);
- case 'r':
- remote_machine = optarg;
- break;
- case 'R':
- if(is_root) {
- lp_set_name_resolve_order(optarg);
- break;
- } else
- usage(prog_name, is_root);
- case 'm':
- if(is_root) {
- machine_account = True;
- } else
- usage(prog_name, is_root);
- break;
- case 'h':
- default:
- usage(prog_name, is_root);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /*
- * 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);
- }
-
- 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 {
- if((pwd = getpwuid(real_uid)) != NULL)
- pstrcpy( user_name, pwd->pw_name);
- }
-
- } else {
-
- if(add_user) {
- fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
- usage(prog_name, False);
- }
-
- if(argc > 1)
- usage(prog_name, False);
-
- if(argc == 1) {
- fstrcpy(new_passwd, argv[0]);
- got_new_pass = True;
- }
-
- 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";
- }
+{
+ static pstring servicesf = CONFIGFILE;
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ TimeInit();
+
+ setup_logging("smbpasswd", True);
+
+ if(!initialize_password_db(True)) {
+ fprintf(stderr, "Can't setup password database vectors.\n");
+ exit(1);
+ }
+
+ if (!lp_load(servicesf,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ servicesf);
+ exit(1);
+ }
+
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
- if (*user_name == '\0') {
- fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
- exit(1);
- }
-
- /*
- * 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);
- }
-
- /*
- * 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);
- }
-
- if (!got_new_pass) {
- 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:");
-
- if (strncmp(p, new_passwd, sizeof(fstring)-1))
- {
- fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
- exit(1);
- }
- }
-
- if (new_passwd[0] == '\0') {
- printf("Password not set\n");
- exit(0);
- }
-
- /*
- * 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);
- }
-
- memset(&cli, '\0', sizeof(struct cli_state));
-
- if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
- fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- exit(1);
- }
-
- if (!cli_session_request(&cli, remote_machine, 0x20, myname)) {
- fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- cli_shutdown(&cli);
- exit(1);
- }
-
- cli.protocol = PROTOCOL_NT1;
-
- if (!cli_negprot(&cli)) {
- fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- cli_shutdown(&cli);
- exit(1);
- }
-
- if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
- "", 0, "")) {
- fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- cli_shutdown(&cli);
- exit(1);
- }
-
- if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
- fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- cli_shutdown(&cli);
- exit(1);
- }
-
- if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
- fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
- prog_name, remote_machine, get_error_message(&cli) );
- cli_shutdown(&cli);
- exit(1);
- }
-
- cli_shutdown(&cli);
- exit(0);
- }
-
- /*
- * 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';
- }
-
- /*
- * 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;
- sprintf(machine_dir_name, "Workstation machine account for %s", user_name);
- pwd->pw_gecos = "";
- pwd->pw_dir = machine_dir_name;
- pwd->pw_shell = "";
- pwd->pw_uid = get_new_machine_uid();
-
- }
-
- /* Calculate the MD4 hash (NT compatible) of the new password. */
-
- memset(new_nt_p16, '\0', 16);
- E_md4hash((uchar *) new_passwd, new_nt_p16);
-
- /* Mangle the password into Lanman format */
- new_passwd[14] = '\0';
- strupper(new_passwd);
-
- /*
- * Calculate the SMB (lanman) hash functions of the new password.
- */
-
- 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
- */
- 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",
- prog_name, pfile);
- errno = err;
- perror(prog_name);
- exit(err);
- }
-
- /* Set read buffer to 16k for effiecient reads */
- setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
-
- /* make sure it is only rw by the owner */
- chmod(pfile, 0600);
-
- /* Lock the smbpasswd file for write. */
- if ((lockfd = pw_file_lock(fileno(fp), F_WRLCK, 5)) < 0) {
- err = errno;
- fprintf(stderr, "%s: Failed to lock password file %s.\n",
- prog_name, pfile);
- fclose(fp);
- errno = err;
- perror(prog_name);
- exit(err);
- }
-
- /* Get the smb passwd entry for this user */
- smb_pwent = _my_get_smbpwnam(fp, user_name, &valid_old_pwd,
- &got_valid_nt_entry, &seekpos);
- if (smb_pwent == NULL) {
- if(add_user == False) {
- fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
- prog_name, pwd->pw_name, pfile);
- fclose(fp);
- pw_file_unlock(lockfd);
- exit(1);
- }
-
- /* Create a new smb passwd entry and set it to the given password. */
- {
- int fd;
- int new_entry_length;
- char *new_entry;
- long offpos;
- uint16 acct_ctrl = (machine_account ? ACB_WSTRUST : ACB_NORMAL);
-
- /* 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. \
-Error was %s\n", prog_name, user_name, pfile, strerror(errno));
- fclose(fp);
- pw_file_unlock(lockfd);
- exit(1);
- }
-
- new_entry_length = strlen(user_name) + 1 + 15 + 1 +
- 32 + 1 + 32 + 1 + sizeof(fstring) +
- 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. \
-Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(1);
- }
-
- sprintf(new_entry, "%s:%u:", pwd->pw_name, (unsigned)pwd->pw_uid);
- p = &new_entry[strlen(new_entry)];
- 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]);
- }
- p += 32;
- *p++ = ':';
- sprintf(p, "%s:%s:%s\n", encode_acct_ctrl(acct_ctrl),
- pwd->pw_dir, pwd->pw_shell);
- if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
- fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
-Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
- /* Remove the entry we just wrote. */
- if(ftruncate(fd, offpos) == -1) {
- fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
-Error was %s. Password file may be corrupt ! Please examine by hand !\n",
- prog_name, pwd->pw_name, strerror(errno));
- }
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(1);
- }
-
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(0);
- }
- } else {
- /* the entry already existed */
- add_user = False;
- }
-
- /*
- * We are root - just write the new password.
- */
-
- /* Create the 32 byte representation of the new p16 */
- 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]);
- }
- }
- if(got_valid_nt_entry) {
- /* Add on the NT md4 hash */
- ascii_p16[32] = ':';
- 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]);
- }
- }
- }
- /*
- * 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",
- prog_name, pfile);
- errno = err;
- perror(prog_name);
- pw_file_unlock(lockfd);
- fclose(fp);
- 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",
- prog_name, pfile);
- errno = err;
- perror(prog_name);
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(1);
- }
- if (c != ':') {
- fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
- prog_name, pfile);
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(1);
- }
- writelen = (got_valid_nt_entry) ? 65 : 32;
- if (write(pwfd, ascii_p16, writelen) != writelen) {
- err = errno;
- fprintf(stderr, "%s: write fail in file %s.\n",
- prog_name, pfile);
- errno = err;
- perror(prog_name);
- pw_file_unlock(lockfd);
- fclose(fp);
- exit(err);
- }
- pw_file_unlock(lockfd);
- 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);
- return 0;
+ 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 ((geteuid() == (uid_t)0) && (getuid() != (uid_t)0)) {
+ 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);
}