first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[bbaumbach/samba-autobuild/.git] / source3 / passdb / smbpass.c
1 /*
2  * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
3  * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
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 #ifdef USE_SMBPASS_DB
23
24 extern int DEBUGLEVEL;
25 extern pstring samlogon_user;
26 extern BOOL sam_logon_in_ssb;
27
28 static char s_readbuf[1024];
29 static int pw_file_lock_depth;
30
31 /***************************************************************
32  Start to enumerate the smbpasswd list. Returns a void pointer
33  to ensure no modification outside this module.
34 ****************************************************************/
35
36 static void *startsmbfilepwent(BOOL update)
37 {
38   FILE *fp = NULL;
39   char *pfile = lp_smb_passwd_file();
40
41   if (!*pfile) {
42     DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
43     return (NULL);
44   }
45   DEBUG(10, ("startsmbfilepwent: opening file %s\n", pfile));
46
47   fp = sys_fopen(pfile, update ? "r+b" : "rb");
48
49   if (fp == NULL) {
50     DEBUG(0, ("startsmbfilepwent: unable to open file %s\n", pfile));
51     return NULL;
52   }
53
54   /* Set a buffer to do more efficient reads */
55   setvbuf(fp, s_readbuf, _IOFBF, sizeof(s_readbuf));
56
57   if (!pw_file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, &pw_file_lock_depth))
58   {
59     DEBUG(0, ("startsmbfilepwent: unable to lock file %s\n", pfile));
60     fclose(fp);
61     return NULL;
62   }
63
64   /* Make sure it is only rw by the owner */
65   chmod(pfile, 0600);
66
67   /* We have a lock on the file. */
68   return (void *)fp;
69 }
70
71 /***************************************************************
72  End enumeration of the smbpasswd list.
73 ****************************************************************/
74
75 static void endsmbfilepwent(void *vp)
76 {
77   FILE *fp = (FILE *)vp;
78
79   pw_file_unlock(fileno(fp), &pw_file_lock_depth);
80   fclose(fp);
81   DEBUG(7, ("endsmbfilepwent: closed password file.\n"));
82 }
83
84 /*************************************************************************
85  Routine to return the next entry in the smbpasswd list.
86  *************************************************************************/
87 static struct smb_passwd *getsmbfilepwent(void *vp)
88 {
89   /* Static buffers we will return. */
90   static struct smb_passwd pw_buf;
91   static pstring  user_name;
92   static unsigned char smbpwd[16];
93   static unsigned char smbntpwd[16];
94   FILE *fp = (FILE *)vp;
95   char            linebuf[256];
96   unsigned char   c;
97   unsigned char  *p;
98   long            uidval;
99   size_t            linebuf_len;
100
101   if(fp == NULL) {
102     DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
103     return NULL;
104   }
105
106   pdb_init_smb(&pw_buf);
107
108   pw_buf.acct_ctrl = ACB_NORMAL;  
109
110   /*
111    * Scan the file, a line at a time and check if the name matches.
112    */
113   while (!feof(fp)) {
114     linebuf[0] = '\0';
115
116     fgets(linebuf, 256, fp);
117     if (ferror(fp)) {
118       return NULL;
119     }
120
121     /*
122      * Check if the string is terminated with a newline - if not
123      * then we must keep reading and discard until we get one.
124      */
125     linebuf_len = strlen(linebuf);
126     if (linebuf[linebuf_len - 1] != '\n') {
127       c = '\0';
128       while (!ferror(fp) && !feof(fp)) {
129         c = fgetc(fp);
130         if (c == '\n')
131           break;
132       }
133     } else
134       linebuf[linebuf_len - 1] = '\0';
135
136 #ifdef DEBUG_PASSWORD
137     DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
138 #endif
139     if ((linebuf[0] == 0) && feof(fp)) {
140       DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
141       break;
142     }
143     /*
144      * The line we have should be of the form :-
145      * 
146      * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
147      * ignored....
148      * 
149      * or,
150      *
151      * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
152      *
153      * if Windows NT compatible passwords are also present.
154      * [Account type] is an ascii encoding of the type of account.
155      * LCT-(8 hex digits) is the time_t value of the last change time.
156      */
157
158     if (linebuf[0] == '#' || linebuf[0] == '\0') {
159       DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
160       continue;
161     }
162     p = (unsigned char *) strchr(linebuf, ':');
163     if (p == NULL) {
164       DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
165       continue;
166     }
167     /*
168      * As 256 is shorter than a pstring we don't need to check
169      * length here - if this ever changes....
170      */
171     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
172     user_name[PTR_DIFF(p, linebuf)] = '\0';
173
174     /* Get smb uid. */
175
176     p++;                /* Go past ':' */
177
178     if(*p == '-') {
179       DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
180       continue;
181     }
182
183     if (!isdigit(*p)) {
184       DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
185       continue;
186     }
187
188     uidval = atoi((char *) p);
189
190     while (*p && isdigit(*p))
191       p++;
192
193     if (*p != ':') {
194       DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
195       continue;
196     }
197
198     pw_buf.smb_name = user_name;
199     pw_buf.smb_userid = uidval;
200
201     /*
202      * Now get the password value - this should be 32 hex digits
203      * which are the ascii representations of a 16 byte string.
204      * Get two at a time and put them into the password.
205      */
206
207     /* Skip the ':' */
208     p++;
209
210     if (*p == '*' || *p == 'X') {
211       /* Password deliberately invalid - end here. */
212       DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
213       pw_buf.smb_nt_passwd = NULL;
214       pw_buf.smb_passwd = NULL;
215       pw_buf.acct_ctrl |= ACB_DISABLED;
216       return &pw_buf;
217     }
218
219     if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
220       DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
221       continue;
222     }
223
224     if (p[32] != ':') {
225       DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
226       continue;
227     }
228
229     if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
230       pw_buf.smb_passwd = NULL;
231       pw_buf.acct_ctrl |= ACB_PWNOTREQ;
232     } else {
233       if (!pdb_gethexpwd((char *)p, smbpwd)) {
234         DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
235         continue;
236       }
237       pw_buf.smb_passwd = smbpwd;
238     }
239
240     /* 
241      * Now check if the NT compatible password is
242      * available.
243      */
244     pw_buf.smb_nt_passwd = NULL;
245
246     p += 33; /* Move to the first character of the line after
247                 the lanman password. */
248     if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
249       if (*p != '*' && *p != 'X') {
250         if(pdb_gethexpwd((char *)p,smbntpwd))
251           pw_buf.smb_nt_passwd = smbntpwd;
252       }
253       p += 33; /* Move to the first character of the line after
254                   the NT password. */
255     }
256
257     DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
258              user_name, uidval));
259
260     if (*p == '[')
261         {
262       pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
263
264       /* Must have some account type set. */
265       if(pw_buf.acct_ctrl == 0)
266         pw_buf.acct_ctrl = ACB_NORMAL;
267
268       /* Now try and get the last change time. */
269       if(*p == ']')
270         p++;
271       if(*p == ':') {
272         p++;
273         if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
274           int i;
275           p += 4;
276           for(i = 0; i < 8; i++) {
277             if(p[i] == '\0' || !isxdigit(p[i]))
278               break;
279           }
280           if(i == 8) {
281             /*
282              * p points at 8 characters of hex digits - 
283              * read into a time_t as the seconds since
284              * 1970 that the password was last changed.
285              */
286             pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
287           }
288         }
289       }
290     } else {
291       /* 'Old' style file. Fake up based on user name. */
292       /*
293        * Currently trust accounts are kept in the same
294        * password file as 'normal accounts'. If this changes
295        * we will have to fix this code. JRA.
296        */
297       if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
298         pw_buf.acct_ctrl &= ~ACB_NORMAL;
299         pw_buf.acct_ctrl |= ACB_WSTRUST;
300       }
301     }
302
303     return &pw_buf;
304   }
305
306   DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
307   return NULL;
308 }
309
310 /*************************************************************************
311  Routine to return the next entry in the smbpasswd list.
312  this function is a nice, messy combination of reading:
313  - the smbpasswd file
314  - the unix password database
315  - smb.conf options (not done at present).
316  *************************************************************************/
317
318 static struct sam_passwd *getsmbfile21pwent(void *vp)
319 {
320         struct smb_passwd *pw_buf = getsmbfilepwent(vp);
321         static struct sam_passwd user;
322         struct passwd *pwfile;
323
324         static pstring full_name;
325         static pstring home_dir;
326         static pstring home_drive;
327         static pstring logon_script;
328         static pstring profile_path;
329         static pstring acct_desc;
330         static pstring workstations;
331         
332         DEBUG(5,("getsmbfile21pwent\n"));
333
334         if (pw_buf == NULL) return NULL;
335
336         pwfile = sys_getpwnam(pw_buf->smb_name);
337         if (pwfile == NULL)
338         {
339                 DEBUG(0,("getsmbfile21pwent: smbpasswd database is corrupt!\n"));
340                 DEBUG(0,("getsmbfile21pwent: username %s not in unix passwd database!\n", pw_buf->smb_name));
341                 return NULL;
342         }
343
344         pdb_init_sam(&user);
345
346         pstrcpy(samlogon_user, pw_buf->smb_name);
347
348         if (samlogon_user[strlen(samlogon_user)-1] != '$')
349         {
350                 /* XXXX hack to get standard_sub_basic() to use sam logon username */
351                 /* possibly a better way would be to do a become_user() call */
352                 sam_logon_in_ssb = True;
353
354                 user.smb_userid    = pw_buf->smb_userid;
355                 user.smb_grpid     = pwfile->pw_gid;
356
357                 user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
358                 user.group_rid = pdb_gid_to_group_rid(user.smb_grpid );
359
360                 pstrcpy(full_name    , pwfile->pw_gecos        );
361                 pstrcpy(logon_script , lp_logon_script       ());
362                 pstrcpy(profile_path , lp_logon_path         ());
363                 pstrcpy(home_drive   , lp_logon_drive        ());
364                 pstrcpy(home_dir     , lp_logon_home         ());
365                 pstrcpy(acct_desc    , "");
366                 pstrcpy(workstations , "");
367
368                 sam_logon_in_ssb = False;
369         }
370         else
371         {
372                 user.smb_userid    = pw_buf->smb_userid;
373                 user.smb_grpid     = pwfile->pw_gid;
374
375                 user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
376                 user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
377
378                 pstrcpy(full_name    , "");
379                 pstrcpy(logon_script , "");
380                 pstrcpy(profile_path , "");
381                 pstrcpy(home_drive   , "");
382                 pstrcpy(home_dir     , "");
383                 pstrcpy(acct_desc    , "");
384                 pstrcpy(workstations , "");
385         }
386
387         user.smb_name     = pw_buf->smb_name;
388         user.full_name    = full_name;
389         user.home_dir     = home_dir;
390         user.dir_drive    = home_drive;
391         user.logon_script = logon_script;
392         user.profile_path = profile_path;
393         user.acct_desc    = acct_desc;
394         user.workstations = workstations;
395
396         user.unknown_str = NULL; /* don't know, yet! */
397         user.munged_dial = NULL; /* "munged" dial-back telephone number */
398
399         user.smb_nt_passwd = pw_buf->smb_nt_passwd;
400         user.smb_passwd    = pw_buf->smb_passwd;
401                         
402         user.acct_ctrl = pw_buf->acct_ctrl;
403
404         user.unknown_3 = 0xffffff; /* don't know */
405         user.logon_divs = 168; /* hours per week */
406         user.hours_len = 21; /* 21 times 8 bits = 168 */
407         memset(user.hours, 0xff, user.hours_len); /* available at all hours */
408         user.unknown_5 = 0x00020000; /* don't know */
409         user.unknown_5 = 0x000004ec; /* don't know */
410
411         return &user;
412 }
413
414 /*************************************************************************
415  Return the current position in the smbpasswd list as an SMB_BIG_UINT.
416  This must be treated as an opaque token.
417 *************************************************************************/
418
419 static SMB_BIG_UINT getsmbfilepwpos(void *vp)
420 {
421   return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
422 }
423
424 /*************************************************************************
425  Set the current position in the smbpasswd list from an SMB_BIG_UINT.
426  This must be treated as an opaque token.
427 *************************************************************************/
428
429 static BOOL setsmbfilepwpos(void *vp, SMB_BIG_UINT tok)
430 {
431   return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
432 }
433
434 /************************************************************************
435  Routine to add an entry to the smbpasswd file.
436 *************************************************************************/
437
438 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
439 {
440   char *pfile = lp_smb_passwd_file();
441   struct smb_passwd *pwd = NULL;
442   FILE *fp = NULL;
443
444   int i;
445   int wr_len;
446
447   int fd;
448   int new_entry_length;
449   char *new_entry;
450   SMB_OFF_T offpos;
451   char *p;
452
453   /* Open the smbpassword file - for update. */
454   fp = startsmbfilepwent(True);
455
456   if (fp == NULL) {
457     DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
458     return False;
459   }
460
461   /*
462    * Scan the file, a line at a time and check if the name matches.
463    */
464
465   while ((pwd = getsmbfilepwent(fp)) != NULL) {
466     if (strequal(newpwd->smb_name, pwd->smb_name)) {
467       DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
468       endsmbfilepwent(fp);
469       return False;
470     }
471   }
472
473   /* Ok - entry doesn't exist. We can add it */
474
475   /* Create a new smb passwd entry and set it to the given password. */
476   /* 
477    * The add user write needs to be atomic - so get the fd from 
478    * the fp and do a raw write() call.
479    */
480   fd = fileno(fp);
481
482   if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
483     DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
484 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
485     endsmbfilepwent(fp);
486     return False;
487   }
488
489   new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
490
491   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
492     DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
493 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
494     endsmbfilepwent(fp);
495     return False;
496   }
497
498   slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
499   p = &new_entry[strlen(new_entry)];
500
501   if(newpwd->smb_passwd != NULL) {
502     for( i = 0; i < 16; i++) {
503       slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
504     }
505   } else {
506     i=0;
507     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
508       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
509     else
510       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
511   }
512   
513   p += 32;
514
515   *p++ = ':';
516
517   if(newpwd->smb_nt_passwd != NULL) {
518     for( i = 0; i < 16; i++) {
519       slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
520     }
521   } else {
522     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
523       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
524     else
525       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
526   }
527
528   p += 32;
529
530   *p++ = ':';
531
532   /* Add the account encoding and the last change time. */
533   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
534            pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), (uint32)time(NULL));
535
536 #ifdef DEBUG_PASSWORD
537   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d entry_len %d made line |%s|", 
538                              fd, new_entry_length, strlen(new_entry), new_entry));
539 #endif
540
541   if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry)) {
542     DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
543 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
544
545     /* Remove the entry we just wrote. */
546     if(sys_ftruncate(fd, offpos) == -1) {
547       DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
548 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
549              newpwd->smb_name, strerror(errno)));
550     }
551
552     endsmbfilepwent(fp);
553     free(new_entry);
554     return False;
555   }
556
557   free(new_entry);
558   endsmbfilepwent(fp);
559   return True;
560 }
561
562 /************************************************************************
563  Routine to search the smbpasswd file for an entry matching the username.
564  and then modify its password entry. We can't use the startsmbpwent()/
565  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
566  in the actual file to decide how much room we have to write data.
567  override = False, normal
568  override = True, override XXXXXXXX'd out password or NO PASS
569 ************************************************************************/
570
571 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
572 {
573   /* Static buffers we will return. */
574   static pstring  user_name;
575
576   char            linebuf[256];
577   char            readbuf[1024];
578   unsigned char   c;
579   fstring         ascii_p16;
580   fstring         encode_bits;
581   unsigned char  *p = NULL;
582   size_t            linebuf_len = 0;
583   FILE           *fp;
584   int             lockfd;
585   char           *pfile = lp_smb_passwd_file();
586   BOOL found_entry = False;
587   BOOL got_pass_last_set_time = False;
588
589   SMB_OFF_T pwd_seekpos = 0;
590
591   int i;
592   int wr_len;
593   int fd;
594
595   if (!*pfile) {
596     DEBUG(0, ("No SMB password file set\n"));
597     return False;
598   }
599   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
600
601   fp = sys_fopen(pfile, "r+");
602
603   if (fp == NULL) {
604     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
605     return False;
606   }
607   /* Set a buffer to do more efficient reads */
608   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
609
610   lockfd = fileno(fp);
611
612   if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
613     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
614     fclose(fp);
615     return False;
616   }
617
618   /* Make sure it is only rw by the owner */
619   chmod(pfile, 0600);
620
621   /* We have a write lock on the file. */
622   /*
623    * Scan the file, a line at a time and check if the name matches.
624    */
625   while (!feof(fp)) {
626     pwd_seekpos = sys_ftell(fp);
627
628     linebuf[0] = '\0';
629
630     fgets(linebuf, sizeof(linebuf), fp);
631     if (ferror(fp)) {
632       pw_file_unlock(lockfd, &pw_file_lock_depth);
633       fclose(fp);
634       return False;
635     }
636
637     /*
638      * Check if the string is terminated with a newline - if not
639      * then we must keep reading and discard until we get one.
640      */
641     linebuf_len = strlen(linebuf);
642     if (linebuf[linebuf_len - 1] != '\n') {
643       c = '\0';
644       while (!ferror(fp) && !feof(fp)) {
645         c = fgetc(fp);
646         if (c == '\n') {
647           break;
648         }
649       }
650     } else {
651       linebuf[linebuf_len - 1] = '\0';
652     }
653
654 #ifdef DEBUG_PASSWORD
655     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
656 #endif
657
658     if ((linebuf[0] == 0) && feof(fp)) {
659       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
660       break;
661     }
662
663     /*
664      * The line we have should be of the form :-
665      * 
666      * username:uid:[32hex bytes]:....other flags presently
667      * ignored....
668      * 
669      * or,
670      *
671      * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
672      *
673      * if Windows NT compatible passwords are also present.
674      */
675
676     if (linebuf[0] == '#' || linebuf[0] == '\0') {
677       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
678       continue;
679     }
680
681     p = (unsigned char *) strchr(linebuf, ':');
682
683     if (p == NULL) {
684       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
685       continue;
686     }
687
688     /*
689      * As 256 is shorter than a pstring we don't need to check
690      * length here - if this ever changes....
691      */
692     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
693     user_name[PTR_DIFF(p, linebuf)] = '\0';
694     if (strequal(user_name, pwd->smb_name)) {
695       found_entry = True;
696       break;
697     }
698   }
699
700   if (!found_entry) {
701     pw_file_unlock(lockfd, &pw_file_lock_depth);
702     fclose(fp);
703     return False;
704   }
705
706   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
707
708   /* User name matches - get uid and password */
709   p++;          /* Go past ':' */
710
711   if (!isdigit(*p)) {
712     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
713     pw_file_unlock(lockfd, &pw_file_lock_depth);
714     fclose(fp);
715     return False;
716   }
717
718   while (*p && isdigit(*p))
719     p++;
720   if (*p != ':') {
721     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
722     pw_file_unlock(lockfd, &pw_file_lock_depth);
723     fclose(fp);
724     return False;
725   }
726
727   /*
728    * Now get the password value - this should be 32 hex digits
729    * which are the ascii representations of a 16 byte string.
730    * Get two at a time and put them into the password.
731    */
732   p++;
733
734   /* Record exact password position */
735   pwd_seekpos += PTR_DIFF(p, linebuf);
736
737   if (!override && (*p == '*' || *p == 'X')) {
738     /* Password deliberately invalid - end here. */
739     DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
740     pw_file_unlock(lockfd, &pw_file_lock_depth);
741     fclose(fp);
742     return False;
743   }
744
745   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
746     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
747     pw_file_unlock(lockfd,&pw_file_lock_depth);
748     fclose(fp);
749     return (False);
750   }
751
752   if (p[32] != ':') {
753     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
754     pw_file_unlock(lockfd,&pw_file_lock_depth);
755     fclose(fp);
756     return False;
757   }
758
759   if (!override && (*p == '*' || *p == 'X')) {
760     pw_file_unlock(lockfd,&pw_file_lock_depth);
761     fclose(fp);
762     return False;
763   }
764
765   /* Now check if the NT compatible password is
766      available. */
767   p += 33; /* Move to the first character of the line after
768               the lanman password. */
769   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
770     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
771     pw_file_unlock(lockfd,&pw_file_lock_depth);
772     fclose(fp);
773     return (False);
774   }
775
776   if (p[32] != ':') {
777     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
778     pw_file_unlock(lockfd,&pw_file_lock_depth);
779     fclose(fp);
780     return False;
781   }
782
783   /* 
784    * Now check if the account info and the password last
785    * change time is available.
786    */
787   p += 33; /* Move to the first character of the line after
788               the NT password. */
789
790   /*
791    * If both NT and lanman passwords are provided - reset password
792    * not required flag.
793    */
794
795   if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) {
796     /* Reqiure password in the future (should ACB_DISABLED also be reset?) */
797     pwd->acct_ctrl &= ~(ACB_PWNOTREQ);
798   }
799
800   if (*p == '[') {
801
802     i = 0;
803     encode_bits[i++] = *p++;
804     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
805       encode_bits[i++] = *p++;
806
807     encode_bits[i++] = ']';
808     encode_bits[i++] = '\0';
809
810     if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
811       /*
812        * We are using a new format, space padded
813        * acct ctrl field. Encode the given acct ctrl
814        * bits into it.
815        */
816       fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
817     } else {
818       /*
819        * If using the old format and the ACB_DISABLED or
820        * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
821        * here as we have no space to encode the change.
822        */
823       if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
824         pwd->smb_passwd = NULL;
825         pwd->smb_nt_passwd = NULL;
826       }
827     }
828
829     /* Go past the ']' */
830     if(linebuf_len > PTR_DIFF(p, linebuf))
831       p++;
832
833     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
834       p++;
835
836       /* We should be pointing at the LCT entry. */
837       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
838
839         p += 4;
840         for(i = 0; i < 8; i++) {
841           if(p[i] == '\0' || !isxdigit(p[i]))
842             break;
843         }
844         if(i == 8) {
845           /*
846            * p points at 8 characters of hex digits -
847            * read into a time_t as the seconds since
848            * 1970 that the password was last changed.
849            */
850           got_pass_last_set_time = True;
851         } /* i == 8 */
852       } /* *p && StrnCaseCmp() */
853     } /* p == ':' */
854   } /* p == '[' */
855
856   /* Entry is correctly formed. */
857
858   /* Create the 32 byte representation of the new p16 */
859   if(pwd->smb_passwd != NULL) {
860     for (i = 0; i < 16; i++) {
861       slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
862     }
863   } else {
864     if(pwd->acct_ctrl & ACB_PWNOTREQ)
865       fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
866     else
867       fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
868   }
869
870   /* Add on the NT md4 hash */
871   ascii_p16[32] = ':';
872   wr_len = 66;
873   if (pwd->smb_nt_passwd != NULL) {
874     for (i = 0; i < 16; i++) {
875       slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
876     }
877   } else {
878     if(pwd->acct_ctrl & ACB_PWNOTREQ)
879       fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
880     else
881       fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
882   }
883   ascii_p16[65] = ':';
884   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
885
886   /* Add on the account info bits and the time of last
887      password change. */
888
889   pwd->pass_last_set_time = time(NULL);
890
891   if(got_pass_last_set_time) {
892     slprintf(&ascii_p16[strlen(ascii_p16)], 
893              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
894              "%s:LCT-%08X:", 
895                      encode_bits, (uint32)pwd->pass_last_set_time );
896     wr_len = strlen(ascii_p16);
897   }
898
899 #ifdef DEBUG_PASSWORD
900   DEBUG(100,("mod_smbfilepwd_entry: "));
901   dump_data(100, ascii_p16, wr_len);
902 #endif
903
904   if(wr_len > sizeof(linebuf)) {
905     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
906     pw_file_unlock(lockfd,&pw_file_lock_depth);
907     fclose(fp);
908     return (False);
909   }
910
911   /*
912    * Do an atomic write into the file at the position defined by
913    * seekpos.
914    */
915
916   /* The mod user write needs to be atomic - so get the fd from 
917      the fp and do a raw write() call.
918    */
919
920   fd = fileno(fp);
921
922   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
923     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
924     pw_file_unlock(lockfd,&pw_file_lock_depth);
925     fclose(fp);
926     return False;
927   }
928
929   /* Sanity check - ensure the areas we are writing are framed by ':' */
930   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
931     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
932     pw_file_unlock(lockfd,&pw_file_lock_depth);
933     fclose(fp);
934     return False;
935   }
936
937   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
938     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
939     pw_file_unlock(lockfd,&pw_file_lock_depth);
940     fclose(fp);
941     return False;
942   }
943  
944   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
945     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
946     pw_file_unlock(lockfd,&pw_file_lock_depth);
947     fclose(fp);
948     return False;
949   }
950
951   if (write(fd, ascii_p16, wr_len) != wr_len) {
952     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
953     pw_file_unlock(lockfd,&pw_file_lock_depth);
954     fclose(fp);
955     return False;
956   }
957
958   pw_file_unlock(lockfd,&pw_file_lock_depth);
959   fclose(fp);
960   return True;
961 }
962
963 /*
964  * Stub functions - implemented in terms of others.
965  */
966
967 static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
968 {
969         return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override);
970 }
971
972 static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
973 {
974         return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd));
975 }
976
977 static struct sam_disp_info *getsmbfiledispnam(char *name)
978 {
979         return pdb_sam_to_dispinfo(getsam21pwnam(name));
980 }
981
982 static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
983 {
984         return pdb_sam_to_dispinfo(getsam21pwrid(rid));
985 }
986
987 static struct sam_disp_info *getsmbfiledispent(void *vp)
988 {
989         return pdb_sam_to_dispinfo(getsam21pwent(vp));
990 }
991
992 static struct passdb_ops file_ops = {
993   startsmbfilepwent,
994   endsmbfilepwent,
995   getsmbfilepwpos,
996   setsmbfilepwpos,
997   iterate_getsmbpwnam,          /* In passdb.c */
998   iterate_getsmbpwuid,          /* In passdb.c */
999   iterate_getsmbpwrid,          /* In passdb.c */
1000   getsmbfilepwent,
1001   add_smbfilepwd_entry,
1002   mod_smbfilepwd_entry,
1003   getsmbfile21pwent,
1004   iterate_getsam21pwnam,
1005   iterate_getsam21pwuid,
1006   iterate_getsam21pwrid, 
1007   add_smbfile21pwd_entry,
1008   mod_smbfile21pwd_entry,
1009   getsmbfiledispnam,
1010   getsmbfiledisprid,
1011   getsmbfiledispent
1012 };
1013
1014 struct passdb_ops *file_initialize_password_db(void)
1015 {    
1016   return &file_ops;
1017 }
1018
1019 #else
1020  /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
1021  void smbpass_dummy_function(void) { } /* stop some compilers complaining */
1022 #endif /* USE_SMBPASS_DB */