char -> uchar fix
[Samba/gbeck.git] / source / utils / smbpasswd.c
blobda7caacb117743f719d87fc0ec365bb0985cc8eb
1 /*
2 * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
3 * (C) Jeremy Allison 1995-1998
4 *
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)
8 * any later version.
9 *
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
13 * more details.
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.
20 #include "includes.h"
22 extern pstring scope;
23 extern pstring myhostname;
24 extern pstring global_myname;
25 extern fstring global_myworkgroup;
26 extern int DEBUGLEVEL;
29 /*********************************************************
30 a strdup with exit
31 **********************************************************/
32 static char *xstrdup(char *s)
34 s = strdup(s);
35 if (!s) {
36 fprintf(stderr,"out of memory\n");
37 exit(1);
39 return s;
43 /*********************************************************
44 Print command usage on stderr and die.
45 **********************************************************/
46 static void usage(void)
48 if (getuid() == 0) {
49 printf("smbpasswd [options] [username] [password]\n");
50 } else {
51 printf("smbpasswd [options] [password]\n");
53 printf("options:\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");
59 if (getuid() == 0) {
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");
68 exit(1);
71 /*********************************************************
72 Join a domain.
73 **********************************************************/
74 static int join_domain(char *domain, char *remote)
76 pstring remote_machine;
77 fstring trust_passwd;
78 unsigned char orig_trust_passwd_hash[16];
79 BOOL ret;
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
88 controller. */
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);
92 return 1;
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);
101 return 1;
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();
112 return 1;
116 * If we are given a remote machine assume this is the PDC.
119 if(remote == NULL) {
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();
127 return 1;
130 ret = change_trust_account_password( domain, remote_machine);
131 trust_password_unlock();
133 if(!ret) {
134 trust_password_delete( domain, global_myname);
135 fprintf(stderr,"Unable to join domain %s.\n",domain);
136 } else {
137 printf("Joined domain %s.\n",domain);
140 return (int)ret;
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;
156 size_t len;
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
163 * a null terminator.
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;
171 return(new_passwd);
175 /*************************************************************
176 Utility function to get passwords via tty or stdin
177 Used if the '-s' option is set to silently get passwords
178 to enable scripting.
179 *************************************************************/
180 static char *get_pass( char *prompt, BOOL stdin_get)
182 char *p;
183 if (stdin_get) {
184 p = stdin_new_passwd();
185 } else {
186 p = getpass(prompt);
188 return xstrdup(p);
191 /*************************************************************
192 Utility function to prompt for new password.
193 *************************************************************/
194 static char *prompt_for_new_password(BOOL stdin_get)
196 char *p;
197 fstring new_passwd;
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");
209 return NULL;
212 return xstrdup(p);
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;
226 struct in_addr ip;
228 if(!resolve_name( remote_machine, &ip, 0x20)) {
229 fprintf(stderr, "unable to find an IP address for machine %s.\n",
230 remote_machine );
231 return False;
234 ZERO_STRUCT(cli);
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) );
239 return False;
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) );
248 cli_shutdown(&cli);
249 return False;
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) );
257 cli_shutdown(&cli);
258 return False;
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) );
270 cli_shutdown(&cli);
271 return False;
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) );
277 cli_shutdown(&cli);
278 return False;
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) );
284 cli_shutdown(&cli);
285 return False;
288 cli_shutdown(&cli);
289 return True;
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);
309 if(disable_user) {
310 new_smb_pwent.acct_ctrl |= ACB_DISABLED;
311 } else if (set_no_password) {
312 new_smb_pwent.acct_ctrl |= ACB_PWNOTREQ;
313 } else {
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,
327 char *new_passwd)
329 struct passwd *pwd;
330 void *vp;
331 struct smb_passwd *smb_pwent;
332 uchar new_p16[16];
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",
343 user_name);
344 return False;
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) {
355 FILE *fp;
356 fprintf(stderr,"smbpasswd file did not exist - attempting to create it.\n");
357 fp = fopen(lp_smb_passwd_file(), "w");
358 if (fp) {
359 fprintf(fp, "# Samba SMB password file\n");
360 fclose(fp);
361 vp = startsmbpwent(True);
365 if (!vp) {
366 perror(lp_smb_passwd_file());
367 return False;
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",
375 pwd->pw_name);
376 endsmbpwent(vp);
377 return False;
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);
383 endsmbpwent(vp);
384 return True;
385 } else {
386 fprintf(stderr, "Failed to add entry for user %s.\n", user_name);
387 endsmbpwent(vp);
388 return False;
390 } else {
391 /* the entry already existed */
392 add_user = False;
396 * We are root - just write the new password
397 * and the valid last change time.
400 if(disable_user) {
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;
413 } else {
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",
421 pwd->pw_name);
422 endsmbpwent(vp);
423 return False;
426 endsmbpwent(vp);
428 return True;
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,
439 BOOL trust_account)
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 */
444 return False;
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[])
459 struct passwd *pwd;
460 int ch;
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) {
475 switch(ch) {
476 case 'a':
477 add_user = True;
478 break;
479 case 'd':
480 disable_user = True;
481 new_passwd = "XXXXXX";
482 break;
483 case 'e':
484 enable_user = True;
485 break;
486 case 'D':
487 DEBUGLEVEL = atoi(optarg);
488 break;
489 case 'n':
490 set_no_password = True;
491 new_passwd = "NO PASSWORD";
492 case 'r':
493 remote_machine = optarg;
494 break;
495 case 's':
496 set_line_buffering(stdin);
497 set_line_buffering(stdout);
498 set_line_buffering(stderr);
499 stdin_passwd_get = True;
500 break;
501 case 'R':
502 lp_set_name_resolve_order(optarg);
503 break;
504 case 'm':
505 trust_account = True;
506 break;
507 case 'j':
508 new_domain = optarg;
509 strupper(new_domain);
510 joining_domain = True;
511 break;
512 case 'U':
513 user_name = optarg;
514 break;
515 default:
516 usage();
520 argc -= optind;
521 argv += optind;
525 * Ensure add_user and either remote machine or join domain are
526 * not both set.
528 if(add_user && ((remote_machine != NULL) || joining_domain)) {
529 usage();
532 if(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.
541 switch(argc) {
542 case 0:
543 break;
544 case 1:
545 user_name = argv[0];
546 break;
547 case 2:
548 user_name = argv[0];
549 new_passwd = argv[1];
550 break;
551 default:
552 usage();
555 if (!user_name && (pwd = getpwuid(0))) {
556 user_name = xstrdup(pwd->pw_name);
559 if (!user_name) {
560 fprintf(stderr,"You must specify a username\n");
561 exit(1);
564 if (!remote_machine && !Get_Pwnam(user_name, True)) {
565 fprintf(stderr, "User \"%s\" was not found in system password file.\n",
566 user_name);
567 exit(1);
570 if (user_name[strlen(user_name)-1] == '$') {
571 user_name[strlen(user_name)-1] = 0;
574 if (trust_account) {
575 /* add the $ automatically */
576 static fstring buf;
578 if (add_user) {
579 new_passwd = xstrdup(user_name);
580 strlower(new_passwd);
583 slprintf(buf, sizeof(buf)-1, "%s$", user_name);
584 user_name = buf;
587 if (remote_machine != NULL) {
588 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
591 if (!new_passwd) {
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,
597 trust_account)) {
598 fprintf(stderr,"Failed to change password entry for %s\n", user_name);
599 return 1;
602 if(disable_user) {
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);
608 } else {
609 printf("Password changed for user %s\n", user_name);
611 return 0;
615 /*************************************************************
616 handle password changing for non-root
617 *************************************************************/
618 static int process_nonroot(int argc, char *argv[])
620 struct passwd *pwd = NULL;
621 int ch;
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) {
629 switch(ch) {
630 case 'D':
631 DEBUGLEVEL = atoi(optarg);
632 break;
633 case 'r':
634 remote_machine = optarg;
635 break;
636 case 's':
637 set_line_buffering(stdin);
638 set_line_buffering(stdout);
639 set_line_buffering(stderr);
640 stdin_passwd_get = True;
641 break;
642 case 'U':
643 user_name = optarg;
644 break;
645 default:
646 usage();
650 argc -= optind;
651 argv += optind;
653 if(argc > 1) {
654 usage();
657 if (argc == 1) {
658 new_passwd = argv[0];
661 if (!user_name) {
662 pwd = getpwuid(getuid());
663 if (pwd) {
664 user_name = xstrdup(pwd->pw_name);
665 } else {
666 fprintf(stderr,"you don't exist - go away\n");
667 exit(1);
672 * A non-root user is always setting a password
673 * via a remote machine (even if that machine is
674 * localhost).
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);
685 if (!new_passwd) {
686 new_passwd = prompt_for_new_password(stdin_passwd_get);
689 if (!new_passwd) {
690 printf("unable to get new password\n");
691 exit(0);
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);
697 return 1;
700 printf("Password changed for user %s\n", user_name);
701 return 0;
706 /*********************************************************
707 Start here.
708 **********************************************************/
709 int main(int argc, char **argv)
711 static pstring servicesf = CONFIGFILE;
713 TimeInit();
715 setup_logging("smbpasswd", True);
717 charset_initialise();
719 if(!initialize_password_db()) {
720 fprintf(stderr, "Can't setup password database vectors.\n");
721 exit(1);
724 if (!lp_load(servicesf,True,False,False)) {
725 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
726 servicesf);
727 exit(1);
730 if(!get_myname(myhostname,NULL)) {
731 fprintf(stderr, "unable to get my hostname.\n");
732 exit(1);
736 * Set the machine NETBIOS name if not already
737 * set from the config file.
740 if (!*global_myname) {
741 char *p;
742 fstrcpy(global_myname, myhostname);
743 p = strchr(global_myname, '.' );
744 if (p) *p = 0;
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");
753 exit(1);
756 if (getuid() == 0) {
757 return process_root(argc, argv);
760 return process_nonroot(argc, argv);