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