converted smbclient to use clientgen.c rather than clientutil.c
[kai/samba.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 scope;
23 extern pstring myhostname;
24 extern pstring global_myname;
25 extern fstring global_myworkgroup;
26
27 static char *prog_name;
28
29 /*********************************************************
30  Print command usage on stderr and die.
31 **********************************************************/
32
33 static void usage(char *name, BOOL is_root)
34 {
35         if(is_root)
36                 fprintf(stderr, "Usage is : %s [-D DEBUGLEVEL] [-h] [-a] [-d] [-e] [-m] [-n] [-s] [username] [password]\n\
37 %s: [-R <name resolve order>] [-D DEBUGLEVEL] [-j DOMAINNAME] [-r machine] [-U remote_username] [username] [password]\n%s: [-h]\n", name, name, name);
38         else
39                 fprintf(stderr, "Usage is : %s [-h] [-s] [-D DEBUGLEVEL] [-r machine] [-U remote_username] [password]\n", name);
40         exit(1);
41 }
42
43 /*********************************************************
44 Join a domain.
45 **********************************************************/
46
47 static int join_domain( char *domain, char *remote)
48 {
49   pstring remote_machine;
50   fstring trust_passwd;
51   unsigned char orig_trust_passwd_hash[16];
52   BOOL ret;
53
54   pstrcpy(remote_machine, remote ? remote : "");
55   fstrcpy(trust_passwd, global_myname);
56   strlower(trust_passwd);
57   E_md4hash( (uchar *)trust_passwd, orig_trust_passwd_hash);
58
59   /* Ensure that we are not trying to join a
60      domain if we are locally set up as a domain
61      controller. */
62
63   if(strequal(remote, global_myname)) {
64     fprintf(stderr, "%s: Cannot join domain %s as the domain controller name is our own. \
65 We cannot be a domain controller for a domain and also be a domain member.\n", prog_name, domain);
66     return 1;
67   }
68
69   /*
70    * Create the machine account password file.
71    */
72   if(!trust_password_lock( domain, global_myname, True)) {
73     fprintf(stderr, "%s: unable to open the machine account password file for \
74 machine %s in domain %s.\n", prog_name, global_myname, domain); 
75     return 1;
76   }
77
78   /*
79    * Write the old machine account password.
80    */
81
82   if(!set_trust_account_password( orig_trust_passwd_hash)) {              
83     fprintf(stderr, "%s: unable to write the machine account password for \
84 machine %s in domain %s.\n", prog_name, global_myname, domain);
85     trust_password_unlock();
86     return 1;
87   }
88
89   /*
90    * If we are given a remote machine assume this is the PDC.
91    */
92
93   if(remote == NULL)
94     pstrcpy(remote_machine, lp_passwordserver());
95
96   if(!*remote_machine) {
97     fprintf(stderr, "%s: No password server list given in smb.conf - \
98 unable to join domain.\n", prog_name);
99     trust_password_unlock();
100     return 1;
101   }
102
103   ret = change_trust_account_password( domain, remote_machine);
104   trust_password_unlock();
105
106   if(!ret) {
107     trust_password_delete( domain, global_myname);
108     fprintf(stderr,"%s: Unable to join domain %s.\n", prog_name, domain);
109   } else {
110     printf("%s: Joined domain %s.\n", prog_name, domain);
111   }
112
113   return (int)ret;
114 }
115
116 /*************************************************************
117  Utility function to prompt for passwords from stdin. Each
118  password entered must end with a newline.
119 *************************************************************/
120
121 static char *stdin_new_passwd(void)
122 {
123   static fstring new_passwd;
124   size_t len;
125
126   memset( new_passwd, '\0', sizeof(new_passwd));
127
128   /*
129    * if no error is reported from fgets() and string at least contains
130    * the newline that ends the password, then replace the newline with
131    * a null terminator.
132    */
133   if ( fgets(new_passwd, sizeof(new_passwd), stdin) != NULL) {
134     if ((len = strlen(new_passwd)) > 0) {
135       if(new_passwd[len-1] == '\n')
136         new_passwd[len - 1] = 0; 
137     }
138   }
139   return(new_passwd);
140 }
141
142 /*************************************************************
143  Utility function to get passwords via tty or stdin
144  Used if the '-s' option is set to silently get passwords
145  to enable scripting.
146 *************************************************************/
147
148 static char *get_pass( char *prompt, BOOL stdin_get)
149 {
150   if (stdin_get)
151     return( stdin_new_passwd());
152   else
153     return( getpass( prompt));
154 }
155
156 /*************************************************************
157  Utility function to prompt for new password.
158 *************************************************************/
159
160 static void prompt_for_new_password(char *new_passwd, BOOL stdin_get)
161 {
162   char *p;
163
164   new_passwd[0] = '\0';
165  
166   p = get_pass("New SMB password:", stdin_get);
167
168   strncpy(new_passwd, p, sizeof(fstring));
169   new_passwd[sizeof(fstring)-1] = '\0';
170
171   p = get_pass("Retype new SMB password:", stdin_get);
172
173   if (strncmp(p, new_passwd, sizeof(fstring)-1))
174   {
175     fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
176     exit(1);
177   }
178
179   if (new_passwd[0] == '\0') {
180     printf("Password not set\n");
181     exit(0);
182   }
183 }
184
185 /*********************************************************
186  Start here.
187 **********************************************************/
188
189 int main(int argc, char **argv)
190 {
191   extern char *optarg;
192   extern int optind;
193   extern int DEBUGLEVEL;
194   uid_t             real_uid;
195   uid_t             eff_uid;
196   struct passwd  *pwd = NULL;
197   fstring         old_passwd;
198   fstring         new_passwd;
199   uchar           new_p16[16];
200   uchar           new_nt_p16[16];
201   char           *p;
202   struct smb_passwd *smb_pwent;
203   FILE           *fp;
204   int             ch;
205   int             err;
206   BOOL is_root = False;
207   pstring  user_name;
208   BOOL remote_user_name = False;
209   char *remote_machine = NULL;
210   BOOL add_user = False;
211   BOOL got_new_pass = False;
212   BOOL trust_account = False;
213   BOOL disable_user = False;
214   BOOL enable_user = False;
215   BOOL set_no_password = False;
216   BOOL joining_domain = False;
217   BOOL stdin_passwd_get = False;
218   char *new_domain = NULL;
219   pstring servicesf = CONFIGFILE;
220   void           *vp;
221   struct nmb_name calling, called;
222   
223
224   new_passwd[0] = '\0';
225   user_name[0] = '\0';
226
227   memset(old_passwd, '\0', sizeof(old_passwd));
228   memset(new_passwd, '\0', sizeof(new_passwd));
229
230   prog_name = argv[0];
231
232   TimeInit();
233
234   setup_logging(prog_name,True);
235   
236   charset_initialise();
237
238   if(!initialize_password_db()) {
239     fprintf(stderr, "%s: Can't setup password database vectors.\n", prog_name);
240     exit(1);
241   }
242
243   if (!lp_load(servicesf,True,False,False)) {
244     fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
245     exit(1);
246   }
247
248   if(!get_myname(myhostname,NULL)) {
249     fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
250     exit(1);
251   }
252
253   /*
254    * Set the machine NETBIOS name if not already
255    * set from the config file. 
256    */ 
257     
258   if (!*global_myname)
259   {   
260     fstrcpy( global_myname, myhostname );
261     p = strchr( global_myname, '.' );
262     if (p) 
263       *p = 0;
264   }           
265   strupper( global_myname );
266
267   codepage_initialise(lp_client_code_page());
268
269   /* Get the real and effective uids */
270   real_uid = getuid();
271   eff_uid = geteuid();
272  
273   /* Check the effective uid */
274   if ((eff_uid == (uid_t)0) && (real_uid != (uid_t)0)) {
275     fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
276     exit(1);
277   }
278
279   is_root = (eff_uid == (uid_t)0);
280
281   while ((ch = getopt(argc, argv, "adehmnj:r:sR:D:U:")) != EOF) {
282     switch(ch) {
283     case 'a':
284       if(is_root)
285         add_user = True;
286       else
287         usage(prog_name, is_root);
288       break;
289     case 'd':
290       if(is_root) {
291         disable_user = True;
292         got_new_pass = True;
293         fstrcpy(new_passwd, "XXXXXX");
294       } else
295         usage(prog_name, is_root);
296       break;
297     case 'e':
298       if(is_root) {
299         enable_user = True;
300         got_new_pass = True;
301         fstrcpy(new_passwd, "XXXXXX");
302       } else
303         usage(prog_name, is_root);
304       break;
305     case 'D':
306       DEBUGLEVEL = atoi(optarg);
307       break;
308     case 'n':
309       if(is_root) {
310         set_no_password = True;
311         got_new_pass = True;
312         fstrcpy(new_passwd, "NO PASSWORD");
313       } else
314         usage(prog_name, is_root);
315     case 'r':
316       remote_machine = optarg;
317       break;
318     case 's':
319       /*
320        * Ensure stdin/stdout are line buffered.
321        */
322       if (setvbuf( stdin, NULL, _IOLBF, 0) != 0) {
323         fprintf(stderr, "%s: setvbuf error on stdin. Error was %s\n", prog_name, strerror(errno));
324         exit(1);
325       }
326
327       if (setvbuf( stdout, NULL, _IOLBF, 0) != 0) {
328         fprintf(stderr, "%s: setvbuf error on stdout. Error was %s\n", prog_name, strerror(errno));
329         exit(1);
330       }
331
332       if (setvbuf( stderr, NULL, _IOLBF, 0) != 0) {
333         fprintf(stderr, "%s: setvbuf error on stderr. Error was %s\n", prog_name, strerror(errno));
334         perror("setvbuf error on stdout");
335         exit(1);
336       }
337
338       stdin_passwd_get = True;
339       break;
340     case 'R':
341       if(is_root) {
342         lp_set_name_resolve_order(optarg);
343         break;
344       } else
345         usage(prog_name, is_root);
346     case 'm':
347       if(is_root) {
348         trust_account = True;
349       } else
350         usage(prog_name, is_root);
351       break;
352     case 'j':
353       if(is_root) {
354         new_domain = optarg;
355         strupper(new_domain);
356         joining_domain = True;
357       } else
358         usage(prog_name, is_root);  
359       break;
360     case 'U':
361       remote_user_name = True;
362       pstrcpy(user_name, optarg);
363       break;
364     case 'h':
365     default:
366       usage(prog_name, is_root);
367     }
368   }
369
370   argc -= optind;
371   argv += optind;
372
373   if (!is_root && remote_user_name && !remote_machine) {
374     fprintf(stderr, "%s: You can only use -U with -r.\n", prog_name);
375     usage(prog_name, False);
376   }
377
378   /*
379    * Ensure add_user and either remote machine or join domain are
380    * not both set.
381    */
382
383   if(add_user && ((remote_machine != NULL) || joining_domain))
384     usage(prog_name, True);
385
386   /*
387    * Deal with joining a domain.
388    */
389   if(joining_domain && argc != 0)
390     usage(prog_name, True);
391
392   if(joining_domain) {
393     return join_domain( new_domain, remote_machine);
394   }
395
396   if(is_root) {
397
398     /*
399      * Deal with root - can add a user, but only locally.
400      */
401
402     switch(argc) {
403       case 0:
404         break;
405       case 1:
406         /* If we are root we can change another's password. */
407         pstrcpy(user_name, argv[0]);
408         break;
409       case 2:
410         pstrcpy(user_name, argv[0]);
411         fstrcpy(new_passwd, argv[1]);
412         got_new_pass = True;
413         break;
414       default:
415         usage(prog_name, True);
416     }
417
418     if(*user_name) {
419
420       if(trust_account) {
421         int username_len = strlen(user_name);
422         if(username_len >= sizeof(pstring) - 1) {
423           fprintf(stderr, "%s: machine account name too long.\n", user_name);
424           exit(1);
425         }
426
427         if(user_name[username_len-1] != '$') {
428           user_name[username_len] = '$';
429           user_name[username_len+1] = '\0';
430         }
431       }
432
433       if(!remote_machine && ((pwd = Get_Pwnam(user_name, True)) == NULL)) {
434         fprintf(stderr, "%s: User \"%s\" was not found in system password file.\n", 
435                     prog_name, user_name);
436         exit(1);
437       }
438     } else {
439       if((pwd = getpwuid(eff_uid)) != NULL)
440         pstrcpy( user_name, pwd->pw_name);
441     }
442
443   } else {
444
445     if(add_user) {
446       fprintf(stderr, "%s: Only root can add a user.\n", prog_name);
447       usage(prog_name, False);
448     }
449
450     if(disable_user) {
451       fprintf(stderr, "%s: Only root can disable a user.\n", prog_name);
452       usage(prog_name, False);
453     }
454
455     if(enable_user) {
456       fprintf(stderr, "%s: Only root can enable a user.\n", prog_name);
457       usage(prog_name, False);
458     }
459
460     if(argc > 1)
461       usage(prog_name, False);
462
463     if(argc == 1) {
464       fstrcpy(new_passwd, argv[0]);
465       got_new_pass = True;
466     }
467
468     if(!remote_user_name && ((pwd = getpwuid(eff_uid)) != NULL))
469       pstrcpy( user_name, pwd->pw_name);
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
477     if(remote_machine == NULL)
478       remote_machine = "127.0.0.1";
479   }
480     
481   if (*user_name == '\0') {
482     fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
483     exit(1);
484   }
485
486   /*
487    * If we are adding a machine account then pretend
488    * we already have the new password, we will be using
489    * the machinename as the password.
490    */
491
492   if(add_user && trust_account) {
493     got_new_pass = True;
494     strncpy(new_passwd, user_name, sizeof(fstring));
495     new_passwd[sizeof(fstring)-1] = '\0';
496     strlower(new_passwd);
497     if(new_passwd[strlen(new_passwd)-1] == '$')
498       new_passwd[strlen(new_passwd)-1] = '\0';
499   }
500
501   /* 
502    * If we are root we don't ask for the old password (unless it's on a
503    * remote machine.
504    */
505
506   if (remote_machine != NULL) {
507     p = get_pass("Old SMB password:",stdin_passwd_get);
508     fstrcpy(old_passwd, p);
509   }
510
511   if (!got_new_pass)
512     prompt_for_new_password(new_passwd,stdin_passwd_get);
513   
514   if (new_passwd[0] == '\0') {
515     printf("Password not set\n");
516     exit(0);
517   }
518  
519   /* 
520    * Now do things differently depending on if we're changing the
521    * password on a remote machine. Remember - a normal user is
522    * always using this code, looping back to the local smbd.
523    */
524
525   if(remote_machine != NULL) {
526     struct cli_state cli;
527     struct in_addr ip;
528
529     if(!resolve_name( remote_machine, &ip, 0x20)) {
530       fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
531               prog_name, remote_machine );
532       exit(1);
533     }
534  
535     ZERO_STRUCT(cli);
536  
537     if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
538       fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
539               prog_name, remote_machine, cli_errstr(&cli) );
540       exit(1);
541     }
542   
543         make_nmb_name(&calling, global_myname , 0x0 , scope);
544         make_nmb_name(&called , remote_machine, 0x20, scope);
545
546         if (!cli_session_request(&cli, &calling, &called))
547         {
548       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
549               prog_name, remote_machine, cli_errstr(&cli) );
550       cli_shutdown(&cli);
551       exit(1);
552     }
553   
554     cli.protocol = PROTOCOL_NT1;
555
556     if (!cli_negprot(&cli)) {
557       fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",        
558               prog_name, remote_machine, cli_errstr(&cli) );
559       cli_shutdown(&cli);
560       exit(1);
561     }
562   
563     /*
564      * We should connect as the anonymous user here, in case
565      * the server has "must change password" checked...
566      * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
567      */
568
569     if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
570       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",        
571               prog_name, remote_machine, cli_errstr(&cli) );
572       cli_shutdown(&cli);
573       exit(1);
574     }               
575
576     if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
577       fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
578               prog_name, remote_machine, cli_errstr(&cli) );
579       cli_shutdown(&cli);
580       exit(1);
581     }
582
583     if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
584       fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
585               prog_name, remote_machine, cli_errstr(&cli) );
586       cli_shutdown(&cli);
587       exit(1);
588     }
589
590     cli_shutdown(&cli);
591     exit(0);
592   }
593
594   /*
595    * Check for a machine account.
596    */
597
598   if(trust_account && !pwd) {
599     fprintf(stderr, "%s: User %s does not exist in system password file \
600 (usually /etc/passwd). Cannot add machine account without a valid system user.\n",
601            prog_name, user_name);
602     exit(1);
603   }
604
605   /* Calculate the MD4 hash (NT compatible) of the new password. */
606   
607   nt_lm_owf_gen( new_passwd, new_nt_p16, new_p16);
608
609   /*
610    * Open the smbpaswd file.
611    */
612   vp = startsmbpwent(True);
613   if (!vp && errno == ENOENT) {
614       fprintf(stderr,"%s: smbpasswd file did not exist - attempting to create it.\n", prog_name);
615           fp = fopen(lp_smb_passwd_file(), "w");
616           if (fp) {
617                   fprintf(fp, "# Samba SMB password file\n");
618                   fclose(fp);
619                   vp = startsmbpwent(True);
620           }
621   }
622   if (!vp) {
623           err = errno;
624           fprintf(stderr, "%s: Failed to open password file %s.\n",
625                   prog_name, lp_smb_passwd_file());
626           errno = err;
627           perror(prog_name);
628           exit(err);
629   }
630   
631   /* Get the smb passwd entry for this user */
632   smb_pwent = getsmbpwnam(user_name);
633   if (smb_pwent == NULL) {
634     if(add_user == False) {
635       fprintf(stderr, "%s: Failed to find entry for user %s.\n",
636               prog_name, pwd->pw_name);
637       endsmbpwent(vp);
638       exit(1);
639     }
640
641     /* Create a new smb passwd entry and set it to the given password. */
642     {
643       struct smb_passwd new_smb_pwent;
644
645       new_smb_pwent.smb_userid = pwd->pw_uid;
646       new_smb_pwent.smb_name = pwd->pw_name; 
647       new_smb_pwent.smb_passwd = NULL;
648       new_smb_pwent.smb_nt_passwd = NULL;
649       new_smb_pwent.acct_ctrl = (trust_account ? ACB_WSTRUST : ACB_NORMAL);
650
651       if(disable_user) {
652         new_smb_pwent.acct_ctrl |= ACB_DISABLED;
653       } else if (set_no_password) {
654         new_smb_pwent.acct_ctrl |= ACB_PWNOTREQ;
655       } else {
656         new_smb_pwent.smb_passwd = new_p16;
657         new_smb_pwent.smb_nt_passwd = new_nt_p16;
658       }
659
660       if(add_smbpwd_entry(&new_smb_pwent) == False) {
661         fprintf(stderr, "%s: Failed to add entry for user %s.\n", 
662                 prog_name, pwd->pw_name);
663         endsmbpwent(vp);
664         exit(1);
665       }
666       
667       endsmbpwent(vp);
668       printf("%s: Added user %s.\n", prog_name, user_name);
669       exit(0);
670     }
671   } else {
672           /* the entry already existed */
673           add_user = False;
674   }
675
676   /*
677    * We are root - just write the new password
678    * and the valid last change time.
679    */
680
681   if(disable_user)
682     smb_pwent->acct_ctrl |= ACB_DISABLED;
683   else if (enable_user) {
684     if(smb_pwent->smb_passwd == NULL) {
685       prompt_for_new_password(new_passwd,stdin_passwd_get);
686       nt_lm_owf_gen( new_passwd, new_nt_p16, new_p16);
687       smb_pwent->smb_passwd = new_p16;
688       smb_pwent->smb_nt_passwd = new_nt_p16;
689     }
690     smb_pwent->acct_ctrl &= ~ACB_DISABLED;
691   } else if (set_no_password) {
692     smb_pwent->acct_ctrl |= ACB_PWNOTREQ;
693     /* This is needed to preserve ACB_PWNOTREQ in mod_smbfilepwd_entry */
694     smb_pwent->smb_passwd = NULL;
695     smb_pwent->smb_nt_passwd = NULL;
696   } else {
697     smb_pwent->acct_ctrl &= ~ACB_PWNOTREQ;
698     smb_pwent->smb_passwd = new_p16;
699     smb_pwent->smb_nt_passwd = new_nt_p16;
700   }
701
702   if(mod_smbpwd_entry(smb_pwent,True) == False) {
703     fprintf(stderr, "%s: Failed to modify entry for user %s.\n",
704             prog_name, pwd->pw_name);
705     endsmbpwent(vp);
706     exit(1);
707   }
708
709   endsmbpwent(vp);
710   if(disable_user)
711     printf("User %s disabled.\n", user_name);
712   else if(enable_user)
713     printf("User %s enabled.\n", user_name);
714   else if (set_no_password)
715     printf("User %s - set to no password.\n", user_name);
716   else
717     printf("Password changed for user %s.\n", user_name);
718   return 0;
719 }