#include "includes.h"
-/*
- * Password changing error codes.
+extern pstring global_myname;
+extern int DEBUGLEVEL;
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
*/
+extern char *optarg;
+extern int optind;
+
+/*********************************************************
+a strdup with exit
+**********************************************************/
+static char *xstrdup(char *s)
+{
+ s = strdup(s);
+ if (!s) {
+ fprintf(stderr,"out of memory\n");
+ exit(1);
+ }
+ return s;
+}
+
-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) {
+ printf(" -R ORDER name resolve order\n");
+ printf(" -j DOMAIN join domain name\n");
+ printf(" -a add 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;
+ }
+
+ /*
+ * Create the machine account password file.
+ */
+ if(!trust_password_lock( domain, global_myname, True)) {
+ fprintf(stderr, "Unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, domain);
+ return 1;
+ }
+
+ /*
+ * Write the old machine account password.
+ */
+
+ if(!set_trust_account_password( orig_trust_passwd_hash)) {
+ fprintf(stderr, "Unable to write the machine account password for \
+machine %s in domain %s.\n", global_myname, domain);
+ trust_password_unlock();
+ 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");
+ trust_password_unlock();
+ return 1;
+ }
+
+ ret = change_trust_account_password( domain, remote_machine);
+ trust_password_unlock();
+
+ if(!ret) {
+ trust_password_delete( domain, global_myname);
+ 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)
{
- {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)
+ 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 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;
+ 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);
}
-/******************************************************
- Convert a hex password.
-*******************************************************/
-static int gethexpwd(char *p, char *pwd)
+/*************************************************************
+ 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)
{
- 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]);
+ 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);
- p1 = strchr(hexchars, hinybble);
- p2 = strchr(hexchars, lonybble);
- if (!p1 || !p2)
- return (False);
+ fstrcpy(new_passwd, p);
- hinybble = PTR_DIFF(p1, hexchars);
- lonybble = PTR_DIFF(p2, hexchars);
+ p = get_pass("Retype new SMB password:", stdin_get);
- pwd[i / 2] = (hinybble << 4) | lonybble;
+ if (strcmp(p, new_passwd)) {
+ fprintf(stderr, "Mismatch - password unchanged.\n");
+ ZERO_ARRAY(new_passwd);
+ return NULL;
}
- return (True);
+
+ return xstrdup(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,
+ BOOL add_user, BOOL enable_user,
+ BOOL disable_user, BOOL set_no_password,
+ BOOL trust_account)
+{
+ BOOL ret;
+ pstring err_str;
+ pstring msg_str;
+
+ if (remote_machine != NULL) {
+ if (add_user || enable_user || disable_user || set_no_password || trust_account) {
+ /* 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, trust_account, add_user, enable_user,
+ disable_user, set_no_password, 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;
}
-/******************************************************
- Find a password entry by name.
-*******************************************************/
-static struct smb_passwd *
-_my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd,
- BOOL *got_valid_nt_entry, long *pwd_seekpos)
+/*************************************************************
+handle password changing for root
+*************************************************************/
+static int process_root(int argc, char *argv[])
{
- /* 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];
+ struct passwd *pwd;
+ int ch;
+ BOOL joining_domain = False;
+ BOOL trust_account = False;
+ BOOL add_user = False;
+ BOOL disable_user = False;
+ BOOL enable_user = False;
+ BOOL set_no_password = False;
+ BOOL stdin_passwd_get = False;
+ char *user_name = NULL;
+ char *new_domain = NULL;
+ char *new_passwd = NULL;
+ char *old_passwd = NULL;
+ char *remote_machine = NULL;
+
+ while ((ch = getopt(argc, argv, "adehmnj:r:sR:D:U:")) != EOF) {
+ switch(ch) {
+ case 'a':
+ add_user = True;
+ break;
+ case 'd':
+ disable_user = True;
+ new_passwd = "XXXXXX";
+ break;
+ case 'e':
+ enable_user = True;
+ break;
+ case 'D':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'n':
+ set_no_password = True;
+ new_passwd = "NO PASSWORD";
+ 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 'm':
+ trust_account = True;
+ break;
+ case 'j':
+ new_domain = optarg;
+ strupper(new_domain);
+ joining_domain = True;
+ break;
+ case 'U':
+ user_name = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
- char linebuf[256];
- unsigned char c;
- unsigned char *p;
- long uidval;
- long linebuf_len;
- pw_buf.acct_ctrl = ACB_NORMAL;
+ /*
+ * Ensure add_user and either remote machine or join domain are
+ * not both set.
+ */
+ if(add_user && ((remote_machine != NULL) || joining_domain)) {
+ usage();
+ }
+
+ if(joining_domain) {
+ if (argc != 0) usage();
+ return join_domain(new_domain, remote_machine);
+ }
/*
- * Scan the file, a line at a time and check if the name matches.
+ * Deal with root - can add a user, but only locally.
*/
- while (!feof(fp)) {
- linebuf[0] = '\0';
- *pwd_seekpos = ftell(fp);
- fgets(linebuf, 256, fp);
- if (ferror(fp))
- return NULL;
+ switch(argc) {
+ case 0:
+ break;
+ case 1:
+ user_name = argv[0];
+ break;
+ case 2:
+ user_name = argv[0];
+ new_passwd = argv[1];
+ break;
+ default:
+ usage();
+ }
- /*
- * 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 (!user_name && (pwd = sys_getpwuid(0))) {
+ user_name = xstrdup(pwd->pw_name);
+ }
+
+ if (!user_name) {
+ fprintf(stderr,"You must specify a username\n");
+ exit(1);
+ }
+
+ if (trust_account) {
+ /* add the $ automatically */
+ static fstring buf;
- if ((linebuf[0] == 0) && feof(fp))
- break;
/*
- * 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 (add_user) {
+ 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);
+ slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+ user_name = buf;
+ }
- uidval = atoi((char *) p);
- while (*p && isdigit(*p))
- p++;
+ if (!remote_machine && !Get_Pwnam(user_name, True)) {
+ fprintf(stderr, "User \"%s\" was not found in system password file.\n",
+ user_name);
+ exit(1);
+ }
- 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(enable_user) {
+ struct smb_passwd *smb_pass = getsmbpwnam(user_name);
+ if((smb_pass != NULL) && (smb_pass->smb_passwd != NULL)) {
+ new_passwd = "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);
}
- 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,
+ add_user, enable_user, disable_user, set_no_password,
+ trust_account)) {
+ fprintf(stderr,"Failed to change password entry for %s\n", user_name);
+ return 1;
+ }
+
+ if(disable_user) {
+ printf("User %s disabled.\n", user_name);
+ } else if(enable_user) {
+ printf("User %s enabled.\n", user_name);
+ } else if (set_no_password) {
+ printf("User %s - set to no password.\n", user_name);
+ } else {
+ struct smb_passwd *smb_pass = getsmbpwnam(user_name);
+ printf("Password changed for user %s.", user_name );
+ if((smb_pass != NULL) && (smb_pass->acct_ctrl & ACB_DISABLED ))
+ printf(" User has disabled flag set.");
+ if((smb_pass != NULL) && (smb_pass->acct_ctrl & ACB_PWNOTREQ))
+ printf(" User has no password flag set.");
+ printf("\n");
+ }
+ return 0;
+}
- 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;
- }
- 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 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).
+ */
+ if (remote_machine == NULL) {
+ remote_machine = "127.0.0.1";
+ }
-/**********************************************************
- Encode the account control bits into a string.
-**********************************************************/
-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) {
+ 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);
+ }
-/**********************************************************
- Allocate an unused uid in the smbpasswd file to a new
- machine account.
-***********************************************************/
+ if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
+ False, False, False, False, False)) {
+ fprintf(stderr,"Failed to change password for %s\n", user_name);
+ return 1;
+ }
-int get_new_machine_uid(void)
-{
- int next_uid_start;
- FILE *fp;
- struct smb_passwd *smbpw;
-
- if(sizeof(uid_t) == 2)
- next_uid_start = 65533;
-
- if(sizeof(uid_t) == 4)
- next_uid_start = 0x7fffffff;
-
- fp = startsmbpwent(False);
- while((smbpw = getsmbpwent(fp)) != NULL) {
- if((smbpw->acct_ctrl & (ACB_SVRTRUST|ACB_WSTRUST)))
- next_uid_start = MIN(next_uid_start, (smbpw->smb_userid-1));
- }
- endsmbpwent(fp);
- return next_uid_start;
+ printf("Password changed for user %s\n", user_name);
+ return 0;
}
-/*********************************************************
- Print command usage on stderr and die.
-**********************************************************/
-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);
-}
/*********************************************************
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);
+
+ charset_initialise();
+
+ if(!initialize_password_db()) {
+ 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);
- }
-
- cli.error = 0;
-
- 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(global_myname, '.' );
+ if (p) *p = 0;
+ }
+ strupper(global_myname);
+
+ codepage_initialise(lp_client_code_page());
+
+ load_interfaces();
+
+ /* 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);
+ }
+
+ if (getuid() == 0) {
+ return process_root(argc, argv);
+ }
+
+ return process_nonroot(argc, argv);
}