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