bdf01ee6a8bfc01c37d7314a20f6b335c6c3f287
[samba.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 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 static 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 static 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 static 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 static 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 static 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         unsigned 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((char *) 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((char *) 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((char *)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((char *)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
268         static pstring full_name;
269         static pstring home_dir;
270         static pstring home_drive;
271         static pstring logon_script;
272         static pstring profile_path;
273         static pstring acct_desc;
274         static pstring workstations;
275         
276         DEBUG(5,("getsmbfile21pwent\n"));
277
278         if (pw_buf == NULL) return NULL;
279
280         pwdb_init_sam(&user);
281
282         pwfile = Get_Pwnam(pw_buf->smb_name, False);
283         if (pwfile == NULL)
284         {
285                 DEBUG(0,("getsmbfile21pwent: smbpasswd database is corrupt!\n"));
286                 DEBUG(0,("getsmbfile21pwent: username %s not in unix passwd database!\n", pw_buf->smb_name));
287                 return NULL;
288         }
289
290         pstrcpy(samlogon_user, pw_buf->smb_name);
291
292         if (samlogon_user[strlen(samlogon_user)-1] != '$')
293         {
294                 /* XXXX hack to get standard_sub_basic() to use sam logon username */
295                 /* possibly a better way would be to do a become_user() call */
296                 sam_logon_in_ssb = True;
297
298                 user.smb_userid    = pw_buf->smb_userid;
299                 user.smb_grpid     = pwfile->pw_gid;
300
301                 user.user_rid  = pwdb_uid_to_user_rid (user.smb_userid);
302                 user.group_rid = pwdb_gid_to_group_rid(user.smb_grpid );
303
304                 pstrcpy(full_name    , pwfile->pw_gecos        );
305                 pstrcpy(logon_script , lp_logon_script       ());
306                 pstrcpy(profile_path , lp_logon_path         ());
307                 pstrcpy(home_drive   , lp_logon_drive        ());
308                 pstrcpy(home_dir     , lp_logon_home         ());
309                 pstrcpy(acct_desc    , "");
310                 pstrcpy(workstations , "");
311
312                 sam_logon_in_ssb = False;
313         }
314         else
315         {
316                 user.smb_userid    = pw_buf->smb_userid;
317                 user.smb_grpid     = pwfile->pw_gid;
318
319                 user.user_rid  = pwdb_uid_to_user_rid (user.smb_userid);
320                 user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
321
322                 pstrcpy(full_name    , "");
323                 pstrcpy(logon_script , "");
324                 pstrcpy(profile_path , "");
325                 pstrcpy(home_drive   , "");
326                 pstrcpy(home_dir     , "");
327                 pstrcpy(acct_desc    , "");
328                 pstrcpy(workstations , "");
329         }
330
331         user.smb_name     = pw_buf->smb_name;
332         user.full_name    = full_name;
333         user.home_dir     = home_dir;
334         user.dir_drive    = home_drive;
335         user.logon_script = logon_script;
336         user.profile_path = profile_path;
337         user.acct_desc    = acct_desc;
338         user.workstations = workstations;
339
340         user.unknown_str = NULL; /* don't know, yet! */
341         user.munged_dial = NULL; /* "munged" dial-back telephone number */
342
343         user.smb_nt_passwd = pw_buf->smb_nt_passwd;
344         user.smb_passwd    = pw_buf->smb_passwd;
345                         
346         user.acct_ctrl = pw_buf->acct_ctrl;
347
348         user.unknown_3 = 0xffffff; /* don't know */
349         user.logon_divs = 168; /* hours per week */
350         user.hours_len = 21; /* 21 times 8 bits = 168 */
351         memset(user.hours, 0xff, user.hours_len); /* available at all hours */
352         user.unknown_5 = 0x00020000; /* don't know */
353         user.unknown_5 = 0x000004ec; /* don't know */
354
355         return &user;
356 }
357
358 /************************************************************************
359  Routine to add an entry to the smbpasswd file.
360 *************************************************************************/
361
362 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
363 {
364   char *pfile = lp_smb_passwd_file();
365   struct smb_passwd *pwd = NULL;
366   FILE *fp = NULL;
367
368   int i;
369   int wr_len;
370
371   int fd;
372   int new_entry_length;
373   char *new_entry;
374   SMB_OFF_T offpos;
375   char *p;
376
377   /* Open the smbpassword file - for update. */
378   fp = startsmbfilepwent(True);
379
380   if (fp == NULL) {
381     DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
382     return False;
383   }
384
385   /*
386    * Scan the file, a line at a time and check if the name matches.
387    */
388
389   while ((pwd = getsmbfilepwent(fp)) != NULL) {
390     if (strequal(newpwd->smb_name, pwd->smb_name)) {
391       DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
392       endsmbfilepwent(fp);
393       return False;
394     }
395   }
396
397   /* Ok - entry doesn't exist. We can add it */
398
399   /* Create a new smb passwd entry and set it to the given password. */
400   /* 
401    * The add user write needs to be atomic - so get the fd from 
402    * the fp and do a raw write() call.
403    */
404   fd = fileno(fp);
405
406   if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
407     DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
408 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
409     endsmbfilepwent(fp);
410     return False;
411   }
412
413   new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
414
415   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
416     DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
417 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
418     endsmbfilepwent(fp);
419     return False;
420   }
421
422   slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
423   p = &new_entry[strlen(new_entry)];
424
425   if(newpwd->smb_passwd != NULL) {
426     for( i = 0; i < 16; i++) {
427       slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
428     }
429   } else {
430     i=0;
431     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
432       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
433     else
434       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
435   }
436   
437   p += 32;
438
439   *p++ = ':';
440
441   if(newpwd->smb_nt_passwd != NULL) {
442     for( i = 0; i < 16; i++) {
443       slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
444     }
445   } else {
446     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
447       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
448     else
449       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
450   }
451
452   p += 32;
453
454   *p++ = ':';
455
456   /* Add the account encoding and the last change time. */
457   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
458            pwdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), (uint32)time(NULL));
459
460 #ifdef DEBUG_PASSWORD
461   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d entry_len %d made line |%s|", 
462                              fd, new_entry_length, strlen(new_entry), new_entry));
463 #endif
464
465   if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry)) {
466     DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
467 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
468
469     /* Remove the entry we just wrote. */
470     if(sys_ftruncate(fd, offpos) == -1) {
471       DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
472 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
473              newpwd->smb_name, strerror(errno)));
474     }
475
476     endsmbfilepwent(fp);
477     free(new_entry);
478     return False;
479   }
480
481   free(new_entry);
482   endsmbfilepwent(fp);
483   return True;
484 }
485
486 /************************************************************************
487  Routine to search the smbpasswd file for an entry matching the username.
488  and then modify its password entry. We can't use the startsmbpwent()/
489  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
490  in the actual file to decide how much room we have to write data.
491  override = False, normal
492  override = True, override XXXXXXXX'd out password or NO PASS
493 ************************************************************************/
494
495 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
496 {
497   /* Static buffers we will return. */
498   static pstring  user_name;
499
500   char            linebuf[256];
501   char            readbuf[1024];
502   unsigned char   c;
503   fstring         ascii_p16;
504   fstring         encode_bits;
505   unsigned char  *p = NULL;
506   size_t            linebuf_len = 0;
507   FILE           *fp;
508   int             lockfd;
509   char           *pfile = lp_smb_passwd_file();
510   BOOL found_entry = False;
511   BOOL got_pass_last_set_time = False;
512
513   SMB_OFF_T pwd_seekpos = 0;
514
515   int i;
516   int wr_len;
517   int fd;
518
519   if (!*pfile) {
520     DEBUG(0, ("No SMB password file set\n"));
521     return False;
522   }
523   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
524
525   fp = fopen(pfile, "r+");
526
527   if (fp == NULL) {
528     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
529     return False;
530   }
531   /* Set a buffer to do more efficient reads */
532   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
533
534   lockfd = fileno(fp);
535
536   if (!file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
537     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
538     fclose(fp);
539     return False;
540   }
541
542   /* Make sure it is only rw by the owner */
543   chmod(pfile, 0600);
544
545   /* We have a write lock on the file. */
546   /*
547    * Scan the file, a line at a time and check if the name matches.
548    */
549   while (!feof(fp)) {
550     pwd_seekpos = sys_ftell(fp);
551
552     linebuf[0] = '\0';
553
554     fgets(linebuf, sizeof(linebuf), fp);
555     if (ferror(fp)) {
556       file_unlock(lockfd, &pw_file_lock_depth);
557       fclose(fp);
558       return False;
559     }
560
561     /*
562      * Check if the string is terminated with a newline - if not
563      * then we must keep reading and discard until we get one.
564      */
565     linebuf_len = strlen(linebuf);
566     if (linebuf[linebuf_len - 1] != '\n') {
567       c = '\0';
568       while (!ferror(fp) && !feof(fp)) {
569         c = fgetc(fp);
570         if (c == '\n') {
571           break;
572         }
573       }
574     } else {
575       linebuf[linebuf_len - 1] = '\0';
576     }
577
578 #ifdef DEBUG_PASSWORD
579     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
580 #endif
581
582     if ((linebuf[0] == 0) && feof(fp)) {
583       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
584       break;
585     }
586
587     /*
588      * The line we have should be of the form :-
589      * 
590      * username:uid:[32hex bytes]:....other flags presently
591      * ignored....
592      * 
593      * or,
594      *
595      * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
596      *
597      * if Windows NT compatible passwords are also present.
598      */
599
600     if (linebuf[0] == '#' || linebuf[0] == '\0') {
601       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
602       continue;
603     }
604
605     p = (unsigned char *) strchr(linebuf, ':');
606
607     if (p == NULL) {
608       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
609       continue;
610     }
611
612     /*
613      * As 256 is shorter than a pstring we don't need to check
614      * length here - if this ever changes....
615      */
616     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
617     user_name[PTR_DIFF(p, linebuf)] = '\0';
618     if (strequal(user_name, pwd->smb_name)) {
619       found_entry = True;
620       break;
621     }
622   }
623
624   if (!found_entry) {
625     file_unlock(lockfd, &pw_file_lock_depth);
626     fclose(fp);
627     return False;
628   }
629
630   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
631
632   /* User name matches - get uid and password */
633   p++;          /* Go past ':' */
634
635   if (!isdigit(*p)) {
636     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
637     file_unlock(lockfd, &pw_file_lock_depth);
638     fclose(fp);
639     return False;
640   }
641
642   while (*p && isdigit(*p))
643     p++;
644   if (*p != ':') {
645     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
646     file_unlock(lockfd, &pw_file_lock_depth);
647     fclose(fp);
648     return False;
649   }
650
651   /*
652    * Now get the password value - this should be 32 hex digits
653    * which are the ascii representations of a 16 byte string.
654    * Get two at a time and put them into the password.
655    */
656   p++;
657
658   /* Record exact password position */
659   pwd_seekpos += PTR_DIFF(p, linebuf);
660
661   if (!override && (*p == '*' || *p == 'X')) {
662     /* Password deliberately invalid - end here. */
663     DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
664     file_unlock(lockfd, &pw_file_lock_depth);
665     fclose(fp);
666     return False;
667   }
668
669   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
670     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
671     file_unlock(lockfd,&pw_file_lock_depth);
672     fclose(fp);
673     return (False);
674   }
675
676   if (p[32] != ':') {
677     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
678     file_unlock(lockfd,&pw_file_lock_depth);
679     fclose(fp);
680     return False;
681   }
682
683   if (!override && (*p == '*' || *p == 'X')) {
684     file_unlock(lockfd,&pw_file_lock_depth);
685     fclose(fp);
686     return False;
687   }
688
689   /* Now check if the NT compatible password is
690      available. */
691   p += 33; /* Move to the first character of the line after
692               the lanman password. */
693   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
694     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
695     file_unlock(lockfd,&pw_file_lock_depth);
696     fclose(fp);
697     return (False);
698   }
699
700   if (p[32] != ':') {
701     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
702     file_unlock(lockfd,&pw_file_lock_depth);
703     fclose(fp);
704     return False;
705   }
706
707   /* 
708    * Now check if the account info and the password last
709    * change time is available.
710    */
711   p += 33; /* Move to the first character of the line after
712               the NT password. */
713
714   /*
715    * If both NT and lanman passwords are provided - reset password
716    * not required flag.
717    */
718
719   if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) {
720     /* Reqiure password in the future (should ACB_DISABLED also be reset?) */
721     pwd->acct_ctrl &= ~(ACB_PWNOTREQ);
722   }
723
724   if (*p == '[') {
725
726     i = 0;
727     encode_bits[i++] = *p++;
728     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
729       encode_bits[i++] = *p++;
730
731     encode_bits[i++] = ']';
732     encode_bits[i++] = '\0';
733
734     if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
735       /*
736        * We are using a new format, space padded
737        * acct ctrl field. Encode the given acct ctrl
738        * bits into it.
739        */
740       fstrcpy(encode_bits, pwdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
741     } else {
742       /*
743        * If using the old format and the ACB_DISABLED or
744        * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
745        * here as we have no space to encode the change.
746        */
747       if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
748         pwd->smb_passwd = NULL;
749         pwd->smb_nt_passwd = NULL;
750       }
751     }
752
753     /* Go past the ']' */
754     if(linebuf_len > PTR_DIFF(p, linebuf))
755       p++;
756
757     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
758       p++;
759
760       /* We should be pointing at the LCT entry. */
761       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
762
763         p += 4;
764         for(i = 0; i < 8; i++) {
765           if(p[i] == '\0' || !isxdigit(p[i]))
766             break;
767         }
768         if(i == 8) {
769           /*
770            * p points at 8 characters of hex digits -
771            * read into a time_t as the seconds since
772            * 1970 that the password was last changed.
773            */
774           got_pass_last_set_time = True;
775         } /* i == 8 */
776       } /* *p && StrnCaseCmp() */
777     } /* p == ':' */
778   } /* p == '[' */
779
780   /* Entry is correctly formed. */
781
782   /* Create the 32 byte representation of the new p16 */
783   if(pwd->smb_passwd != NULL) {
784     for (i = 0; i < 16; i++) {
785       slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
786     }
787   } else {
788     if(pwd->acct_ctrl & ACB_PWNOTREQ)
789       fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
790     else
791       fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
792   }
793
794   /* Add on the NT md4 hash */
795   ascii_p16[32] = ':';
796   wr_len = 66;
797   if (pwd->smb_nt_passwd != NULL) {
798     for (i = 0; i < 16; i++) {
799       slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
800     }
801   } else {
802     if(pwd->acct_ctrl & ACB_PWNOTREQ)
803       fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
804     else
805       fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
806   }
807   ascii_p16[65] = ':';
808   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
809
810   /* Add on the account info bits and the time of last
811      password change. */
812
813   pwd->pass_last_set_time = time(NULL);
814
815   if(got_pass_last_set_time) {
816     slprintf(&ascii_p16[strlen(ascii_p16)], 
817              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
818              "%s:LCT-%08X:", 
819                      encode_bits, (uint32)pwd->pass_last_set_time );
820     wr_len = strlen(ascii_p16);
821   }
822
823 #ifdef DEBUG_PASSWORD
824   DEBUG(100,("mod_smbfilepwd_entry: "));
825   dump_data(100, ascii_p16, wr_len);
826 #endif
827
828   if(wr_len > sizeof(linebuf)) {
829     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
830     file_unlock(lockfd,&pw_file_lock_depth);
831     fclose(fp);
832     return (False);
833   }
834
835   /*
836    * Do an atomic write into the file at the position defined by
837    * seekpos.
838    */
839
840   /* The mod user write needs to be atomic - so get the fd from 
841      the fp and do a raw write() call.
842    */
843
844   fd = fileno(fp);
845
846   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
847     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
848     file_unlock(lockfd,&pw_file_lock_depth);
849     fclose(fp);
850     return False;
851   }
852
853   /* Sanity check - ensure the areas we are writing are framed by ':' */
854   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
855     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
856     file_unlock(lockfd,&pw_file_lock_depth);
857     fclose(fp);
858     return False;
859   }
860
861   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
862     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
863     file_unlock(lockfd,&pw_file_lock_depth);
864     fclose(fp);
865     return False;
866   }
867  
868   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
869     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
870     file_unlock(lockfd,&pw_file_lock_depth);
871     fclose(fp);
872     return False;
873   }
874
875   if (write(fd, ascii_p16, wr_len) != wr_len) {
876     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
877     file_unlock(lockfd,&pw_file_lock_depth);
878     fclose(fp);
879     return False;
880   }
881
882   file_unlock(lockfd,&pw_file_lock_depth);
883   fclose(fp);
884   return True;
885 }
886
887 /*
888  * Stub functions - implemented in terms of others.
889  */
890
891 static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
892 {
893         return mod_smbfilepwd_entry(pwdb_sam_to_smb(pwd), override);
894 }
895
896 static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
897 {
898         return add_smbfilepwd_entry(pwdb_sam_to_smb(newpwd));
899 }
900
901 static struct sam_disp_info *getsmbfiledispnam(char *name)
902 {
903         return pwdb_sam_to_dispinfo(getsam21pwnam(name));
904 }
905
906 static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
907 {
908         return pwdb_sam_to_dispinfo(getsam21pwrid(rid));
909 }
910
911 static struct sam_disp_info *getsmbfiledispent(void *vp)
912 {
913         return pwdb_sam_to_dispinfo(getsam21pwent(vp));
914 }
915
916 static struct passdb_ops file_ops = {
917   startsmbfilepwent,
918   endsmbfilepwent,
919   getsmbfilepwpos,
920   setsmbfilepwpos,
921   iterate_getsmbpwnam,          /* In passdb.c */
922   iterate_getsmbpwuid,          /* In passdb.c */
923   iterate_getsmbpwrid,          /* In passdb.c */
924   getsmbfilepwent,
925   add_smbfilepwd_entry,
926   mod_smbfilepwd_entry,
927   getsmbfile21pwent,
928   iterate_getsam21pwnam,
929   iterate_getsam21pwuid,
930   iterate_getsam21pwrid, 
931   add_smbfile21pwd_entry,
932   mod_smbfile21pwd_entry,
933   getsmbfiledispnam,
934   getsmbfiledisprid,
935   getsmbfiledispent
936 };
937
938 struct passdb_ops *file_initialise_password_db(void)
939 {    
940   return &file_ops;
941 }
942
943 #else
944  /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
945  void smbpass_dummy_function(void) { } /* stop some compilers complaining */
946 #endif /* USE_SMBPASS_DB */