2303bc56df168fdbf9e6c14549ea0ca0a7830f16
[kai/samba.git] / source / 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 myhostname;
23 extern pstring global_myname;
24 extern int DEBUGLEVEL;
25
26
27 /*********************************************************
28 a strdup with exit
29 **********************************************************/
30 static char *xstrdup(char *s)
31 {
32         s = strdup(s);
33         if (!s) {
34                 fprintf(stderr,"out of memory\n");
35                 exit(1);
36         }
37         return s;
38 }
39
40
41 /*********************************************************
42  Print command usage on stderr and die.
43 **********************************************************/
44 static void usage(void)
45 {
46         if (getuid() == 0) {
47                 printf("smbpasswd [options] [username] [password]\n");
48         } else {
49                 printf("smbpasswd [options] [password]\n");
50         }
51         printf("options:\n");
52         printf("  -s                   use stdin for password prompt\n");
53         printf("  -D LEVEL             debug level\n");
54         printf("  -U USER              remote username\n");
55         printf("  -r MACHINE           remote machine\n");
56
57         if (getuid() == 0) {
58                 printf("  -R ORDER             name resolve order\n");
59                 printf("  -j DOMAIN            join domain name\n");
60                 printf("  -a                   add user\n");
61                 printf("  -d                   delete user\n");
62                 printf("  -e                   enable user\n");
63                 printf("  -n                   set no password\n");
64                 printf("  -m                   machine trust account\n");
65         }
66         exit(1);
67 }
68
69 /*********************************************************
70 Join a domain.
71 **********************************************************/
72 static int join_domain(char *domain, char *remote)
73 {
74         pstring remote_machine;
75         fstring trust_passwd;
76         unsigned char orig_trust_passwd_hash[16];
77         BOOL ret;
78
79         pstrcpy(remote_machine, remote ? remote : "");
80         fstrcpy(trust_passwd, global_myname);
81         strlower(trust_passwd);
82         E_md4hash( (uchar *)trust_passwd, orig_trust_passwd_hash);
83
84         /* Ensure that we are not trying to join a
85            domain if we are locally set up as a domain
86            controller. */
87
88         if(strequal(remote, global_myname)) {
89                 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);
90                 return 1;
91         }
92
93         /*
94          * Create the machine account password file.
95          */
96         if(!trust_password_lock( domain, global_myname, True)) {
97                 fprintf(stderr, "unable to open the machine account password file for \
98 machine %s in domain %s.\n", global_myname, domain); 
99                 return 1;
100         }
101
102         /*
103          * Write the old machine account password.
104          */
105         
106         if(!set_trust_account_password( orig_trust_passwd_hash)) {              
107                 fprintf(stderr, "unable to write the machine account password for \
108 machine %s in domain %s.\n", global_myname, domain);
109                 trust_password_unlock();
110                 return 1;
111         }
112         
113         /*
114          * If we are given a remote machine assume this is the PDC.
115          */
116         
117         if(remote == NULL) {
118                 pstrcpy(remote_machine, lp_passwordserver());
119         }
120
121         if(!*remote_machine) {
122                 fprintf(stderr, "No password server list given in smb.conf - \
123 unable to join domain.\n");
124                 trust_password_unlock();
125                 return 1;
126         }
127
128         ret = change_trust_account_password( domain, remote_machine);
129         trust_password_unlock();
130         
131         if(!ret) {
132                 trust_password_delete( domain, global_myname);
133                 fprintf(stderr,"Unable to join domain %s.\n",domain);
134         } else {
135                 printf("Joined domain %s.\n",domain);
136         }
137         
138         return (int)ret;
139 }
140
141
142 static void set_line_buffering(FILE *f)
143 {
144         setvbuf(f, NULL, _IOLBF, 0);
145 }
146
147 /*************************************************************
148  Utility function to prompt for passwords from stdin. Each
149  password entered must end with a newline.
150 *************************************************************/
151 static char *stdin_new_passwd(void)
152 {
153         static fstring new_passwd;
154         size_t len;
155
156         ZERO_STRUCT(new_passwd);
157
158         /*
159          * if no error is reported from fgets() and string at least contains
160          * the newline that ends the password, then replace the newline with
161          * a null terminator.
162          */
163         if ( fgets(new_passwd, sizeof(new_passwd), stdin) != NULL) {
164                 if ((len = strlen(new_passwd)) > 0) {
165                         if(new_passwd[len-1] == '\n')
166                                 new_passwd[len - 1] = 0; 
167                 }
168         }
169         return(new_passwd);
170 }
171
172
173 /*************************************************************
174  Utility function to get passwords via tty or stdin
175  Used if the '-s' option is set to silently get passwords
176  to enable scripting.
177 *************************************************************/
178 static char *get_pass( char *prompt, BOOL stdin_get)
179 {
180         char *p;
181         if (stdin_get) {
182                 p = stdin_new_passwd();
183         } else {
184                 p = getpass(prompt);
185         }
186         return xstrdup(p);
187 }
188
189 /*************************************************************
190  Utility function to prompt for new password.
191 *************************************************************/
192 static char *prompt_for_new_password(BOOL stdin_get)
193 {
194         char *p;
195         fstring new_passwd;
196
197         ZERO_STRUCT(new_passwd);
198  
199         p = get_pass("New SMB password:", stdin_get);
200
201         fstrcpy(new_passwd, p);
202
203         p = get_pass("Retype new SMB password:", stdin_get);
204
205         if (strcmp(p, new_passwd)) {
206                 fprintf(stderr, "Mismatch - password unchanged.\n");
207                 return NULL;
208         }
209
210         return xstrdup(p);
211 }
212
213
214 /*************************************************************
215 change a password either locally or remotely
216 *************************************************************/
217 static BOOL password_change(const char *remote_machine, char *user_name, 
218                             char *old_passwd, char *new_passwd, 
219                             BOOL add_user, BOOL enable_user, 
220                             BOOL disable_user, BOOL set_no_password,
221                             BOOL trust_account)
222 {
223         if (remote_machine != NULL) {
224                 if (add_user || enable_user || disable_user || set_no_password || trust_account) {
225                         /* these things can't be done remotely yet */
226                         return False;
227                 }
228                 return remote_password_change(remote_machine, user_name, old_passwd, new_passwd);
229         }
230         
231         return local_password_change(user_name, trust_account, add_user, enable_user, 
232                                      disable_user, set_no_password, new_passwd);
233 }
234
235
236 /*************************************************************
237 handle password changing for root
238 *************************************************************/
239 static int process_root(int argc, char *argv[])
240 {
241         struct passwd  *pwd;
242         int ch;
243         BOOL joining_domain = False;
244         BOOL trust_account = False;
245         BOOL add_user = False;
246         BOOL disable_user = False;
247         BOOL enable_user = False;
248         BOOL set_no_password = False;
249         BOOL stdin_passwd_get = False;
250         char *user_name = NULL;
251         char *new_domain = NULL;
252         char *new_passwd = NULL;
253         char *old_passwd = NULL;
254         char *remote_machine = NULL;
255
256         while ((ch = getopt(argc, argv, "adehmnj:r:sR:D:U:")) != EOF) {
257                 switch(ch) {
258                 case 'a':
259                         add_user = True;
260                         break;
261                 case 'd':
262                         disable_user = True;
263                         new_passwd = "XXXXXX";
264                         break;
265                 case 'e':
266                         enable_user = True;
267                         break;
268                 case 'D':
269                         DEBUGLEVEL = atoi(optarg);
270                         break;
271                 case 'n':
272                         set_no_password = True;
273                         new_passwd = "NO PASSWORD";
274                 case 'r':
275                         remote_machine = optarg;
276                         break;
277                 case 's':
278                         set_line_buffering(stdin);
279                         set_line_buffering(stdout);
280                         set_line_buffering(stderr);
281                         stdin_passwd_get = True;
282                         break;
283                 case 'R':
284                         lp_set_name_resolve_order(optarg);
285                         break;
286                 case 'm':
287                         trust_account = True;
288                         break;
289                 case 'j':
290                         new_domain = optarg;
291                         strupper(new_domain);
292                         joining_domain = True;
293                         break;
294                 case 'U':
295                         user_name = optarg;
296                         break;
297                 default:
298                         usage();
299                 }
300         }
301         
302         argc -= optind;
303         argv += optind;
304
305
306         /*
307          * Ensure add_user and either remote machine or join domain are
308          * not both set.
309          */     
310         if(add_user && ((remote_machine != NULL) || joining_domain)) {
311                 usage();
312         }
313         
314         if(joining_domain) {
315                 if (argc != 0) usage();
316                 return join_domain(new_domain, remote_machine);
317         }
318
319         /*
320          * Deal with root - can add a user, but only locally.
321          */
322
323         switch(argc) {
324         case 0:
325                 break;
326         case 1:
327                 user_name = argv[0];
328                 break;
329         case 2:
330                 user_name = argv[0];
331                 new_passwd = argv[1];
332                 break;
333         default:
334                 usage();
335         }
336
337         if (!user_name && (pwd = getpwuid(0))) {
338                 user_name = xstrdup(pwd->pw_name);
339         } 
340
341         if (!user_name) {
342                 fprintf(stderr,"You must specify a username\n");
343                 exit(1);
344         }
345
346         if (!remote_machine && !Get_Pwnam(user_name, True)) {
347                 fprintf(stderr, "User \"%s\" was not found in system password file.\n", 
348                         user_name);
349                 exit(1);
350         }
351
352         if (user_name[strlen(user_name)-1] == '$') {
353                 user_name[strlen(user_name)-1] = 0;
354         }
355
356         if (trust_account) {
357                 /* add the $ automatically */
358                 static fstring buf;
359
360                 if (add_user) {
361                         new_passwd = xstrdup(user_name);
362                         strlower(new_passwd);
363                 }
364
365                 slprintf(buf, sizeof(buf)-1, "%s$", user_name);
366                 user_name = buf;
367         }
368
369         if (remote_machine != NULL) {
370                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
371         }
372         
373         if (!new_passwd) {
374                 new_passwd = prompt_for_new_password(stdin_passwd_get);
375         }
376         
377         if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
378                              add_user, enable_user, disable_user, set_no_password,
379                              trust_account)) {
380                 fprintf(stderr,"Failed to change password entry for %s\n", user_name);
381                 return 1;
382         } 
383
384         if(disable_user) {
385                 printf("User %s disabled.\n", user_name);
386         } else if(enable_user) {
387                 printf("User %s enabled.\n", user_name);
388         } else if (set_no_password) {
389                 printf("User %s - set to no password.\n", user_name);
390         } else {
391                 printf("Password changed for user %s\n", user_name);
392         }
393         return 0;
394 }
395
396
397 /*************************************************************
398 handle password changing for non-root
399 *************************************************************/
400 static int process_nonroot(int argc, char *argv[])
401 {
402         struct passwd  *pwd = NULL;
403         int ch;
404         BOOL stdin_passwd_get = False;
405         char *old_passwd = NULL;
406         char *remote_machine = NULL;
407         char *user_name = NULL;
408         char *new_passwd = NULL;
409
410         while ((ch = getopt(argc, argv, "hD:r:sU:")) != EOF) {
411                 switch(ch) {
412                 case 'D':
413                         DEBUGLEVEL = atoi(optarg);
414                         break;
415                 case 'r':
416                         remote_machine = optarg;
417                         break;
418                 case 's':
419                         set_line_buffering(stdin);
420                         set_line_buffering(stdout);
421                         set_line_buffering(stderr);
422                         stdin_passwd_get = True;
423                         break;
424                 case 'U':
425                         user_name = optarg;
426                         break;
427                 default:
428                         usage();
429                 }
430         }
431         
432         argc -= optind;
433         argv += optind;
434
435         if(argc > 1) {
436                 usage();
437         }
438         
439         if (argc == 1) {
440                 new_passwd = argv[0];
441         }
442         
443         if (!user_name) {
444                 pwd = getpwuid(getuid());
445                 if (pwd) {
446                         user_name = xstrdup(pwd->pw_name);
447                 } else {
448                         fprintf(stderr,"you don't exist - go away\n");
449                         exit(1);
450                 }
451         }
452         
453         /*
454          * A non-root user is always setting a password
455          * via a remote machine (even if that machine is
456          * localhost).
457          */     
458         if (remote_machine == NULL) {
459                 remote_machine = "127.0.0.1";
460         }
461
462
463         if (remote_machine != NULL) {
464                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
465         }
466         
467         if (!new_passwd) {
468                 new_passwd = prompt_for_new_password(stdin_passwd_get);
469         }
470         
471         if (!new_passwd) {
472                 printf("unable to get new password\n");
473                 exit(0);
474         }
475
476         if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
477                              False, False, False, False, False)) {
478                 fprintf(stderr,"Failed to change password for %s\n", user_name);
479                 return 1;
480         }
481
482         printf("Password changed for user %s\n", user_name);
483         return 0;
484 }
485
486
487
488 /*********************************************************
489  Start here.
490 **********************************************************/
491 int main(int argc, char **argv)
492 {       
493         static pstring servicesf = CONFIGFILE;
494
495         TimeInit();
496         
497         setup_logging("smbpasswd", True);
498         
499         charset_initialise();
500         
501         if(!initialize_password_db()) {
502                 fprintf(stderr, "Can't setup password database vectors.\n");
503                 exit(1);
504         }
505
506         if (!lp_load(servicesf,True,False,False)) {
507                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
508                         servicesf);
509                 exit(1);
510         }
511
512         if(!get_myname(myhostname,NULL)) {
513                 fprintf(stderr, "unable to get my hostname.\n");
514                 exit(1);
515         }
516
517         /*
518          * Set the machine NETBIOS name if not already
519          * set from the config file. 
520          */ 
521     
522         if (!*global_myname) {   
523                 char *p;
524                 fstrcpy(global_myname, myhostname);
525                 p = strchr(global_myname, '.' );
526                 if (p) *p = 0;
527         }           
528         strupper(global_myname);
529
530         codepage_initialise(lp_client_code_page());
531
532         /* Check the effective uid - make sure we are not setuid */
533         if ((geteuid() == (uid_t)0) && (getuid() != (uid_t)0)) {
534                 fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
535                 exit(1);
536         }
537
538         if (getuid() == 0) {
539                 return process_root(argc, argv);
540         } 
541
542         return process_nonroot(argc, argv);
543 }