2686f1d68fc47e1db662733e82a356f79ed7976c
[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 #ifdef DEBUG_PASSWORD
412         DEBUG(100,("mod_smbfilepwd_entry: password entries\n"));
413         dump_data(100, pwd->smb_passwd, 16);
414         dump_data(100, pwd->smb_nt_passwd, 16);
415 #endif
416   if (!*pfile) {
417     DEBUG(0, ("No SMB password file set\n"));
418     return False;
419   }
420   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
421
422   fp = sys_fopen(pfile, "r+");
423
424   if (fp == NULL) {
425     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
426     return False;
427   }
428   /* Set a buffer to do more efficient reads */
429   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
430
431   lockfd = fileno(fp);
432
433   if (!file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
434     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
435     fclose(fp);
436     return False;
437   }
438
439   /* Make sure it is only rw by the owner */
440   chmod(pfile, 0600);
441
442   /* We have a write lock on the file. */
443   /*
444    * Scan the file, a line at a time and check if the name matches.
445    */
446   while (!feof(fp)) {
447     pwd_seekpos = sys_ftell(fp);
448
449     linebuf[0] = '\0';
450
451     fgets(linebuf, sizeof(linebuf), fp);
452     if (ferror(fp)) {
453       file_unlock(lockfd, &pw_file_lock_depth);
454       fclose(fp);
455       return False;
456     }
457
458     /*
459      * Check if the string is terminated with a newline - if not
460      * then we must keep reading and discard until we get one.
461      */
462     linebuf_len = strlen(linebuf);
463     if (linebuf[linebuf_len - 1] != '\n') {
464       c = '\0';
465       while (!ferror(fp) && !feof(fp)) {
466         c = fgetc(fp);
467         if (c == '\n') {
468           break;
469         }
470       }
471     } else {
472       linebuf[linebuf_len - 1] = '\0';
473     }
474
475 #ifdef DEBUG_PASSWORD
476     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
477 #endif
478
479     if ((linebuf[0] == 0) && feof(fp)) {
480       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
481       break;
482     }
483
484     /*
485      * The line we have should be of the form :-
486      * 
487      * username:uid:[32hex bytes]:....other flags presently
488      * ignored....
489      * 
490      * or,
491      *
492      * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
493      *
494      * if Windows NT compatible passwords are also present.
495      */
496
497     if (linebuf[0] == '#' || linebuf[0] == '\0') {
498       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
499       continue;
500     }
501
502     p = (unsigned char *) strchr(linebuf, ':');
503
504     if (p == NULL) {
505       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
506       continue;
507     }
508
509     /*
510      * As 256 is shorter than a pstring we don't need to check
511      * length here - if this ever changes....
512      */
513     strncpy(unix_name, linebuf, PTR_DIFF(p, linebuf));
514     unix_name[PTR_DIFF(p, linebuf)] = '\0';
515     if (strequal(unix_name, pwd->unix_name)) {
516       found_entry = True;
517       break;
518     }
519   }
520
521   if (!found_entry) {
522     file_unlock(lockfd, &pw_file_lock_depth);
523     fclose(fp);
524     return False;
525   }
526
527   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
528
529   /* User name matches - get uid and password */
530   p++;          /* Go past ':' */
531
532   if (!isdigit(*p)) {
533     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
534     file_unlock(lockfd, &pw_file_lock_depth);
535     fclose(fp);
536     return False;
537   }
538
539   while (*p && isdigit(*p))
540     p++;
541   if (*p != ':') {
542     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
543     file_unlock(lockfd, &pw_file_lock_depth);
544     fclose(fp);
545     return False;
546   }
547
548   /*
549    * Now get the password value - this should be 32 hex digits
550    * which are the ascii representations of a 16 byte string.
551    * Get two at a time and put them into the password.
552    */
553   p++;
554
555   /* Record exact password position */
556   pwd_seekpos += PTR_DIFF(p, linebuf);
557
558   if (!override && (*p == '*' || *p == 'X')) {
559     /* Password deliberately invalid - end here. */
560     DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for unix user %s\n", unix_name));
561     file_unlock(lockfd, &pw_file_lock_depth);
562     fclose(fp);
563     return False;
564   }
565
566   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
567     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
568     file_unlock(lockfd,&pw_file_lock_depth);
569     fclose(fp);
570     return (False);
571   }
572
573   if (p[32] != ':') {
574     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
575     file_unlock(lockfd,&pw_file_lock_depth);
576     fclose(fp);
577     return False;
578   }
579
580   if (!override && (*p == '*' || *p == 'X')) {
581     file_unlock(lockfd,&pw_file_lock_depth);
582     fclose(fp);
583     return False;
584   }
585
586   /* Now check if the NT compatible password is
587      available. */
588   p += 33; /* Move to the first character of the line after
589               the lanman password. */
590   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
591     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
592     file_unlock(lockfd,&pw_file_lock_depth);
593     fclose(fp);
594     return (False);
595   }
596
597   if (p[32] != ':') {
598     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
599     file_unlock(lockfd,&pw_file_lock_depth);
600     fclose(fp);
601     return False;
602   }
603
604   /* 
605    * Now check if the account info and the password last
606    * change time is available.
607    */
608   p += 33; /* Move to the first character of the line after
609               the NT password. */
610
611   /*
612    * If both NT and lanman passwords are provided - reset password
613    * not required flag.
614    */
615
616   if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) {
617     /* Reqiure password in the future (should ACB_DISABLED also be reset?) */
618     pwd->acct_ctrl &= ~(ACB_PWNOTREQ);
619   }
620
621   if (*p == '[') {
622
623     i = 0;
624     encode_bits[i++] = *p++;
625     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
626       encode_bits[i++] = *p++;
627
628     encode_bits[i++] = ']';
629     encode_bits[i++] = '\0';
630
631     if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
632       /*
633        * We are using a new format, space padded
634        * acct ctrl field. Encode the given acct ctrl
635        * bits into it.
636        */
637       fstrcpy(encode_bits, pwdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
638     } else {
639       /*
640        * If using the old format and the ACB_DISABLED or
641        * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
642        * here as we have no space to encode the change.
643        */
644       if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
645         pwd->smb_passwd = NULL;
646         pwd->smb_nt_passwd = NULL;
647       }
648     }
649
650     /* Go past the ']' */
651     if(linebuf_len > PTR_DIFF(p, linebuf))
652       p++;
653
654     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
655       p++;
656
657       /* We should be pointing at the LCT entry. */
658       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
659
660         p += 4;
661         for(i = 0; i < 8; i++) {
662           if(p[i] == '\0' || !isxdigit(p[i]))
663             break;
664         }
665         if(i == 8) {
666           /*
667            * p points at 8 characters of hex digits -
668            * read into a time_t as the seconds since
669            * 1970 that the password was last changed.
670            */
671           got_pass_last_set_time = True;
672         } /* i == 8 */
673       } /* *p && StrnCaseCmp() */
674     } /* p == ':' */
675   } /* p == '[' */
676
677   /* Entry is correctly formed. */
678
679   /* Create the 32 byte representation of the new p16 */
680   if(pwd->smb_passwd != NULL) {
681     for (i = 0; i < 16; i++) {
682       slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
683     }
684   } else {
685     if(pwd->acct_ctrl & ACB_PWNOTREQ)
686       fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
687     else
688       fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
689   }
690
691   /* Add on the NT md4 hash */
692   ascii_p16[32] = ':';
693   wr_len = 66;
694   if (pwd->smb_nt_passwd != NULL) {
695     for (i = 0; i < 16; i++) {
696       slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
697     }
698   } else {
699     if(pwd->acct_ctrl & ACB_PWNOTREQ)
700       fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
701     else
702       fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
703   }
704   ascii_p16[65] = ':';
705   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
706
707   /* Add on the account info bits and the time of last
708      password change. */
709
710   pwd->pass_last_set_time = time(NULL);
711
712   if(got_pass_last_set_time) {
713     slprintf(&ascii_p16[strlen(ascii_p16)], 
714              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
715              "%s:LCT-%08X:", 
716                      encode_bits, (uint32)pwd->pass_last_set_time );
717     wr_len = strlen(ascii_p16);
718   }
719
720 #ifdef DEBUG_PASSWORD
721   DEBUG(100,("mod_smbfilepwd_entry: "));
722   dump_data(100, ascii_p16, wr_len);
723 #endif
724
725   if(wr_len > sizeof(linebuf)) {
726     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
727     file_unlock(lockfd,&pw_file_lock_depth);
728     fclose(fp);
729     return (False);
730   }
731
732   /*
733    * Do an atomic write into the file at the position defined by
734    * seekpos.
735    */
736
737   /* The mod user write needs to be atomic - so get the fd from 
738      the fp and do a raw write() call.
739    */
740
741   fd = fileno(fp);
742
743   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
744     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
745     file_unlock(lockfd,&pw_file_lock_depth);
746     fclose(fp);
747     return False;
748   }
749
750   /* Sanity check - ensure the areas we are writing are framed by ':' */
751   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
752     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
753     file_unlock(lockfd,&pw_file_lock_depth);
754     fclose(fp);
755     return False;
756   }
757
758   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
759     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
760     file_unlock(lockfd,&pw_file_lock_depth);
761     fclose(fp);
762     return False;
763   }
764  
765   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
766     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
767     file_unlock(lockfd,&pw_file_lock_depth);
768     fclose(fp);
769     return False;
770   }
771
772   if (write(fd, ascii_p16, wr_len) != wr_len) {
773     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
774     file_unlock(lockfd,&pw_file_lock_depth);
775     fclose(fp);
776     return False;
777   }
778
779   file_unlock(lockfd,&pw_file_lock_depth);
780   fclose(fp);
781   return True;
782 }
783
784 static struct smb_passdb_ops file_ops = {
785   startsmbfilepwent,
786   endsmbfilepwent,
787   getsmbfilepwpos,
788   setsmbfilepwpos,
789   iterate_getsmbpwnam,          /* In passdb.c */
790   iterate_getsmbpwuid,          /* In passdb.c */
791   getsmbfilepwent,
792   add_smbfilepwd_entry,
793   mod_smbfilepwd_entry
794 };
795
796 struct smb_passdb_ops *file_initialise_password_db(void)
797 {    
798   return &file_ops;
799 }
800
801 #else
802  /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
803  void smbpass_dummy_function(void) { } /* stop some compilers complaining */
804 #endif /* USE_SMBPASS_DB */