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