2 * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
3 * (C) Jeremy Allison 1995-1998
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 675
17 * Mass Ave, Cambridge, MA 02139, USA.
22 extern pstring myhostname;
23 extern pstring global_myname;
24 extern fstring global_myworkgroup;
26 static char *prog_name;
28 /*********************************************************
29 Print command usage on stderr and die.
30 **********************************************************/
32 static void usage(char *name, BOOL is_root)
35 fprintf(stderr, "Usage is : %s [-D DEBUGLEVEL] [-a] [-d] [-m] [-n] [username] [password]\n\
36 %s: [-R <name resolve order>] [-D DEBUGLEVEL] [-j DOMAINNAME] [-r machine] [username] [password]\n%s: [-h]\n", name, name, name);
38 fprintf(stderr, "Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [password]\n", name);
42 /*********************************************************
44 **********************************************************/
46 static int setup_account( char *domain, char *remote_machine,
47 unsigned char orig_trust_passwd_hash[16],
48 unsigned char new_trust_passwd_hash[16])
50 struct in_addr dest_ip;
53 memset(&cli, '\0', sizeof(struct cli_state));
54 if(cli_initialise(&cli) == False) {
55 fprintf(stderr, "%s: unable to initialize client connection.\n", prog_name);
59 if(!resolve_name( remote_machine, &dest_ip)) {
60 fprintf(stderr, "%s: Can't resolve address for %s\n", prog_name, remote_machine);
64 if (ismyip(dest_ip)) {
65 fprintf(stderr,"%s: Machine %s is one of our addresses. Cannot add to ourselves.\n", prog_name,
70 if (!cli_connect(&cli, remote_machine, &dest_ip)) {
71 fprintf(stderr, "%s: unable to connect to SMB server on \
72 machine %s. Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli) );
76 if (!cli_session_request(&cli, remote_machine, 0x20, global_myname)) {
77 fprintf(stderr, "%s: machine %s rejected the session setup. \
78 Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli) );
83 cli.protocol = PROTOCOL_NT1;
85 if (!cli_negprot(&cli)) {
86 fprintf(stderr, "%s: machine %s rejected the negotiate protocol. \
87 Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli) );
91 if (cli.protocol != PROTOCOL_NT1) {
92 fprintf(stderr, "%s: machine %s didn't negotiate NT protocol.\n", prog_name, remote_machine);
98 * Do an anonymous session setup.
101 if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
102 fprintf(stderr, "%s: machine %s rejected the session setup. \
103 Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli) );
108 if (!(cli.sec_mode & 1)) {
109 fprintf(stderr, "%s: machine %s isn't in user level security mode\n", prog_name, remote_machine);
114 if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
115 fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. \
116 Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli) );
122 * Ok - we have an anonymous connection to the IPC$ share.
123 * Now start the NT Domain stuff :-).
126 if(cli_nt_session_open(&cli, PIPE_NETLOGON, False) == False) {
127 fprintf(stderr, "%s: unable to open the domain client session to \
128 machine %s. Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli));
129 cli_nt_session_close(&cli);
135 if(cli_nt_setup_creds(&cli, orig_trust_passwd_hash) == False) {
136 fprintf(stderr, "%s: unable to setup the PDC credentials to machine \
137 %s. Error was : %s.\n", prog_name, remote_machine, cli_errstr(&cli));
138 cli_nt_session_close(&cli);
144 if( cli_nt_srv_pwset( &cli,new_trust_passwd_hash ) == False) {
145 fprintf(stderr, "%s: unable to change password for machine %s in domain \
146 %s to Domain controller %s. Error was %s.\n", prog_name, global_myname, domain, remote_machine,
148 cli_close(&cli, cli.nt_pipe_fnum);
154 cli_nt_session_close(&cli);
161 /*********************************************************
163 **********************************************************/
165 static int join_domain( char *domain, char *remote)
167 fstring remote_machine;
169 fstring trust_passwd;
170 unsigned char trust_passwd_hash[16];
171 unsigned char new_trust_passwd_hash[16];
174 fstrcpy(remote_machine, remote ? remote : "");
175 fstrcpy(trust_passwd, global_myname);
176 strlower(trust_passwd);
177 E_md4hash( (uchar *)trust_passwd, trust_passwd_hash);
179 generate_random_buffer( new_trust_passwd_hash, 16, True);
181 /* Ensure that we are not trying to join a
182 domain if we are locally set up as a domain
185 if(lp_domain_controller() && strequal(lp_workgroup(), domain)) {
186 fprintf(stderr, "%s: Cannot join domain %s as we already configured as domain controller \
187 for that domain.\n", prog_name, domain);
192 * Write the new machine password.
196 * Get the machine account password.
198 if(!trust_password_lock( domain, global_myname, True)) {
199 fprintf(stderr, "%s: unable to open the machine account password file for \
200 machine %s in domain %s.\n", prog_name, global_myname, domain);
204 if(!set_trust_account_password( new_trust_passwd_hash)) {
205 fprintf(stderr, "%s: unable to read the machine account password for \
206 machine %s in domain %s.\n", prog_name, global_myname, domain);
207 trust_password_unlock();
211 trust_password_unlock();
214 * If we are given a remote machine assume this is the PDC.
218 strupper(remote_machine);
219 ret = setup_account( domain, remote_machine, trust_passwd_hash, new_trust_passwd_hash);
221 printf("%s: Joined domain %s.\n", prog_name, domain);
224 * Treat each name in the 'password server =' line as a potential
225 * PDC/BDC. Contact each in turn and try and authenticate and
226 * change the machine account password.
229 p = lp_passwordserver();
232 fprintf(stderr, "%s: No password server list given in smb.conf - \
233 unable to join domain.\n", prog_name);
235 while(p && next_token( &p, remote_machine, LIST_SEP)) {
237 strupper(remote_machine);
238 if(setup_account( domain, remote_machine, trust_passwd_hash, new_trust_passwd_hash) == 0) {
239 printf("%s: Joined domain %s.\n", prog_name, domain);
246 trust_password_delete( domain, global_myname);
247 fprintf(stderr,"%s: Unable to join domain %s.\n", prog_name, domain);
253 /*********************************************************
255 **********************************************************/
257 int main(int argc, char **argv)
261 extern int DEBUGLEVEL;
267 uchar new_nt_p16[16];
269 struct smb_passwd *smb_pwent;
273 BOOL is_root = False;
275 char *remote_machine = NULL;
276 BOOL add_user = False;
277 BOOL got_new_pass = False;
278 BOOL trust_account = False;
279 BOOL disable_user = False;
280 BOOL set_no_password = False;
281 BOOL joining_domain = False;
282 char *new_domain = NULL;
283 pstring servicesf = CONFIGFILE;
286 new_passwd[0] = '\0';
289 memset(old_passwd, '\0', sizeof(old_passwd));
290 memset(new_passwd, '\0', sizeof(new_passwd));
296 setup_logging(prog_name,True);
298 charset_initialise();
300 if (!lp_load(servicesf,True,False,False)) {
301 fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
304 if(!get_myname(myhostname,NULL)) {
305 fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
310 * Set the machine NETBIOS name if not already
311 * set from the config file.
316 fstrcpy( global_myname, myhostname );
317 p = strchr( global_myname, '.' );
321 strupper( global_myname );
323 codepage_initialise(lp_client_code_page());
325 /* Get the real uid */
328 /* Check the effective uid */
329 if ((geteuid() == 0) && (real_uid != 0)) {
330 fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
334 is_root = (real_uid == 0);
336 while ((ch = getopt(argc, argv, "adhmnj:r:R:D:")) != EOF) {
342 usage(prog_name, is_root);
348 fstrcpy(new_passwd, "XXXXXX");
350 usage(prog_name, is_root);
353 DEBUGLEVEL = atoi(optarg);
357 set_no_password = True;
359 fstrcpy(new_passwd, "NO PASSWORD");
361 usage(prog_name, is_root);
363 remote_machine = optarg;
367 lp_set_name_resolve_order(optarg);
370 usage(prog_name, is_root);
373 trust_account = True;
375 usage(prog_name, is_root);
380 strupper(new_domain);
381 joining_domain = True;
383 usage(prog_name, is_root);
387 usage(prog_name, is_root);
395 * Ensure add_user and either remote machine or join domain are
399 if(add_user && ((remote_machine != NULL) || joining_domain))
400 usage(prog_name, True);
403 * Deal with joining a domain.
405 if(joining_domain && argc != 0)
406 usage(prog_name, True);
409 return join_domain( new_domain, remote_machine);
415 * Deal with root - can add a user, but only locally.
422 /* If we are root we can change another's password. */
423 pstrcpy(user_name, argv[0]);
426 pstrcpy(user_name, argv[0]);
427 fstrcpy(new_passwd, argv[1]);
431 usage(prog_name, True);
437 int username_len = strlen(user_name);
438 if(username_len >= sizeof(pstring) - 1) {
439 fprintf(stderr, "%s: machine account name too long.\n", user_name);
443 if(user_name[username_len-1] != '$') {
444 user_name[username_len] = '$';
445 user_name[username_len+1] = '\0';
450 * Setup the pwd struct to point to known
451 * values for a machine account (it doesn't
452 * exist in /etc/passwd).
454 if((pwd = getpwnam(user_name)) == NULL) {
455 fprintf(stderr, "%s: User \"%s\" was not found in system password file.\n",
456 prog_name, user_name);
460 if((pwd = getpwuid(real_uid)) != NULL)
461 pstrcpy( user_name, pwd->pw_name);
467 fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
468 usage(prog_name, False);
472 usage(prog_name, False);
475 fstrcpy(new_passwd, argv[0]);
479 if((pwd = getpwuid(real_uid)) != NULL)
480 pstrcpy( user_name, pwd->pw_name);
483 * A non-root user is always setting a password
484 * via a remote machine (even if that machine is
488 if(remote_machine == NULL)
489 remote_machine = "127.0.0.1";
492 if (*user_name == '\0') {
493 fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
498 * If we are adding a machine account then pretend
499 * we already have the new password, we will be using
500 * the machinename as the password.
503 if(add_user && trust_account) {
505 strncpy(new_passwd, user_name, sizeof(fstring));
506 new_passwd[sizeof(fstring)-1] = '\0';
507 strlower(new_passwd);
508 if(new_passwd[strlen(new_passwd)-1] == '$')
509 new_passwd[strlen(new_passwd)-1] = '\0';
513 * If we are root we don't ask for the old password (unless it's on a
517 if (remote_machine != NULL) {
518 p = getpass("Old SMB password:");
519 fstrcpy(old_passwd, p);
523 new_passwd[0] = '\0';
525 p = getpass("New SMB password:");
527 strncpy(new_passwd, p, sizeof(fstring));
528 new_passwd[sizeof(fstring)-1] = '\0';
530 p = getpass("Retype new SMB password:");
532 if (strncmp(p, new_passwd, sizeof(fstring)-1))
534 fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
539 if (new_passwd[0] == '\0') {
540 printf("Password not set\n");
545 * Now do things differently depending on if we're changing the
546 * password on a remote machine. Remember - a normal user is
547 * always using this code, looping back to the local smbd.
550 if(remote_machine != NULL) {
551 struct cli_state cli;
554 if(!resolve_name( remote_machine, &ip)) {
555 fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
556 prog_name, remote_machine );
560 memset(&cli, '\0', sizeof(struct cli_state));
562 if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
563 fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
564 prog_name, remote_machine, cli_errstr(&cli) );
568 if (!cli_session_request(&cli, remote_machine, 0x20, global_myname)) {
569 fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
570 prog_name, remote_machine, cli_errstr(&cli) );
575 cli.protocol = PROTOCOL_NT1;
577 if (!cli_negprot(&cli)) {
578 fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",
579 prog_name, remote_machine, cli_errstr(&cli) );
584 if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
586 fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
587 prog_name, remote_machine, cli_errstr(&cli) );
592 if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
593 fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
594 prog_name, remote_machine, cli_errstr(&cli) );
599 if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
600 fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
601 prog_name, remote_machine, cli_errstr(&cli) );
611 * Check for a machine account.
614 if(trust_account && !pwd) {
615 fprintf(stderr, "%s: User %s does not exist in system password file \
616 (usually /etc/passwd). Cannot add machine account without a valid system user.\n",
617 prog_name, user_name);
621 /* Calculate the MD4 hash (NT compatible) of the new password. */
623 memset(new_nt_p16, '\0', 16);
624 E_md4hash((uchar *) new_passwd, new_nt_p16);
626 /* Mangle the password into Lanman format */
627 new_passwd[14] = '\0';
628 strupper(new_passwd);
631 * Calculate the SMB (lanman) hash functions of the new password.
634 memset(new_p16, '\0', 16);
635 E_P16((uchar *) new_passwd, new_p16);
638 * Open the smbpaswd file.
640 vp = startsampwent(True);
641 if (!vp && errno == ENOENT) {
642 fp = fopen(lp_smb_passwd_file(), "w");
644 fprintf(fp, "# Samba SMB password file\n");
646 vp = startsampwent(True);
651 fprintf(stderr, "%s: Failed to open password file %s.\n",
652 prog_name, lp_smb_passwd_file());
658 /* Get the smb passwd entry for this user */
659 smb_pwent = getsampwnam(user_name);
660 if (smb_pwent == NULL) {
661 if(add_user == False) {
662 fprintf(stderr, "%s: Failed to find entry for user %s.\n",
663 prog_name, pwd->pw_name);
668 /* Create a new smb passwd entry and set it to the given password. */
670 struct smb_passwd new_smb_pwent;
672 new_smb_pwent.smb_userid = pwd->pw_uid;
673 new_smb_pwent.smb_name = pwd->pw_name;
674 new_smb_pwent.smb_passwd = NULL;
675 new_smb_pwent.smb_nt_passwd = NULL;
676 new_smb_pwent.acct_ctrl = (trust_account ? ACB_WSTRUST : ACB_NORMAL);
679 new_smb_pwent.acct_ctrl |= ACB_DISABLED;
680 } else if (set_no_password) {
681 new_smb_pwent.acct_ctrl |= ACB_PWNOTREQ;
683 new_smb_pwent.smb_passwd = new_p16;
684 new_smb_pwent.smb_nt_passwd = new_nt_p16;
687 if(add_sampwd_entry(&new_smb_pwent) == False) {
688 fprintf(stderr, "%s: Failed to add entry for user %s.\n",
689 prog_name, pwd->pw_name);
695 printf("%s: Added user %s.\n", prog_name, user_name);
699 /* the entry already existed */
704 * We are root - just write the new password
705 * and the valid last change time.
710 * This currently won't work as it means changing
711 * the length of the record. JRA.
713 smb_pwent->acct_ctrl |= ACB_DISABLED;
714 smb_pwent->smb_passwd = NULL;
715 smb_pwent->smb_nt_passwd = NULL;
716 } else if (set_no_password) {
718 * This currently won't work as it means changing
719 * the length of the record. JRA.
721 smb_pwent->acct_ctrl |= ACB_PWNOTREQ;
722 smb_pwent->smb_passwd = NULL;
723 smb_pwent->smb_nt_passwd = NULL;
725 smb_pwent->smb_passwd = new_p16;
726 smb_pwent->smb_nt_passwd = new_nt_p16;
729 if(mod_sampwd_entry(smb_pwent,True) == False) {
730 fprintf(stderr, "%s: Failed to modify entry for user %s.\n",
731 prog_name, pwd->pw_name);
738 printf("User %s disabled.\n", user_name);
739 else if (set_no_password)
740 printf("User %s - set to no password.\n", user_name);
742 printf("Password changed for user %s.\n", user_name);