merge from 2.2 - allow -c option to specify smb.conf file
[amitay/samba.git] / source3 / utils / smbpasswd.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * Copyright (C) Jeremy Allison 1995-1998
4  * Copyright (C) Tim Potter     2001
5  * 
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) 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 #include "includes.h"
21
22 extern pstring global_myname;
23 extern BOOL AllowDebugChange;
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 /* forced running in root-mode */
33 static BOOL local_mode;
34 static BOOL got_pass = False, got_username = False;
35 static int local_flags = 0;
36 static BOOL stdin_passwd_get = False;
37 static fstring user_name, user_password;
38 static char *new_domain = NULL;
39 static char *new_passwd = NULL;
40 static char *old_passwd = NULL;
41 static char *remote_machine = NULL;
42 static pstring configfile;
43
44 #ifdef WITH_LDAP_SAM
45 static fstring ldap_secret;
46 #endif
47
48 /*********************************************************
49  Print command usage on stderr and die.
50 **********************************************************/
51 static void usage(void)
52 {
53         printf("When run by root:\n");
54         printf("    smbpasswd [options] [username] [password]\n");
55         printf("otherwise:\n");
56         printf("    smbpasswd [options] [password]\n\n");
57
58         printf("options:\n");
59         printf("  -c smb.conf file     Use the given path to the smb.conf file\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         printf("extra options when run by root or in local mode:\n");
66         printf("  -L                   local mode\n");
67         printf("  -R ORDER             name resolve order\n");
68         printf("  -a                   add user\n");
69         printf("  -x                   delete user\n");
70         printf("  -d                   disable user\n");
71         printf("  -e                   enable user\n");
72         printf("  -n                   set no password\n");
73         printf("  -m                   machine trust account\n");
74         printf("  -i                   interdomain trust account\n");
75 #ifdef WITH_LDAP_SAM
76         printf("  -w                   ldap admin password\n");
77 #endif
78
79         exit(1);
80 }
81
82 static void set_line_buffering(FILE *f)
83 {
84         setvbuf(f, NULL, _IOLBF, 0);
85 }
86
87 /*******************************************************************
88  Process command line options
89  ******************************************************************/
90 static void process_options(int argc, char **argv, BOOL amroot)
91 {
92         int ch;
93
94         if (amroot)
95                 local_flags = LOCAL_SET_PASSWORD;
96
97         ZERO_STRUCT(user_name);
98         ZERO_STRUCT(user_password);
99
100         user_name[0] = '\0';
101
102         while ((ch = getopt(argc, argv, "c:axdehmnj:r:sw:R:D:U:L")) != EOF) {
103                 switch(ch) {
104                 case 'L':
105                         local_mode = amroot = True;
106                         break;
107                 case 'c':
108                         pstrcpy(configfile,optarg);
109                         break;
110                 case 'a':
111                         if (!amroot) goto bad_args;
112                         local_flags |= LOCAL_ADD_USER;
113                         break;
114                 case 'x':
115                         if (!amroot) goto bad_args;
116                         local_flags |= LOCAL_DELETE_USER;
117                         local_flags &= ~LOCAL_SET_PASSWORD;
118                         break;
119                 case 'd':
120                         if (!amroot) goto bad_args;
121                         local_flags |= LOCAL_DISABLE_USER;
122                         local_flags &= ~LOCAL_SET_PASSWORD;
123                         break;
124                 case 'e':
125                         if (!amroot) goto bad_args;
126                         local_flags |= LOCAL_ENABLE_USER;
127                         local_flags &= ~LOCAL_SET_PASSWORD;
128                         break;
129                 case 'm':
130                         if (!amroot) goto bad_args;
131                         local_flags |= LOCAL_TRUST_ACCOUNT;
132                         break;
133                 case 'i':
134                         if (!amroot) goto bad_args;
135                         local_flags |= LOCAL_INTERDOM_ACCOUNT;
136                         break;
137                 case 'j':
138                         if (!amroot) goto bad_args;
139                         d_printf("See 'net rpc join' for this functionality\n");
140                         exit(1);
141                         break;
142                 case 'n':
143                         if (!amroot) goto bad_args;
144                         local_flags |= LOCAL_SET_NO_PASSWORD;
145                         new_passwd = smb_xstrdup("NO PASSWORD");
146                         break;
147                 case 'r':
148                         remote_machine = optarg;
149                         break;
150                 case 's':
151                         set_line_buffering(stdin);
152                         set_line_buffering(stdout);
153                         set_line_buffering(stderr);
154                         stdin_passwd_get = True;
155                         break;
156                 case 'w':
157                         if (!amroot) goto bad_args;
158 #ifdef WITH_LDAP_SAM
159                         local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
160                         fstrcpy(ldap_secret, optarg);
161                         break;
162 #else
163                         printf("-w not available unless configured --with-ldap\n");
164                         goto bad_args;
165 #endif                  
166                 case 'R':
167                         if (!amroot) goto bad_args;
168                         lp_set_name_resolve_order(optarg);
169                         break;
170                 case 'D':
171                         DEBUGLEVEL = atoi(optarg);
172                         break;
173                 case 'U': {
174                         char *lp;
175
176                         got_username = True;
177                         fstrcpy(user_name, optarg);
178
179                         if ((lp = strchr(user_name, '%'))) {
180                                 *lp = 0;
181                                 fstrcpy(user_password, lp + 1);
182                                 got_pass = True;
183                                 memset(strchr_m(optarg, '%') + 1, 'X',
184                                        strlen(user_password));
185                         }
186
187                         break;
188                 }
189                 case 'h':
190                 default:
191 bad_args:
192                         usage();
193                 }
194         }
195         
196         argc -= optind;
197         argv += optind;
198
199         switch(argc) {
200         case 0:
201                 if (!got_username)
202                         fstrcpy(user_name, "");
203                 break;
204         case 1:
205                 if (!amroot) {
206                         new_passwd = argv[0];
207                         break;
208                 }
209                 if (got_username)
210                         usage();
211                 fstrcpy(user_name, argv[0]);
212                 break;
213         case 2:
214                 if (!amroot || got_username || got_pass)
215                         usage();
216                 fstrcpy(user_name, argv[0]);
217                 new_passwd = smb_xstrdup(argv[1]);
218                 break;
219         default:
220                 usage();
221         }
222
223 }
224
225 /*************************************************************
226  Utility function to prompt for passwords from stdin. Each
227  password entered must end with a newline.
228 *************************************************************/
229 static char *stdin_new_passwd(void)
230 {
231         static fstring new_passwd;
232         size_t len;
233
234         ZERO_ARRAY(new_passwd);
235
236         /*
237          * if no error is reported from fgets() and string at least contains
238          * the newline that ends the password, then replace the newline with
239          * a null terminator.
240          */
241         if ( fgets(new_passwd, sizeof(new_passwd), stdin) != NULL) {
242                 if ((len = strlen(new_passwd)) > 0) {
243                         if(new_passwd[len-1] == '\n')
244                                 new_passwd[len - 1] = 0; 
245                 }
246         }
247         return(new_passwd);
248 }
249
250
251 /*************************************************************
252  Utility function to get passwords via tty or stdin
253  Used if the '-s' option is set to silently get passwords
254  to enable scripting.
255 *************************************************************/
256 static char *get_pass( char *prompt, BOOL stdin_get)
257 {
258         char *p;
259         if (stdin_get) {
260                 p = stdin_new_passwd();
261         } else {
262                 p = getpass(prompt);
263         }
264         return smb_xstrdup(p);
265 }
266
267 /*************************************************************
268  Utility function to prompt for new password.
269 *************************************************************/
270 static char *prompt_for_new_password(BOOL stdin_get)
271 {
272         char *p;
273         fstring new_passwd;
274
275         ZERO_ARRAY(new_passwd);
276  
277         p = get_pass("New SMB password:", stdin_get);
278
279         fstrcpy(new_passwd, p);
280         SAFE_FREE(p);
281
282         p = get_pass("Retype new SMB password:", stdin_get);
283
284         if (strcmp(p, new_passwd)) {
285                 fprintf(stderr, "Mismatch - password unchanged.\n");
286                 ZERO_ARRAY(new_passwd);
287                 SAFE_FREE(p);
288                 return NULL;
289         }
290
291         return p;
292 }
293
294
295 /*************************************************************
296  Change a password either locally or remotely.
297 *************************************************************/
298
299 static BOOL password_change(const char *remote_machine, char *user_name, 
300                             char *old_passwd, char *new_passwd, int local_flags)
301 {
302         BOOL ret;
303         pstring err_str;
304         pstring msg_str;
305
306         if (remote_machine != NULL) {
307                 if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
308                                                         LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
309                         /* these things can't be done remotely yet */
310                         return False;
311                 }
312                 ret = remote_password_change(remote_machine, user_name, 
313                                              old_passwd, new_passwd, err_str, sizeof(err_str));
314                 if(*err_str)
315                         fprintf(stderr, err_str);
316                 return ret;
317         }
318         
319         ret = local_password_change(user_name, local_flags, new_passwd, 
320                                      err_str, sizeof(err_str), msg_str, sizeof(msg_str));
321
322         if(*msg_str)
323                 printf(msg_str);
324         if(*err_str)
325                 fprintf(stderr, err_str);
326
327         return ret;
328 }
329
330 #ifdef WITH_LDAP_SAM
331 /*******************************************************************
332  Store the LDAP admin password in secrets.tdb
333  ******************************************************************/
334 static BOOL store_ldap_admin_pw (char* pw)
335 {       
336         if (!pw) 
337                 return False;
338
339         if (!secrets_init())
340                 return False;
341         
342         return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
343 }
344 #endif
345
346
347 /*************************************************************
348  Handle password changing for root.
349 *************************************************************/
350
351 static int process_root(void)
352 {
353         struct passwd  *pwd;
354         int result = 0;
355
356 #ifdef WITH_LDAP_SAM
357         if (local_flags & LOCAL_SET_LDAP_ADMIN_PW)
358         {
359                 printf("Setting stored password for \"%s\" in secrets.tdb\n", 
360                         lp_ldap_admin_dn());
361                 if (!store_ldap_admin_pw(ldap_secret))
362                         DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
363                 goto done;
364         }
365 #endif
366
367         /*
368          * Ensure both add/delete user are not set
369          * Ensure add/delete user and either remote machine or join domain are
370          * not both set.
371          */     
372         if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) || 
373            ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) && 
374                 (remote_machine != NULL))) {
375                 usage();
376         }
377         
378         /* Only load interfaces if we are doing network operations. */
379
380         if (remote_machine) {
381                 load_interfaces();
382         }
383
384         if (!user_name[0] && (pwd = sys_getpwuid(geteuid()))) {
385                 fstrcpy(user_name, pwd->pw_name);
386         } 
387
388         if (!user_name[0]) {
389                 fprintf(stderr,"You must specify a username\n");
390                 exit(1);
391         }
392
393         if (local_flags & LOCAL_TRUST_ACCOUNT) {
394                 /* add the $ automatically */
395                 static fstring buf;
396
397                 /*
398                  * Remove any trailing '$' before we
399                  * generate the initial machine password.
400                  */
401
402                 if (user_name[strlen(user_name)-1] == '$') {
403                         user_name[strlen(user_name)-1] = 0;
404                 }
405
406                 if (local_flags & LOCAL_ADD_USER) {
407                         SAFE_FREE(new_passwd);
408                         new_passwd = smb_xstrdup(user_name);
409                         strlower(new_passwd);
410                 }
411
412                 /*
413                  * Now ensure the username ends in '$' for
414                  * the machine add.
415                  */
416
417                 slprintf(buf, sizeof(buf)-1, "%s$", user_name);
418                 fstrcpy(user_name, buf);
419         } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
420                 static fstring buf;
421
422                 if (local_flags & LOCAL_ADD_USER) {
423                         /*
424                          * Prompt for trusting domain's account password
425                          */
426                         new_passwd = prompt_for_new_password(stdin_passwd_get);
427                         if(!new_passwd) {
428                                 fprintf(stderr, "Unable to get newpassword.\n");
429                                 exit(1);
430                         }
431                 }
432                 slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
433                 fstrcpy(user_name, buf);
434
435         } else {
436                 
437                 if (remote_machine != NULL) {
438                         old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
439                 }
440                 
441                 if (!(local_flags & LOCAL_SET_PASSWORD)) {
442                         
443                         /*
444                          * If we are trying to enable a user, first we need to find out
445                          * if they are using a modern version of the smbpasswd file that
446                          * disables a user by just writing a flag into the file. If so
447                          * then we can re-enable a user without prompting for a new
448                          * password. If not (ie. they have a no stored password in the
449                          * smbpasswd file) then we need to prompt for a new password.
450                          */
451                         
452                         if(local_flags & LOCAL_ENABLE_USER) {
453                                 SAM_ACCOUNT *sampass = NULL;
454                                 BOOL ret;
455                                 
456                                 pdb_init_sam(&sampass);
457                                 ret = pdb_getsampwnam(sampass, user_name);
458                                 if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) {
459                                         local_flags |= LOCAL_SET_PASSWORD;
460                                 }
461                                 pdb_free_sam(&sampass);
462                         }
463                 }
464                 
465                 if(local_flags & LOCAL_SET_PASSWORD) {
466                         new_passwd = prompt_for_new_password(stdin_passwd_get);
467                         
468                         if(!new_passwd) {
469                                 fprintf(stderr, "Unable to get new password.\n");
470                                 exit(1);
471                         }
472                 }
473         }
474
475         if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) {
476                 fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
477                 result = 1;
478                 goto done;
479         } 
480
481         if(remote_machine) {
482                 printf("Password changed for user %s on %s.\n", user_name, remote_machine );
483         } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
484                 SAM_ACCOUNT *sampass = NULL;
485                 BOOL ret;
486                 
487                 pdb_init_sam(&sampass);
488                 ret = pdb_getsampwnam(sampass, user_name);
489
490                 printf("Password changed for user %s.", user_name );
491                 if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) )
492                         printf(" User has disabled flag set.");
493                 if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) )
494                         printf(" User has no password flag set.");
495                 printf("\n");
496                 pdb_free_sam(&sampass);
497         }
498
499  done:
500         SAFE_FREE(new_passwd);
501         return result;
502 }
503
504
505 /*************************************************************
506  Handle password changing for non-root.
507 *************************************************************/
508
509 static int process_nonroot(void)
510 {
511         struct passwd  *pwd = NULL;
512         int result = 0;
513
514         if (!user_name[0]) {
515                 pwd = sys_getpwuid(getuid());
516                 if (pwd) {
517                         fstrcpy(user_name,pwd->pw_name);
518                 } else {
519                         fprintf(stderr, "smbpasswd: you don't exist - go away\n");
520                         exit(1);
521                 }
522         }
523         
524         /*
525          * A non-root user is always setting a password
526          * via a remote machine (even if that machine is
527          * localhost).
528          */     
529
530         load_interfaces(); /* Delayed from main() */
531
532         if (remote_machine == NULL) {
533                 remote_machine = "127.0.0.1";
534         }
535
536         if (remote_machine != NULL) {
537                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
538         }
539         
540         if (!new_passwd) {
541                 new_passwd = prompt_for_new_password(stdin_passwd_get);
542         }
543         
544         if (!new_passwd) {
545                 fprintf(stderr, "Unable to get new password.\n");
546                 exit(1);
547         }
548
549         if (!password_change(remote_machine, user_name, old_passwd, new_passwd, 0)) {
550                 fprintf(stderr,"Failed to change password for %s\n", user_name);
551                 result = 1;
552                 goto done;
553         }
554
555         printf("Password changed for user %s\n", user_name);
556
557  done:
558         SAFE_FREE(old_passwd);
559         SAFE_FREE(new_passwd);
560
561         return result;
562 }
563
564
565
566 /*********************************************************
567  Start here.
568 **********************************************************/
569 int main(int argc, char **argv)
570 {       
571         BOOL amroot = getuid() == 0;
572
573         pstrcpy(configfile, dyn_CONFIGFILE);
574         AllowDebugChange = False;
575
576 #if defined(HAVE_SET_AUTH_PARAMETERS)
577         set_auth_parameters(argc, argv);
578 #endif /* HAVE_SET_AUTH_PARAMETERS */
579
580         process_options(argc, argv, amroot);
581
582         setup_logging("smbpasswd", True);
583         
584         if (!lp_load(configfile,True,False,False)) {
585                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
586                         dyn_CONFIGFILE);
587                 exit(1);
588         }
589
590         /*
591          * Set the machine NETBIOS name if not already
592          * set from the config file. 
593          */ 
594     
595         if (!*global_myname) {   
596                 char *p;
597                 fstrcpy(global_myname, myhostname());
598                 p = strchr_m(global_myname, '.' );
599                 if (p) *p = 0;
600         }           
601         strupper(global_myname);
602
603         /* Check the effective uid - make sure we are not setuid */
604         if (is_setuid_root()) {
605                 fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
606                 exit(1);
607         }
608
609         if (local_mode || amroot) {
610                 secrets_init();
611                 return process_root();
612         } 
613
614         return process_nonroot();
615 }