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