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