- static function "create_new_hashes" was identical to "nt_lm_owf_gen".
[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] [-a] [-d] [-e] [-m] [-n] [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] [-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(lp_domain_controller() && strequal(lp_workgroup(), domain)) {
64     fprintf(stderr, "%s: Cannot join domain %s as we already configured as \
65 domain controller for that domain.\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 new password.
118 *************************************************************/
119
120 static void prompt_for_new_password(char *new_passwd)
121 {
122   char *p;
123
124   new_passwd[0] = '\0';
125   
126   p = getpass("New SMB password:");
127
128   strncpy(new_passwd, p, sizeof(fstring));
129   new_passwd[sizeof(fstring)-1] = '\0';
130
131   p = getpass("Retype new SMB password:");
132
133   if (strncmp(p, new_passwd, sizeof(fstring)-1))
134   {
135     fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
136     exit(1);
137   }
138
139   if (new_passwd[0] == '\0') {
140     printf("Password not set\n");
141     exit(0);
142   }
143 }
144
145 /*********************************************************
146  Start here.
147 **********************************************************/
148
149 int main(int argc, char **argv)
150 {
151   extern char *optarg;
152   extern int optind;
153   extern int DEBUGLEVEL;
154   int             real_uid;
155   struct passwd  *pwd = NULL;
156   fstring         old_passwd;
157   fstring         new_passwd;
158   uchar           new_p16[16];
159   uchar           new_nt_p16[16];
160   char           *p;
161   struct smb_passwd *smb_pwent;
162   FILE           *fp;
163   int             ch;
164   int             err;
165   BOOL is_root = False;
166   pstring  user_name;
167   BOOL remote_user_name = False;
168   char *remote_machine = NULL;
169   BOOL add_user = False;
170   BOOL got_new_pass = False;
171   BOOL trust_account = False;
172   BOOL disable_user = False;
173   BOOL enable_user = False;
174   BOOL set_no_password = False;
175   BOOL joining_domain = False;
176   char *new_domain = NULL;
177   pstring servicesf = CONFIGFILE;
178   void           *vp;
179         struct nmb_name calling, called;
180   
181
182   new_passwd[0] = '\0';
183   user_name[0] = '\0';
184
185   memset(old_passwd, '\0', sizeof(old_passwd));
186   memset(new_passwd, '\0', sizeof(new_passwd));
187
188   prog_name = argv[0];
189
190   TimeInit();
191
192   setup_logging(prog_name,True);
193   
194   charset_initialise();
195
196   if(!initialize_password_db()) {
197     fprintf(stderr, "%s: Can't setup password database vectors.\n", prog_name);
198     exit(1);
199   }
200
201   if (!lp_load(servicesf,True,False,False)) {
202     fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
203     exit(1);
204   }
205
206   if(!get_myname(myhostname,NULL)) {
207     fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
208     exit(1);
209   }
210
211   /*
212    * Set the machine NETBIOS name if not already
213    * set from the config file. 
214    */ 
215     
216   if (!*global_myname)
217   {   
218     fstrcpy( global_myname, myhostname );
219     p = strchr( global_myname, '.' );
220     if (p) 
221       *p = 0;
222   }           
223   strupper( global_myname );
224
225   codepage_initialise(lp_client_code_page());
226
227   /* Get the real uid */
228   real_uid = getuid();
229   
230   /* Check the effective uid */
231   if ((geteuid() == 0) && (real_uid != 0)) {
232     fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
233     exit(1);
234   }
235
236   is_root = (real_uid == 0);
237
238   while ((ch = getopt(argc, argv, "adehmnj:r:R:D:U:")) != EOF) {
239     switch(ch) {
240     case 'a':
241       if(is_root)
242         add_user = True;
243       else
244         usage(prog_name, is_root);
245       break;
246     case 'd':
247       if(is_root) {
248         disable_user = True;
249         got_new_pass = True;
250         fstrcpy(new_passwd, "XXXXXX");
251       } else
252         usage(prog_name, is_root);
253       break;
254     case 'e':
255       if(is_root) {
256         enable_user = True;
257         got_new_pass = True;
258         fstrcpy(new_passwd, "XXXXXX");
259       } else
260         usage(prog_name, is_root);
261       break;
262     case 'D':
263       DEBUGLEVEL = atoi(optarg);
264       break;
265     case 'n':
266       if(is_root) {
267         set_no_password = True;
268         got_new_pass = True;
269         fstrcpy(new_passwd, "NO PASSWORD");
270       } else
271         usage(prog_name, is_root);
272     case 'r':
273       remote_machine = optarg;
274       break;
275     case 'R':
276       if(is_root) {
277         lp_set_name_resolve_order(optarg);
278         break;
279       } else
280         usage(prog_name, is_root);
281     case 'm':
282       if(is_root) {
283         trust_account = True;
284       } else
285         usage(prog_name, is_root);
286       break;
287     case 'j':
288       if(is_root) {
289         new_domain = optarg;
290         strupper(new_domain);
291         joining_domain = True;
292       } else
293         usage(prog_name, is_root);  
294       break;
295     case 'U':
296       remote_user_name = True;
297       pstrcpy(user_name, optarg);
298       break;
299     case 'h':
300     default:
301       usage(prog_name, is_root);
302     }
303   }
304
305   argc -= optind;
306   argv += optind;
307
308   if (!is_root && remote_user_name && !remote_machine) {
309     fprintf(stderr, "%s: You can only use -U with -r.\n", prog_name);
310     usage(prog_name, False);
311   }
312
313   /*
314    * Ensure add_user and either remote machine or join domain are
315    * not both set.
316    */
317
318   if(add_user && ((remote_machine != NULL) || joining_domain))
319     usage(prog_name, True);
320
321   /*
322    * Deal with joining a domain.
323    */
324   if(joining_domain && argc != 0)
325     usage(prog_name, True);
326
327   if(joining_domain) {
328     return join_domain( new_domain, remote_machine);
329   }
330
331   if(is_root) {
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         /* If we are root we can change another's password. */
342         pstrcpy(user_name, argv[0]);
343         break;
344       case 2:
345         pstrcpy(user_name, argv[0]);
346         fstrcpy(new_passwd, argv[1]);
347         got_new_pass = True;
348         break;
349       default:
350         usage(prog_name, True);
351     }
352
353     if(*user_name) {
354
355       if(trust_account) {
356         int username_len = strlen(user_name);
357         if(username_len >= sizeof(pstring) - 1) {
358           fprintf(stderr, "%s: machine account name too long.\n", user_name);
359           exit(1);
360         }
361
362         if(user_name[username_len-1] != '$') {
363           user_name[username_len] = '$';
364           user_name[username_len+1] = '\0';
365         }
366       }
367
368       if(!remote_machine && ((pwd = Get_Pwnam(user_name, True)) == NULL)) {
369         fprintf(stderr, "%s: User \"%s\" was not found in system password file.\n", 
370                     prog_name, user_name);
371         exit(1);
372       }
373     } else {
374       if((pwd = getpwuid(real_uid)) != NULL)
375         pstrcpy( user_name, pwd->pw_name);
376     }
377
378   } else {
379
380     if(add_user) {
381       fprintf(stderr, "%s: Only root can add a user.\n", prog_name);
382       usage(prog_name, False);
383     }
384
385     if(disable_user) {
386       fprintf(stderr, "%s: Only root can disable a user.\n", prog_name);
387       usage(prog_name, False);
388     }
389
390     if(enable_user) {
391       fprintf(stderr, "%s: Only root can enable a user.\n", prog_name);
392       usage(prog_name, False);
393     }
394
395     if(argc > 1)
396       usage(prog_name, False);
397
398     if(argc == 1) {
399       fstrcpy(new_passwd, argv[0]);
400       got_new_pass = True;
401     }
402
403     if(!remote_user_name && ((pwd = getpwuid(real_uid)) != NULL))
404       pstrcpy( user_name, pwd->pw_name);
405
406     /*
407      * A non-root user is always setting a password
408      * via a remote machine (even if that machine is
409      * localhost).
410      */
411
412     if(remote_machine == NULL)
413       remote_machine = "127.0.0.1";
414   }    
415     
416   if (*user_name == '\0') {
417     fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
418     exit(1);
419   }
420
421   /*
422    * If we are adding a machine account then pretend
423    * we already have the new password, we will be using
424    * the machinename as the password.
425    */
426
427   if(add_user && trust_account) {
428     got_new_pass = True;
429     strncpy(new_passwd, user_name, sizeof(fstring));
430     new_passwd[sizeof(fstring)-1] = '\0';
431     strlower(new_passwd);
432     if(new_passwd[strlen(new_passwd)-1] == '$')
433       new_passwd[strlen(new_passwd)-1] = '\0';
434   }
435
436   /* 
437    * If we are root we don't ask for the old password (unless it's on a
438    * remote machine.
439    */
440
441   if (remote_machine != NULL) {
442     p = getpass("Old SMB password:");
443     fstrcpy(old_passwd, p);
444   }
445
446   if (!got_new_pass)
447     prompt_for_new_password(new_passwd);
448   
449   if (new_passwd[0] == '\0') {
450     printf("Password not set\n");
451     exit(0);
452   }
453  
454   /* 
455    * Now do things differently depending on if we're changing the
456    * password on a remote machine. Remember - a normal user is
457    * always using this code, looping back to the local smbd.
458    */
459
460   if(remote_machine != NULL) {
461     struct cli_state cli;
462     struct in_addr ip;
463
464     if(!resolve_name( remote_machine, &ip)) {
465       fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
466               prog_name, remote_machine );
467       exit(1);
468     }
469  
470     ZERO_STRUCT(cli);
471  
472     if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
473       fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
474               prog_name, remote_machine, cli_errstr(&cli) );
475       exit(1);
476     }
477   
478         make_nmb_name(&calling, global_myname , 0x0 , scope);
479         make_nmb_name(&called , remote_machine, 0x20, scope);
480
481         if (!cli_session_request(&cli, &calling, &called))
482         {
483       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",
484               prog_name, remote_machine, cli_errstr(&cli) );
485       cli_shutdown(&cli);
486       exit(1);
487     }
488   
489     cli.protocol = PROTOCOL_NT1;
490
491     if (!cli_negprot(&cli)) {
492       fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",        
493               prog_name, remote_machine, cli_errstr(&cli) );
494       cli_shutdown(&cli);
495       exit(1);
496     }
497   
498     if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
499                            "", 0, "")) {
500       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",        
501               prog_name, remote_machine, cli_errstr(&cli) );
502       cli_shutdown(&cli);
503       exit(1);
504     }               
505
506     if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
507       fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
508               prog_name, remote_machine, cli_errstr(&cli) );
509       cli_shutdown(&cli);
510       exit(1);
511     }
512
513     if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
514       fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
515               prog_name, remote_machine, cli_errstr(&cli) );
516       cli_shutdown(&cli);
517       exit(1);
518     }
519
520     cli_shutdown(&cli);
521     exit(0);
522   }
523
524   /*
525    * Check for a machine account.
526    */
527
528   if(trust_account && !pwd) {
529     fprintf(stderr, "%s: User %s does not exist in system password file \
530 (usually /etc/passwd). Cannot add machine account without a valid system user.\n",
531            prog_name, user_name);
532     exit(1);
533   }
534
535   /* Calculate the MD4 hash (NT compatible) of the new password. */
536   
537   nt_lm_owf_gen( new_passwd, new_nt_p16, new_p16);
538
539   /*
540    * Open the smbpaswd file.
541    */
542   vp = startsmbpwent(True);
543   if (!vp && errno == ENOENT) {
544       fprintf(stderr,"%s: smbpasswd file did not exist - attempting to create it.\n", prog_name);
545           fp = fopen(lp_smb_passwd_file(), "w");
546           if (fp) {
547                   fprintf(fp, "# Samba SMB password file\n");
548                   fclose(fp);
549                   vp = startsmbpwent(True);
550           }
551   }
552   if (!vp) {
553           err = errno;
554           fprintf(stderr, "%s: Failed to open password file %s.\n",
555                   prog_name, lp_smb_passwd_file());
556           errno = err;
557           perror(prog_name);
558           exit(err);
559   }
560   
561   /* Get the smb passwd entry for this user */
562   smb_pwent = getsmbpwnam(user_name);
563   if (smb_pwent == NULL) {
564     if(add_user == False) {
565       fprintf(stderr, "%s: Failed to find entry for user %s.\n",
566               prog_name, pwd->pw_name);
567       endsmbpwent(vp);
568       exit(1);
569     }
570
571     /* Create a new smb passwd entry and set it to the given password. */
572     {
573       struct smb_passwd new_smb_pwent;
574
575       new_smb_pwent.smb_userid = pwd->pw_uid;
576       new_smb_pwent.smb_name = pwd->pw_name; 
577       new_smb_pwent.smb_passwd = NULL;
578       new_smb_pwent.smb_nt_passwd = NULL;
579       new_smb_pwent.acct_ctrl = (trust_account ? ACB_WSTRUST : ACB_NORMAL);
580
581       if(disable_user) {
582         new_smb_pwent.acct_ctrl |= ACB_DISABLED;
583       } else if (set_no_password) {
584         new_smb_pwent.acct_ctrl |= ACB_PWNOTREQ;
585       } else {
586         new_smb_pwent.smb_passwd = new_p16;
587         new_smb_pwent.smb_nt_passwd = new_nt_p16;
588       }
589
590       if(add_smbpwd_entry(&new_smb_pwent) == False) {
591         fprintf(stderr, "%s: Failed to add entry for user %s.\n", 
592                 prog_name, pwd->pw_name);
593         endsmbpwent(vp);
594         exit(1);
595       }
596       
597       endsmbpwent(vp);
598       printf("%s: Added user %s.\n", prog_name, user_name);
599       exit(0);
600     }
601   } else {
602           /* the entry already existed */
603           add_user = False;
604   }
605
606   /*
607    * We are root - just write the new password
608    * and the valid last change time.
609    */
610
611   if(disable_user)
612     smb_pwent->acct_ctrl |= ACB_DISABLED;
613   else if (enable_user) {
614     if(smb_pwent->smb_passwd == NULL) {
615       prompt_for_new_password(new_passwd);
616       nt_lm_owf_gen( new_passwd, new_nt_p16, new_p16);
617       smb_pwent->smb_passwd = new_p16;
618       smb_pwent->smb_nt_passwd = new_nt_p16;
619     }
620     smb_pwent->acct_ctrl &= ~ACB_DISABLED;
621   } else if (set_no_password) {
622     smb_pwent->acct_ctrl |= ACB_PWNOTREQ;
623     /* This is needed to preserve ACB_PWNOTREQ in mod_smbfilepwd_entry */
624     smb_pwent->smb_passwd = NULL;
625     smb_pwent->smb_nt_passwd = NULL;
626   } else {
627     smb_pwent->acct_ctrl &= ~ACB_PWNOTREQ;
628     smb_pwent->smb_passwd = new_p16;
629     smb_pwent->smb_nt_passwd = new_nt_p16;
630   }
631
632   if(mod_smbpwd_entry(smb_pwent,True) == False) {
633     fprintf(stderr, "%s: Failed to modify entry for user %s.\n",
634             prog_name, pwd->pw_name);
635     endsmbpwent(vp);
636     exit(1);
637   }
638
639   endsmbpwent(vp);
640   if(disable_user)
641     printf("User %s disabled.\n", user_name);
642   else if(enable_user)
643     printf("User %s enabled.\n", user_name);
644   else if (set_no_password)
645     printf("User %s - set to no password.\n", user_name);
646   else
647     printf("Password changed for user %s.\n", user_name);
648   return 0;
649 }