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