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