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