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