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