Fixed smbpasswd so that enabling a user who already has a password
[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                   disable 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
375                 /*
376                  * If we are trying to enable a user, first we need to find out
377                  * if they are using a modern version of the smbpasswd file that
378                  * disables a user by just writing a flag into the file. If so
379                  * then we can re-enable a user without prompting for a new
380                  * password. If not (ie. they have a no stored password in the
381                  * smbpasswd file) then we need to prompt for a new password.
382                  */
383
384                 if(enable_user) {
385                         struct smb_passwd *smb_pass = getsmbpwnam(user_name);
386                         if((smb_pass != NULL) && (smb_pass->smb_passwd != NULL)) {
387                                 new_passwd = "XXXX"; /* Don't care. */
388                         }
389                 }
390
391                 if(!new_passwd)
392                         new_passwd = prompt_for_new_password(stdin_passwd_get);
393         }
394         
395         if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
396                              add_user, enable_user, disable_user, set_no_password,
397                              trust_account)) {
398                 fprintf(stderr,"Failed to change password entry for %s\n", user_name);
399                 return 1;
400         } 
401
402         if(disable_user) {
403                 printf("User %s disabled.\n", user_name);
404         } else if(enable_user) {
405                 printf("User %s enabled.\n", user_name);
406         } else if (set_no_password) {
407                 printf("User %s - set to no password.\n", user_name);
408         } else {
409                 printf("Password changed for user %s\n", user_name);
410         }
411         return 0;
412 }
413
414
415 /*************************************************************
416 handle password changing for non-root
417 *************************************************************/
418 static int process_nonroot(int argc, char *argv[])
419 {
420         struct passwd  *pwd = NULL;
421         int ch;
422         BOOL stdin_passwd_get = False;
423         char *old_passwd = NULL;
424         char *remote_machine = NULL;
425         char *user_name = NULL;
426         char *new_passwd = NULL;
427
428         while ((ch = getopt(argc, argv, "hD:r:sU:")) != EOF) {
429                 switch(ch) {
430                 case 'D':
431                         DEBUGLEVEL = atoi(optarg);
432                         break;
433                 case 'r':
434                         remote_machine = optarg;
435                         break;
436                 case 's':
437                         set_line_buffering(stdin);
438                         set_line_buffering(stdout);
439                         set_line_buffering(stderr);
440                         stdin_passwd_get = True;
441                         break;
442                 case 'U':
443                         user_name = optarg;
444                         break;
445                 default:
446                         usage();
447                 }
448         }
449         
450         argc -= optind;
451         argv += optind;
452
453         if(argc > 1) {
454                 usage();
455         }
456         
457         if (argc == 1) {
458                 new_passwd = argv[0];
459         }
460         
461         if (!user_name) {
462                 pwd = getpwuid(getuid());
463                 if (pwd) {
464                         user_name = xstrdup(pwd->pw_name);
465                 } else {
466                         fprintf(stderr,"you don't exist - go away\n");
467                         exit(1);
468                 }
469         }
470         
471         /*
472          * A non-root user is always setting a password
473          * via a remote machine (even if that machine is
474          * localhost).
475          */     
476         if (remote_machine == NULL) {
477                 remote_machine = "127.0.0.1";
478         }
479
480
481         if (remote_machine != NULL) {
482                 old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
483         }
484         
485         if (!new_passwd) {
486                 new_passwd = prompt_for_new_password(stdin_passwd_get);
487         }
488         
489         if (!new_passwd) {
490                 printf("unable to get new password\n");
491                 exit(0);
492         }
493
494         if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
495                              False, False, False, False, False)) {
496                 fprintf(stderr,"Failed to change password for %s\n", user_name);
497                 return 1;
498         }
499
500         printf("Password changed for user %s\n", user_name);
501         return 0;
502 }
503
504
505
506 /*********************************************************
507  Start here.
508 **********************************************************/
509 int main(int argc, char **argv)
510 {       
511         static pstring servicesf = CONFIGFILE;
512
513         TimeInit();
514         
515         setup_logging("smbpasswd", True);
516         
517         charset_initialise();
518         
519         if(!initialize_password_db()) {
520                 fprintf(stderr, "Can't setup password database vectors.\n");
521                 exit(1);
522         }
523
524         if (!lp_load(servicesf,True,False,False)) {
525                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
526                         servicesf);
527                 exit(1);
528         }
529
530         if(!get_myname(myhostname,NULL)) {
531                 fprintf(stderr, "unable to get my hostname.\n");
532                 exit(1);
533         }
534
535         /*
536          * Set the machine NETBIOS name if not already
537          * set from the config file. 
538          */ 
539     
540         if (!*global_myname) {   
541                 char *p;
542                 fstrcpy(global_myname, myhostname);
543                 p = strchr(global_myname, '.' );
544                 if (p) *p = 0;
545         }           
546         strupper(global_myname);
547
548         codepage_initialise(lp_client_code_page());
549
550         /* Check the effective uid - make sure we are not setuid */
551         if ((geteuid() == (uid_t)0) && (getuid() != (uid_t)0)) {
552                 fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
553                 exit(1);
554         }
555
556         if (getuid() == 0) {
557                 return process_root(argc, argv);
558         } 
559
560         return process_nonroot(argc, argv);
561 }