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