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