3b87687c261609fdfbdf979b94eec5b42adeb21a
[kai/samba.git] / source3 / utils / smbpasswd.c
1 /*
2  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module.
3  * Copyright (C) Jeremy Allison               1995-1999
4  * Copyright (C) Luke Kenneth Casson Leighton 1996-1999
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  * 
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with
17  * this program; if not, write to the Free Software Foundation, Inc., 675
18  * Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22
23 extern pstring myhostname;
24 extern pstring global_myname;
25 extern pstring global_myworkgroup;
26 extern int DEBUGLEVEL;
27
28 /*
29  * Next two lines needed for SunOS and don't
30  * hurt anything else...
31  */
32 extern char *optarg;
33 extern int optind;
34
35 /*********************************************************
36 a strdup with exit
37 **********************************************************/
38 static char *xstrdup(char *s)
39 {
40         s = strdup(s);
41         if (!s) {
42                 fprintf(stderr,"out of memory\n");
43                 exit(1);
44         }
45         return s;
46 }
47
48
49 /*********************************************************
50  Print command usage on stderr and die.
51 **********************************************************/
52 static void usage(void)
53 {
54         if (getuid() == 0) {
55                 printf("smbpasswd [options] [username] [password]\n");
56         } else {
57                 printf("smbpasswd [options] [password]\n");
58         }
59         printf("options:\n");
60         printf("  -s                   use stdin for password prompt\n");
61         printf("  -D LEVEL             debug level\n");
62         printf("  -U USER              remote username\n");
63         printf("  -r MACHINE           remote machine\n");
64
65         if (getuid() == 0) {
66                 printf("  -R ORDER             name resolve order\n");
67                 printf("  -j DOMAIN            join domain name\n");
68                 printf("  -S                   synchronise with PDC (if we are BDC)\n");
69                 printf("  -a                   add user\n");
70                 printf("  -d                   disable user\n");
71                 printf("  -e                   enable user\n");
72                 printf("  -n                   set no password\n");
73                 printf("  -m                   workstation trust account\n");
74                 printf("  -b                   backup domain controller account\n");
75                 printf("  -i                   inter-domain trust account\n");
76                 printf("  -p                   user cannot change password\n");
77                 printf("  -x                   user can change password\n");
78         }
79         
80         exit(1);
81 }
82
83 /*********************************************************
84 Join a domain.
85 **********************************************************/
86 static int create_interdomain_trust_acct(char *domain, char *name)
87 {
88         fstring trust_passwd;
89         unsigned char hash[16];
90         uint16 sec_chan;
91
92         switch (lp_server_role())
93         {
94                 case ROLE_DOMAIN_PDC:
95                 {
96                         DEBUG(0, ("Joining domain - we are PDC\n"));
97                         sec_chan = SEC_CHAN_DOMAIN;
98                         break;
99                 }
100                 case ROLE_DOMAIN_BDC:
101                 {
102                         DEBUG(0, ("Cannot set up inter-domain trust as BDC!\n"));
103                         return 1;
104                 }
105                 default:
106                 {
107                         DEBUG(0, ("Cannot set up inter-domain trust as workstation!\n"));
108                         return 1;
109                 }
110         }
111
112 #if 0
113         pstrcpy(remote_machine, remote ? remote : lp_passwordserver());
114
115         if (!remote_machine[0])
116         {
117                 fprintf(stderr, "You must specify the PDC via 'password server' or -r.");
118                 return 1;
119         }
120 #endif
121
122         fstrcpy(trust_passwd, name);
123         strlower(trust_passwd);
124         E_md4hash( (uchar *)trust_passwd, hash);
125
126         if (!create_trust_account_file(domain, name, hash))
127         {
128                 return 1;
129         }
130         
131 #if 0
132         if(!change_trust_account_password(domain, remote_machine, sec_chan))
133         {
134                 fprintf(stderr,"Unable to join domain %s.\n",domain);
135                 return 1;
136         }
137 #endif
138         printf("Created Inter-Domain Trust Account for %s.\n",domain);
139         return 0;
140 }
141
142 /*********************************************************
143 Join a domain.
144 **********************************************************/
145 static int join_domain(char *domain, char *remote)
146 {
147         pstring remote_machine;
148         fstring trust_passwd;
149         unsigned char orig_trust_passwd_hash[16];
150         uint16 sec_chan;
151
152         switch (lp_server_role())
153         {
154                 case ROLE_DOMAIN_PDC:
155                 {
156                         DEBUG(0, ("Cannot join domain - we are PDC!\n"));
157                         return 1;
158                 }
159                 case ROLE_DOMAIN_BDC:
160                 {
161                         DEBUG(0, ("Joining Domain as BDC\n"));
162                         sec_chan = SEC_CHAN_BDC;
163                         break;
164                 }
165                 default:
166                 {
167                         DEBUG(0, ("Joining Domain as Workstation\n"));
168                         sec_chan = SEC_CHAN_WKSTA;
169                 }
170         }
171
172         pstrcpy(remote_machine, remote ? remote : lp_passwordserver());
173
174         if (!remote_machine[0])
175         {
176                 fprintf(stderr, "You must specify the PDC via 'password server' or -r.");
177                 return 1;
178         }
179
180         fstrcpy(trust_passwd, global_myname);
181         strlower(trust_passwd);
182         E_md4hash( (uchar *)trust_passwd, orig_trust_passwd_hash);
183
184         if (!create_trust_account_file(domain, global_myname, trust_passwd))
185         {
186                 return 1;
187         }
188         
189         if(!change_trust_account_password(domain, remote_machine, sec_chan))
190         {
191                 fprintf(stderr,"Unable to join domain %s.\n",domain);
192                 return 1;
193         }
194
195         printf("Joined domain %s.\n",domain);
196         return 0;
197 }
198
199
200 static void set_line_buffering(FILE *f)
201 {
202         setvbuf(f, NULL, _IOLBF, 0);
203 }
204
205 /*************************************************************
206  Utility function to prompt for passwords from stdin. Each
207  password entered must end with a newline.
208 *************************************************************/
209 static char *stdin_new_passwd(void)
210 {
211         static fstring new_passwd;
212         size_t len;
213
214         ZERO_ARRAY(new_passwd);
215
216         /*
217          * if no error is reported from fgets() and string at least contains
218          * the newline that ends the password, then replace the newline with
219          * a null terminator.
220          */
221         if ( fgets(new_passwd, sizeof(new_passwd), stdin) != NULL) {
222                 if ((len = strlen(new_passwd)) > 0) {
223                         if(new_passwd[len-1] == '\n')
224                                 new_passwd[len - 1] = 0; 
225                 }
226         }
227         return(new_passwd);
228 }
229
230
231 /*************************************************************
232  Utility function to get passwords via tty or stdin
233  Used if the '-s' option is set to silently get passwords
234  to enable scripting.
235 *************************************************************/
236 static char *get_pass( char *prompt, BOOL stdin_get)
237 {
238         char *p;
239         if (stdin_get) {
240                 p = stdin_new_passwd();
241         } else {
242                 p = getpass(prompt);
243         }
244         return xstrdup(p);
245 }
246
247 /*************************************************************
248  Utility function to prompt for new password.
249 *************************************************************/
250 static char *prompt_for_new_password(BOOL stdin_get)
251 {
252         char *p;
253         fstring new_passwd;
254
255         ZERO_ARRAY(new_passwd);
256  
257         p = get_pass("New SMB password:", stdin_get);
258
259         fstrcpy(new_passwd, p);
260
261         p = get_pass("Retype new SMB password:", stdin_get);
262
263         if (strcmp(p, new_passwd)) {
264                 fprintf(stderr, "Mismatch - password unchanged.\n");
265                 return NULL;
266         }
267
268         return xstrdup(p);
269 }
270
271
272 /*************************************************************
273 change a password either locally or remotely
274 *************************************************************/
275 static BOOL password_change(const char *remote_machine, char *user_name, 
276                                 char *old_passwd, char *new_passwd, 
277                                 BOOL add_user, 
278                                 uint16 acb_info, uint16 acb_mask)
279 {
280         BOOL ret;
281         pstring err_str;
282         pstring msg_str;
283
284         if (remote_machine != NULL)
285         {
286                 if (add_user ||
287                     IS_BITS_SET_SOME(acb_info, ACB_PWNOTREQ | ACB_WSTRUST | ACB_DOMTRUST | ACB_SVRTRUST) ||
288                     (IS_BITS_SET_SOME(acb_mask, ACB_DISABLED) && 
289                      IS_BITS_CLR_ALL(acb_info, ACB_DISABLED)))
290                 {
291                         /* these things can't be done remotely yet */
292                         return False;
293                 }
294                 ret = remote_password_change(remote_machine, user_name, 
295                                             old_passwd, new_passwd,
296                                             err_str, sizeof(err_str));
297                 if (*err_str != 0)
298                 {
299                         fprintf(stderr, err_str);
300                 }
301                 return ret;
302         }
303         
304         ret = local_password_change(user_name, add_user, acb_info, acb_mask,
305                                      new_passwd, 
306                                      err_str, sizeof(err_str),
307                                      msg_str, sizeof(msg_str));
308
309         if (*msg_str != 0)
310         {
311                 printf(msg_str);
312         }
313         if (*err_str != 0)
314         {
315                 fprintf(stderr, err_str);
316         }
317
318         return ret;
319 }
320
321
322 /*************************************************************
323 handle password changing for root
324 *************************************************************/
325 static int process_root(int argc, char *argv[])
326 {
327         struct passwd  *pwd;
328         int ch;
329         uint16 acb_info = 0;
330         uint16 acb_mask = 0;
331         BOOL joining_domain = False;
332         BOOL sam_sync = False;
333         BOOL wks_trust_account = False;
334         BOOL srv_trust_account = False;
335         BOOL dom_trust_account = False;
336         BOOL add_user = False;
337         BOOL disable_user = False;
338         BOOL enable_user = False;
339         BOOL set_no_password = False;
340         BOOL stdin_passwd_get = False;
341         BOOL lock_password = False;
342         BOOL unlock_password = False;
343         char *user_name = NULL;
344         char *new_domain = NULL;
345         char *new_passwd = NULL;
346         char *old_passwd = NULL;
347         char *remote_machine = NULL;
348         int ret;
349
350         while ((ch = getopt(argc, argv, "abdehimnpxj:Sr:sR:D:U:")) != EOF)
351         {
352                 switch(ch)
353                 {
354                         case 'a':
355                         {
356                                 add_user = True;
357                                 break;
358                         }
359                         case 'd':
360                         {
361                                 disable_user = True;
362                                 new_passwd = "XXXXXX";
363                                 break;
364                         }
365                         case 'e':
366                         {
367                                 enable_user = True;
368                                 break;
369                         }
370                         case 'D':
371                         {
372                                 DEBUGLEVEL = atoi(optarg);
373                                 break;
374                         }
375                         case 'n':
376                         {
377                                 set_no_password = True;
378                                 new_passwd = "NO PASSWORD";
379                         }
380                         case 'r':
381                         {
382                                 remote_machine = optarg;
383                                 break;
384                         }
385                         case 's':
386                         {
387                                 set_line_buffering(stdin);
388                                 set_line_buffering(stdout);
389                                 set_line_buffering(stderr);
390                                 stdin_passwd_get = True;
391                                 break;
392                         }
393                         case 'R':
394                         {
395                                 lp_set_name_resolve_order(optarg);
396                                 break;
397                         }
398                         case 'i':
399                         {
400                                 dom_trust_account = True;
401                                 break;
402                         }
403                         case 'b':
404                         {
405                                 srv_trust_account = True;
406                                 break;
407                         }
408                         case 'm':
409                         {
410                                 wks_trust_account = True;
411                                 break;
412                         }
413                         case 'j':
414                         {
415                                 new_domain = optarg;
416                                 strupper(new_domain);
417                                 joining_domain = True;
418                                 break;
419                         }
420                         case 'S':
421                         {
422                                 sam_sync = True;
423                                 break;
424                         }
425                         case 'U':
426                         {
427                                 user_name = optarg;
428                                 break;
429                         }
430                         case 'p':
431                         {
432                                 lock_password = True;
433                                 break;
434                         }
435                         case 'x':
436                         {
437                                 unlock_password = True;
438                                 break;
439                         }
440                         default:
441                         {
442                                 usage();
443                         }
444                 }
445         }
446         
447         argc -= optind;
448         argv += optind;
449
450         /*
451          * Ensure add_user and either remote machine or join domain are
452          * not both set.
453          */     
454         if (add_user && ((remote_machine != NULL) || joining_domain))
455         {
456                 usage();
457         }
458
459         if (sam_sync && lp_server_role() != ROLE_DOMAIN_BDC) {
460                 fprintf(stderr, "The -S option can only be used on a Backup Domain Controller.\n");
461                 return 1;
462         }
463         
464         if (joining_domain)
465         {
466                 if (!dom_trust_account)
467                 {
468                         if (argc != 0) usage();
469                         ret = join_domain(new_domain, remote_machine);
470
471                         if ((ret != 0) || (!sam_sync))
472                                 return ret;
473                 }
474         }
475
476         if (sam_sync)
477         {
478                 return synchronise_passdb();
479         }
480
481         /*
482          * Deal with root - can add a user, but only locally.
483          */
484
485         switch(argc) {
486         case 0:
487                 break;
488         case 1:
489                 user_name = argv[0];
490                 break;
491         case 2:
492                 user_name = argv[0];
493                 new_passwd = argv[1];
494                 break;
495         default:
496                 usage();
497         }
498
499         if (!user_name && (pwd = getpwuid(0))) {
500                 user_name = xstrdup(pwd->pw_name);
501         } 
502
503         if (!user_name) {
504                 fprintf(stderr,"You must specify a username\n");
505                 exit(1);
506         }
507
508         if (wks_trust_account || srv_trust_account || dom_trust_account)
509         {
510                 /* add the $ automatically */
511                 static fstring buf;
512
513                 /*
514                  * Remove any trailing '$' before we
515                  * generate the initial machine password.
516                  */
517
518                 if (user_name[strlen(user_name)-1] == '$') {
519                         user_name[strlen(user_name)-1] = 0;
520                 }
521
522                 if (add_user) {
523                         new_passwd = xstrdup(user_name);
524                         strlower(new_passwd);
525                 }
526
527                 /*
528                  * Now ensure the username ends in '$' for
529                  * the machine add.
530                  */
531
532                 slprintf(buf, sizeof(buf)-1, "%s$", user_name);
533                 user_name = buf;
534         }
535
536         if (!remote_machine && !Get_Pwnam(user_name, True)) {
537                 fprintf(stderr, "User \"%s\" was not found in system password file.\n", 
538                         user_name);
539                 exit(1);
540         }
541
542         if (joining_domain)
543         {
544                 if (dom_trust_account)
545                 {
546                         ret = create_interdomain_trust_acct(new_domain,
547                                                             global_myworkgroup);
548
549                         if ((ret != 0) || (!sam_sync))
550                                 return ret;
551                 }
552         }
553
554         if (remote_machine != NULL) {
555                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
556         }
557         
558         if (!new_passwd)
559         {
560                 /*
561                  * If we are trying to enable a user, first we need to find out
562                  * if they are using a modern version of the smbpasswd file that
563                  * disables a user by just writing a flag into the file. If so
564                  * then we can re-enable a user without prompting for a new
565                  * password. If not (ie. they have a no stored password in the
566                  * smbpasswd file) then we need to prompt for a new password.
567                  */
568
569                 if (enable_user)
570                 {
571                         struct smb_passwd *smb_pass = getsmbpwnam(user_name);
572                         if((smb_pass != NULL) && (smb_pass->smb_passwd != NULL))
573                         {
574                                 new_passwd = "XXXX"; /* Don't care. */
575                         }
576                 }
577
578                 if(!new_passwd)
579                 {
580                         new_passwd = prompt_for_new_password(stdin_passwd_get);
581                 }
582         }
583         
584         if (enable_user)
585         {
586                 acb_mask |= ACB_DISABLED;
587                 acb_info &= ~ACB_DISABLED;
588         }
589
590         if (disable_user)
591         {
592                 acb_mask |= ACB_DISABLED;
593                 acb_info |= ACB_DISABLED;
594         }
595
596         if (set_no_password)
597         {
598                 acb_mask |= ACB_PWNOTREQ;
599                 acb_info |= ACB_PWNOTREQ;
600         }
601
602         if (lock_password)
603         {
604                 acb_mask |= ACB_PWLOCK;
605                 acb_info |= ACB_PWLOCK;
606         }
607
608         if (unlock_password)
609         {
610                 acb_mask |= ACB_PWLOCK;
611                 acb_info &= ~ACB_PWLOCK;
612         }
613         
614         if (wks_trust_account)
615         {
616                 acb_mask |= ACB_WSTRUST;
617                 acb_info |= ACB_WSTRUST;
618         }
619         else if (srv_trust_account)
620         {
621                 acb_mask |= ACB_SVRTRUST;
622                 acb_info |= ACB_SVRTRUST;
623         }
624         else if (dom_trust_account)
625         {
626                 acb_mask |= ACB_DOMTRUST;
627                 acb_info |= ACB_DOMTRUST;
628         }
629         else
630         {
631                 acb_mask |= ACB_NORMAL;
632                 acb_info |= ACB_NORMAL;
633         }
634
635         if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
636                              add_user, acb_info, acb_mask))
637         {
638                 fprintf(stderr,"Failed to change password entry for %s\n", user_name);
639                 return 1;
640         } 
641
642         if (disable_user)
643         {
644                 printf("User %s disabled.\n", user_name);
645         }
646         if (enable_user)
647         {
648                 printf("User %s enabled.\n", user_name);
649         }
650         if (set_no_password)
651         {
652                 printf("User %s - set to no password.\n", user_name);
653         }
654         if (!disable_user && !enable_user && !set_no_password)
655         {
656                 printf("Password changed for user %s\n", user_name);
657         }
658         return 0;
659 }
660
661
662 /*************************************************************
663 handle password changing for non-root
664 *************************************************************/
665 static int process_nonroot(int argc, char *argv[])
666 {
667         struct passwd  *pwd = NULL;
668         int ch;
669         BOOL stdin_passwd_get = False;
670         char *old_passwd = NULL;
671         char *remote_machine = NULL;
672         char *user_name = NULL;
673         char *new_passwd = NULL;
674         
675         while ((ch = getopt(argc, argv, "hD:r:sU:")) != EOF)
676         {
677                 switch(ch)
678                 {
679                 case 'D':
680                         DEBUGLEVEL = atoi(optarg);
681                         break;
682                 case 'r':
683                         remote_machine = optarg;
684                         break;
685                 case 's':
686                         set_line_buffering(stdin);
687                         set_line_buffering(stdout);
688                         set_line_buffering(stderr);
689                         stdin_passwd_get = True;
690                         break;
691                 case 'U':
692                         user_name = optarg;
693                         break;
694                 default:
695                         usage();
696                 }
697         }
698         
699         argc -= optind;
700         argv += optind;
701
702         if(argc > 1) {
703                 usage();
704         }
705         
706         if (argc == 1) {
707                 new_passwd = argv[0];
708         }
709         
710         if (!user_name) {
711                 pwd = getpwuid(getuid());
712                 if (pwd) {
713                         user_name = xstrdup(pwd->pw_name);
714                 } else {
715                         fprintf(stderr,"you don't exist - go away\n");
716                         exit(1);
717                 }
718         }
719         
720         /*
721          * A non-root user is always setting a password
722          * via a remote machine (even if that machine is
723          * localhost).
724          */     
725         if (remote_machine == NULL) {
726                 remote_machine = "127.0.0.1";
727         }
728
729         if (remote_machine != NULL) {
730                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
731         }
732         
733         if (!new_passwd) {
734                 new_passwd = prompt_for_new_password(stdin_passwd_get);
735         }
736         
737         if (!new_passwd) {
738                 printf("unable to get new password\n");
739                 exit(0);
740         }
741
742         if (!password_change(remote_machine, user_name,
743                              old_passwd, new_passwd,
744                              False, 0x0, 0x0))
745         {
746                 fprintf(stderr,"Failed to change password for %s\n", user_name);
747                 return 1;
748         }
749
750         printf("Password changed for user %s\n", user_name);
751         return 0;
752 }
753
754
755
756 /*********************************************************
757  Start here.
758 **********************************************************/
759 int main(int argc, char **argv)
760 {       
761         static pstring servicesf = CONFIGFILE;
762
763         TimeInit();
764         
765         setup_logging("smbpasswd", True);
766         
767         charset_initialise();
768         
769         if (!lp_load(servicesf,True,False,False)) {
770                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
771                         servicesf);
772                 exit(1);
773         }
774
775         if(!get_myname(myhostname,NULL)) {
776                 fprintf(stderr, "unable to get my hostname.\n");
777                 exit(1);
778         }
779
780         /*
781          * Set the machine NETBIOS name if not already
782          * set from the config file. 
783          */ 
784     
785         if (!*global_myname) {   
786                 char *p;
787                 fstrcpy(global_myname, myhostname);
788                 p = strchr(global_myname, '.' );
789                 if (p) *p = 0;
790         }           
791         strupper(global_myname);
792
793         codepage_initialise(lp_client_code_page());
794
795         load_interfaces();
796
797         if(!pwdb_initialise(False))
798         {
799                 fprintf(stderr, "Can't setup password database vectors.\n");
800                 exit(1);
801         }
802
803         /* Check the effective uid - make sure we are not setuid */
804         if ((geteuid() == (uid_t)0) && (getuid() != (uid_t)0)) {
805                 fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
806                 exit(1);
807         }
808
809         if (getuid() == 0) {
810                 return process_root(argc, argv);
811         } 
812
813         return process_nonroot(argc, argv);
814 }