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