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