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.
23 extern pstring myhostname
;
24 extern pstring global_myname
;
25 extern fstring global_myworkgroup
;
26 extern int DEBUGLEVEL
;
29 /*********************************************************
31 **********************************************************/
32 static char *xstrdup(char *s
)
36 fprintf(stderr
,"out of memory\n");
43 /*********************************************************
44 Print command usage on stderr and die.
45 **********************************************************/
46 static void usage(void)
49 printf("smbpasswd [options] [username] [password]\n");
51 printf("smbpasswd [options] [password]\n");
54 printf(" -s use stdin for password prompt\n");
55 printf(" -D LEVEL debug level\n");
56 printf(" -U USER remote username\n");
57 printf(" -r MACHINE remote machine\n");
60 printf(" -R ORDER name resolve order\n");
61 printf(" -j DOMAIN join domain name\n");
62 printf(" -a add user\n");
63 printf(" -d delete user\n");
64 printf(" -e enable user\n");
65 printf(" -n set no password\n");
66 printf(" -m machine trust account\n");
71 /*********************************************************
73 **********************************************************/
74 static int join_domain(char *domain
, char *remote
)
76 pstring remote_machine
;
78 unsigned char orig_trust_passwd_hash
[16];
81 pstrcpy(remote_machine
, remote
? remote
: "");
82 fstrcpy(trust_passwd
, global_myname
);
83 strlower(trust_passwd
);
84 E_md4hash( (uchar
*)trust_passwd
, orig_trust_passwd_hash
);
86 /* Ensure that we are not trying to join a
87 domain if we are locally set up as a domain
90 if(strequal(remote
, global_myname
)) {
91 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
);
96 * Create the machine account password file.
98 if(!trust_password_lock( domain
, global_myname
, True
)) {
99 fprintf(stderr
, "unable to open the machine account password file for \
100 machine %s in domain %s.\n", global_myname
, domain
);
105 * Write the old machine account password.
108 if(!set_trust_account_password( orig_trust_passwd_hash
)) {
109 fprintf(stderr
, "unable to write the machine account password for \
110 machine %s in domain %s.\n", global_myname
, domain
);
111 trust_password_unlock();
116 * If we are given a remote machine assume this is the PDC.
120 pstrcpy(remote_machine
, lp_passwordserver());
123 if(!*remote_machine
) {
124 fprintf(stderr
, "No password server list given in smb.conf - \
125 unable to join domain.\n");
126 trust_password_unlock();
130 ret
= change_trust_account_password( domain
, remote_machine
);
131 trust_password_unlock();
134 trust_password_delete( domain
, global_myname
);
135 fprintf(stderr
,"Unable to join domain %s.\n",domain
);
137 printf("Joined domain %s.\n",domain
);
144 static void set_line_buffering(FILE *f
)
146 setvbuf(f
, NULL
, _IOLBF
, 0);
149 /*************************************************************
150 Utility function to prompt for passwords from stdin. Each
151 password entered must end with a newline.
152 *************************************************************/
153 static char *stdin_new_passwd(void)
155 static fstring new_passwd
;
158 ZERO_STRUCT(new_passwd
);
161 * if no error is reported from fgets() and string at least contains
162 * the newline that ends the password, then replace the newline with
165 if ( fgets(new_passwd
, sizeof(new_passwd
), stdin
) != NULL
) {
166 if ((len
= strlen(new_passwd
)) > 0) {
167 if(new_passwd
[len
-1] == '\n')
168 new_passwd
[len
- 1] = 0;
175 /*************************************************************
176 Utility function to get passwords via tty or stdin
177 Used if the '-s' option is set to silently get passwords
179 *************************************************************/
180 static char *get_pass( char *prompt
, BOOL stdin_get
)
184 p
= stdin_new_passwd();
191 /*************************************************************
192 Utility function to prompt for new password.
193 *************************************************************/
194 static char *prompt_for_new_password(BOOL stdin_get
)
199 ZERO_STRUCT(new_passwd
);
201 p
= get_pass("New SMB password:", stdin_get
);
203 fstrcpy(new_passwd
, p
);
205 p
= get_pass("Retype new SMB password:", stdin_get
);
207 if (strcmp(p
, new_passwd
)) {
208 fprintf(stderr
, "Mismatch - password unchanged.\n");
218 /*************************************************************
219 change a password on a remote machine using IPC calls
220 *************************************************************/
221 static BOOL
remote_password_change(const char *remote_machine
, const char *user_name
,
222 const char *old_passwd
, const char *new_passwd
)
224 struct nmb_name calling
, called
;
225 struct cli_state cli
;
228 if(!resolve_name( remote_machine
, &ip
, 0x20)) {
229 fprintf(stderr
, "unable to find an IP address for machine %s.\n",
236 if (!cli_initialise(&cli
) || !cli_connect(&cli
, remote_machine
, &ip
)) {
237 fprintf(stderr
, "unable to connect to SMB server on machine %s. Error was : %s.\n",
238 remote_machine
, cli_errstr(&cli
) );
242 make_nmb_name(&calling
, global_myname
, 0x0 , scope
);
243 make_nmb_name(&called
, remote_machine
, 0x20, scope
);
245 if (!cli_session_request(&cli
, &calling
, &called
)) {
246 fprintf(stderr
, "machine %s rejected the session setup. Error was : %s.\n",
247 remote_machine
, cli_errstr(&cli
) );
252 cli
.protocol
= PROTOCOL_NT1
;
254 if (!cli_negprot(&cli
)) {
255 fprintf(stderr
, "machine %s rejected the negotiate protocol. Error was : %s.\n",
256 remote_machine
, cli_errstr(&cli
) );
262 * We should connect as the anonymous user here, in case
263 * the server has "must change password" checked...
264 * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
267 if (!cli_session_setup(&cli
, "", "", 0, "", 0, "")) {
268 fprintf(stderr
, "machine %s rejected the session setup. Error was : %s.\n",
269 remote_machine
, cli_errstr(&cli
) );
274 if (!cli_send_tconX(&cli
, "IPC$", "IPC", "", 1)) {
275 fprintf(stderr
, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
276 remote_machine
, cli_errstr(&cli
) );
281 if(!cli_oem_change_password(&cli
, user_name
, new_passwd
, old_passwd
)) {
282 fprintf(stderr
, "machine %s rejected the password change: Error was : %s.\n",
283 remote_machine
, cli_errstr(&cli
) );
293 /*************************************************************
294 add a new user to the local smbpasswd file
295 *************************************************************/
296 static BOOL
add_new_user(char *user_name
, uid_t uid
, BOOL trust_account
,
297 BOOL disable_user
, BOOL set_no_password
,
298 uchar
*new_p16
, uchar
*new_nt_p16
)
300 struct smb_passwd new_smb_pwent
;
302 /* Create a new smb passwd entry and set it to the given password. */
303 new_smb_pwent
.smb_userid
= uid
;
304 new_smb_pwent
.smb_name
= user_name
;
305 new_smb_pwent
.smb_passwd
= NULL
;
306 new_smb_pwent
.smb_nt_passwd
= NULL
;
307 new_smb_pwent
.acct_ctrl
= (trust_account
? ACB_WSTRUST
: ACB_NORMAL
);
310 new_smb_pwent
.acct_ctrl
|= ACB_DISABLED
;
311 } else if (set_no_password
) {
312 new_smb_pwent
.acct_ctrl
|= ACB_PWNOTREQ
;
314 new_smb_pwent
.smb_passwd
= new_p16
;
315 new_smb_pwent
.smb_nt_passwd
= new_nt_p16
;
318 return add_smbpwd_entry(&new_smb_pwent
);
322 /*************************************************************
323 change a password entry in the local smbpasswd file
324 *************************************************************/
325 static BOOL
local_password_change(char *user_name
, BOOL trust_account
, BOOL add_user
,
326 BOOL enable_user
, BOOL disable_user
, BOOL set_no_password
,
331 struct smb_passwd
*smb_pwent
;
333 uchar new_nt_p16
[16];
335 pwd
= getpwnam(user_name
);
338 * Check for a machine account.
341 if(trust_account
&& !pwd
) {
342 fprintf(stderr
, "User %s does not exist in system password file (usually /etc/passwd). Cannot add machine account without a valid system user.\n",
347 /* Calculate the MD4 hash (NT compatible) of the new password. */
348 nt_lm_owf_gen(new_passwd
, new_nt_p16
, new_p16
);
351 * Open the smbpaswd file.
353 vp
= startsmbpwent(True
);
354 if (!vp
&& errno
== ENOENT
) {
356 fprintf(stderr
,"smbpasswd file did not exist - attempting to create it.\n");
357 fp
= fopen(lp_smb_passwd_file(), "w");
359 fprintf(fp
, "# Samba SMB password file\n");
361 vp
= startsmbpwent(True
);
366 perror(lp_smb_passwd_file());
370 /* Get the smb passwd entry for this user */
371 smb_pwent
= getsmbpwnam(user_name
);
372 if (smb_pwent
== NULL
) {
373 if(add_user
== False
) {
374 fprintf(stderr
, "Failed to find entry for user %s.\n",
380 if (add_new_user(user_name
, pwd
->pw_uid
, trust_account
, disable_user
,
381 set_no_password
, new_p16
, new_nt_p16
)) {
382 printf("Added user %s.\n", user_name
);
386 fprintf(stderr
, "Failed to add entry for user %s.\n", user_name
);
391 /* the entry already existed */
396 * We are root - just write the new password
397 * and the valid last change time.
401 smb_pwent
->acct_ctrl
|= ACB_DISABLED
;
402 } else if (enable_user
) {
403 if(smb_pwent
->smb_passwd
== NULL
) {
404 smb_pwent
->smb_passwd
= new_p16
;
405 smb_pwent
->smb_nt_passwd
= new_nt_p16
;
407 smb_pwent
->acct_ctrl
&= ~ACB_DISABLED
;
408 } else if (set_no_password
) {
409 smb_pwent
->acct_ctrl
|= ACB_PWNOTREQ
;
410 /* This is needed to preserve ACB_PWNOTREQ in mod_smbfilepwd_entry */
411 smb_pwent
->smb_passwd
= NULL
;
412 smb_pwent
->smb_nt_passwd
= NULL
;
414 smb_pwent
->acct_ctrl
&= ~ACB_PWNOTREQ
;
415 smb_pwent
->smb_passwd
= new_p16
;
416 smb_pwent
->smb_nt_passwd
= new_nt_p16
;
419 if(mod_smbpwd_entry(smb_pwent
,True
) == False
) {
420 fprintf(stderr
, "Failed to modify entry for user %s.\n",
432 /*************************************************************
433 change a password either locally or remotely
434 *************************************************************/
435 static BOOL
password_change(const char *remote_machine
, char *user_name
,
436 char *old_passwd
, char *new_passwd
,
437 BOOL add_user
, BOOL enable_user
,
438 BOOL disable_user
, BOOL set_no_password
,
441 if (remote_machine
!= NULL
) {
442 if (add_user
|| enable_user
|| disable_user
|| set_no_password
|| trust_account
) {
443 /* these things can't be done remotely yet */
446 return remote_password_change(remote_machine
, user_name
, old_passwd
, new_passwd
);
449 return local_password_change(user_name
, trust_account
, add_user
, enable_user
,
450 disable_user
, set_no_password
, new_passwd
);
454 /*************************************************************
455 handle password changing for root
456 *************************************************************/
457 static int process_root(int argc
, char *argv
[])
461 BOOL joining_domain
= False
;
462 BOOL trust_account
= False
;
463 BOOL add_user
= False
;
464 BOOL disable_user
= False
;
465 BOOL enable_user
= False
;
466 BOOL set_no_password
= False
;
467 BOOL stdin_passwd_get
= False
;
468 char *user_name
= NULL
;
469 char *new_domain
= NULL
;
470 char *new_passwd
= NULL
;
471 char *old_passwd
= NULL
;
472 char *remote_machine
= NULL
;
474 while ((ch
= getopt(argc
, argv
, "adehmnj:r:sR:D:U:")) != EOF
) {
481 new_passwd
= "XXXXXX";
487 DEBUGLEVEL
= atoi(optarg
);
490 set_no_password
= True
;
491 new_passwd
= "NO PASSWORD";
493 remote_machine
= optarg
;
496 set_line_buffering(stdin
);
497 set_line_buffering(stdout
);
498 set_line_buffering(stderr
);
499 stdin_passwd_get
= True
;
502 lp_set_name_resolve_order(optarg
);
505 trust_account
= True
;
509 strupper(new_domain
);
510 joining_domain
= True
;
525 * Ensure add_user and either remote machine or join domain are
528 if(add_user
&& ((remote_machine
!= NULL
) || joining_domain
)) {
533 if (argc
!= 0) usage();
534 return join_domain(new_domain
, remote_machine
);
538 * Deal with root - can add a user, but only locally.
549 new_passwd
= argv
[1];
555 if (!user_name
&& (pwd
= getpwuid(0))) {
556 user_name
= xstrdup(pwd
->pw_name
);
560 fprintf(stderr
,"You must specify a username\n");
564 if (!remote_machine
&& !Get_Pwnam(user_name
, True
)) {
565 fprintf(stderr
, "User \"%s\" was not found in system password file.\n",
570 if (user_name
[strlen(user_name
)-1] == '$') {
571 user_name
[strlen(user_name
)-1] = 0;
575 /* add the $ automatically */
579 new_passwd
= xstrdup(user_name
);
580 strlower(new_passwd
);
583 slprintf(buf
, sizeof(buf
)-1, "%s$", user_name
);
587 if (remote_machine
!= NULL
) {
588 old_passwd
= get_pass("Old SMB password:",stdin_passwd_get
);
592 new_passwd
= prompt_for_new_password(stdin_passwd_get
);
595 if (!password_change(remote_machine
, user_name
, old_passwd
, new_passwd
,
596 add_user
, enable_user
, disable_user
, set_no_password
,
598 fprintf(stderr
,"Failed to change password entry for %s\n", user_name
);
603 printf("User %s disabled.\n", user_name
);
604 } else if(enable_user
) {
605 printf("User %s enabled.\n", user_name
);
606 } else if (set_no_password
) {
607 printf("User %s - set to no password.\n", user_name
);
609 printf("Password changed for user %s\n", user_name
);
615 /*************************************************************
616 handle password changing for non-root
617 *************************************************************/
618 static int process_nonroot(int argc
, char *argv
[])
620 struct passwd
*pwd
= NULL
;
622 BOOL stdin_passwd_get
= False
;
623 char *old_passwd
= NULL
;
624 char *remote_machine
= NULL
;
625 char *user_name
= NULL
;
626 char *new_passwd
= NULL
;
628 while ((ch
= getopt(argc
, argv
, "hD:r:sU:")) != EOF
) {
631 DEBUGLEVEL
= atoi(optarg
);
634 remote_machine
= optarg
;
637 set_line_buffering(stdin
);
638 set_line_buffering(stdout
);
639 set_line_buffering(stderr
);
640 stdin_passwd_get
= True
;
658 new_passwd
= argv
[0];
662 pwd
= getpwuid(getuid());
664 user_name
= xstrdup(pwd
->pw_name
);
666 fprintf(stderr
,"you don't exist - go away\n");
672 * A non-root user is always setting a password
673 * via a remote machine (even if that machine is
676 if (remote_machine
== NULL
) {
677 remote_machine
= "127.0.0.1";
681 if (remote_machine
!= NULL
) {
682 old_passwd
= get_pass("Old SMB password:",stdin_passwd_get
);
686 new_passwd
= prompt_for_new_password(stdin_passwd_get
);
690 printf("unable to get new password\n");
694 if (!password_change(remote_machine
, user_name
, old_passwd
, new_passwd
,
695 False
, False
, False
, False
, False
)) {
696 fprintf(stderr
,"Failed to change password for %s\n", user_name
);
700 printf("Password changed for user %s\n", user_name
);
706 /*********************************************************
708 **********************************************************/
709 int main(int argc
, char **argv
)
711 static pstring servicesf
= CONFIGFILE
;
715 setup_logging("smbpasswd", True
);
717 charset_initialise();
719 if(!initialize_password_db()) {
720 fprintf(stderr
, "Can't setup password database vectors.\n");
724 if (!lp_load(servicesf
,True
,False
,False
)) {
725 fprintf(stderr
, "Can't load %s - run testparm to debug it\n",
730 if(!get_myname(myhostname
,NULL
)) {
731 fprintf(stderr
, "unable to get my hostname.\n");
736 * Set the machine NETBIOS name if not already
737 * set from the config file.
740 if (!*global_myname
) {
742 fstrcpy(global_myname
, myhostname
);
743 p
= strchr(global_myname
, '.' );
746 strupper(global_myname
);
748 codepage_initialise(lp_client_code_page());
750 /* Check the effective uid - make sure we are not setuid */
751 if ((geteuid() == (uid_t
)0) && (getuid() != (uid_t
)0)) {
752 fprintf(stderr
, "smbpasswd must *NOT* be setuid root.\n");
757 return process_root(argc
, argv
);
760 return process_nonroot(argc
, argv
);