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