980c54adf123169f537f00f001553957abe1a735
[ira/wip.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 extern int pw_file_lock_depth;
25 extern int DEBUGLEVEL;
26 extern pstring samlogon_user;
27 extern BOOL sam_logon_in_ssb;
28
29 static char s_readbuf[1024];
30
31 /***************************************************************
32  Start to enumerate the smbpasswd list. Returns a void pointer
33  to ensure no modification outside this module.
34 ****************************************************************/
35
36 static void *startsmbfilepwent(BOOL update)
37 {
38   FILE *fp = NULL;
39   char *pfile = lp_smb_passwd_file();
40
41   if (!*pfile) {
42     DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
43     return (NULL);
44   }
45   DEBUG(10, ("startsmbfilepwent: opening file %s\n", pfile));
46
47   fp = fopen(pfile, update ? "r+b" : "rb");
48
49   if (fp == NULL) {
50     DEBUG(0, ("startsmbfilepwent: unable to open file %s\n", pfile));
51     return NULL;
52   }
53
54   /* Set a buffer to do more efficient reads */
55   setvbuf(fp, s_readbuf, _IOFBF, sizeof(s_readbuf));
56
57   if (!pw_file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, &pw_file_lock_depth))
58   {
59     DEBUG(0, ("startsmbfilepwent: unable to lock file %s\n", pfile));
60     fclose(fp);
61     return NULL;
62   }
63
64   /* Make sure it is only rw by the owner */
65   chmod(pfile, 0600);
66
67   /* We have a lock on the file. */
68   return (void *)fp;
69 }
70
71 /***************************************************************
72  End enumeration of the smbpasswd list.
73 ****************************************************************/
74
75 static void endsmbfilepwent(void *vp)
76 {
77   FILE *fp = (FILE *)vp;
78
79   pw_file_unlock(fileno(fp), &pw_file_lock_depth);
80   fclose(fp);
81   DEBUG(7, ("endsmbfilepwent: closed password file.\n"));
82 }
83
84 /*************************************************************************
85  Routine to return the next entry in the smbpasswd list.
86  *************************************************************************/
87 static struct smb_passwd *getsmbfilepwent(void *vp)
88 {
89   /* Static buffers we will return. */
90   static struct smb_passwd pw_buf;
91   static pstring  user_name;
92   static unsigned char smbpwd[16];
93   static unsigned char smbntpwd[16];
94   FILE *fp = (FILE *)vp;
95   char            linebuf[256];
96   unsigned char   c;
97   unsigned char  *p;
98   long            uidval;
99   long            linebuf_len;
100
101   if(fp == NULL) {
102     DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
103     return NULL;
104   }
105
106   pdb_init_smb(&pw_buf);
107
108   pw_buf.acct_ctrl = ACB_NORMAL;  
109
110   /*
111    * Scan the file, a line at a time and check if the name matches.
112    */
113   while (!feof(fp)) {
114     linebuf[0] = '\0';
115
116     fgets(linebuf, 256, fp);
117     if (ferror(fp)) {
118       return NULL;
119     }
120
121     /*
122      * Check if the string is terminated with a newline - if not
123      * then we must keep reading and discard until we get one.
124      */
125     linebuf_len = strlen(linebuf);
126     if (linebuf[linebuf_len - 1] != '\n') {
127       c = '\0';
128       while (!ferror(fp) && !feof(fp)) {
129         c = fgetc(fp);
130         if (c == '\n')
131           break;
132       }
133     } else
134       linebuf[linebuf_len - 1] = '\0';
135
136 #ifdef DEBUG_PASSWORD
137     DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
138 #endif
139     if ((linebuf[0] == 0) && feof(fp)) {
140       DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
141       break;
142     }
143     /*
144      * The line we have should be of the form :-
145      * 
146      * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
147      * ignored....
148      * 
149      * or,
150      *
151      * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
152      *
153      * if Windows NT compatible passwords are also present.
154      * [Account type] is an ascii encoding of the type of account.
155      * LCT-(8 hex digits) is the time_t value of the last change time.
156      */
157
158     if (linebuf[0] == '#' || linebuf[0] == '\0') {
159       DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
160       continue;
161     }
162     p = (unsigned char *) strchr(linebuf, ':');
163     if (p == NULL) {
164       DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
165       continue;
166     }
167     /*
168      * As 256 is shorter than a pstring we don't need to check
169      * length here - if this ever changes....
170      */
171     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
172     user_name[PTR_DIFF(p, linebuf)] = '\0';
173
174     /* Get smb uid. */
175
176     p++;                /* Go past ':' */
177     if (!isdigit(*p)) {
178       DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
179       continue;
180     }
181
182     uidval = atoi((char *) p);
183
184     while (*p && isdigit(*p))
185       p++;
186
187     if (*p != ':') {
188       DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
189       continue;
190     }
191
192     pw_buf.smb_name = user_name;
193     pw_buf.smb_userid = uidval;
194
195     /*
196      * Now get the password value - this should be 32 hex digits
197      * which are the ascii representations of a 16 byte string.
198      * Get two at a time and put them into the password.
199      */
200
201     /* Skip the ':' */
202     p++;
203
204     if (*p == '*' || *p == 'X') {
205       /* Password deliberately invalid - end here. */
206       DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
207       pw_buf.smb_nt_passwd = NULL;
208       pw_buf.smb_passwd = NULL;
209       pw_buf.acct_ctrl |= ACB_DISABLED;
210       return &pw_buf;
211     }
212
213     if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
214       DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
215       continue;
216     }
217
218     if (p[32] != ':') {
219       DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
220       continue;
221     }
222
223     if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
224       pw_buf.smb_passwd = NULL;
225       pw_buf.acct_ctrl |= ACB_PWNOTREQ;
226     } else {
227       if (!pdb_gethexpwd((char *)p, (char *)smbpwd)) {
228         DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
229         continue;
230       }
231       pw_buf.smb_passwd = smbpwd;
232     }
233
234     /* 
235      * Now check if the NT compatible password is
236      * available.
237      */
238     pw_buf.smb_nt_passwd = NULL;
239
240     p += 33; /* Move to the first character of the line after
241                 the lanman password. */
242     if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
243       if (*p != '*' && *p != 'X') {
244         if(pdb_gethexpwd((char *)p,(char *)smbntpwd))
245           pw_buf.smb_nt_passwd = smbntpwd;
246       }
247       p += 33; /* Move to the first character of the line after
248                   the NT password. */
249     }
250
251     DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
252              user_name, uidval));
253
254     if (*p == '[')
255         {
256       pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
257
258       /* Must have some account type set. */
259       if(pw_buf.acct_ctrl == 0)
260         pw_buf.acct_ctrl = ACB_NORMAL;
261
262       /* Now try and get the last change time. */
263       if(*p == ']')
264         p++;
265       if(*p == ':') {
266         p++;
267         if(*p && StrnCaseCmp((char *)p, "LCT-", 4)) {
268           int i;
269           p += 4;
270           for(i = 0; i < 8; i++) {
271             if(p[i] == '\0' || !isxdigit(p[i]))
272               break;
273           }
274           if(i == 8) {
275             /*
276              * p points at 8 characters of hex digits - 
277              * read into a time_t as the seconds since
278              * 1970 that the password was last changed.
279              */
280             pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
281           }
282         }
283       }
284     } else {
285       /* 'Old' style file. Fake up based on user name. */
286       /*
287        * Currently trust accounts are kept in the same
288        * password file as 'normal accounts'. If this changes
289        * we will have to fix this code. JRA.
290        */
291       if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
292         pw_buf.acct_ctrl &= ~ACB_NORMAL;
293         pw_buf.acct_ctrl |= ACB_WSTRUST;
294       }
295     }
296
297     return &pw_buf;
298   }
299
300   DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
301   return NULL;
302 }
303
304 /*************************************************************************
305  Routine to return the next entry in the smbpasswd list.
306  this function is a nice, messy combination of reading:
307  - the smbpasswd file
308  - the unix password database
309  - smb.conf options (not done at present).
310  *************************************************************************/
311
312 static struct sam_passwd *getsmbfile21pwent(void *vp)
313 {
314         struct smb_passwd *pw_buf = getsmbfilepwent(vp);
315         static struct sam_passwd user;
316         struct passwd *pwfile;
317
318         static pstring full_name;
319         static pstring home_dir;
320         static pstring home_drive;
321         static pstring logon_script;
322         static pstring profile_path;
323         static pstring acct_desc;
324         static pstring workstations;
325         
326         if (pw_buf == NULL) return NULL;
327
328         pwfile = getpwnam(pw_buf->smb_name);
329         if (pwfile == NULL) return NULL;
330
331         pdb_init_sam(&user);
332
333         pstrcpy(samlogon_user, pw_buf->smb_name);
334
335         if (samlogon_user[strlen(samlogon_user)-1] != '$')
336         {
337                 /* XXXX hack to get standard_sub_basic() to use sam logon username */
338                 /* possibly a better way would be to do a become_user() call */
339                 sam_logon_in_ssb = True;
340
341                 user.smb_userid    = pw_buf->smb_userid;
342                 user.smb_grpid     = pwfile->pw_gid;
343
344                 user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
345                 user.group_rid = pdb_gid_to_group_rid(user.smb_grpid );
346
347                 pstrcpy(full_name    , pwfile->pw_gecos        );
348                 pstrcpy(logon_script , lp_logon_script       ());
349                 pstrcpy(profile_path , lp_logon_path         ());
350                 pstrcpy(home_drive   , lp_logon_drive        ());
351                 pstrcpy(home_dir     , lp_logon_home         ());
352                 pstrcpy(acct_desc    , "");
353                 pstrcpy(workstations , "");
354
355                 sam_logon_in_ssb = False;
356         }
357         else
358         {
359                 user.smb_userid    = pw_buf->smb_userid;
360                 user.smb_grpid     = pwfile->pw_gid;
361
362                 user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
363                 user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
364
365                 pstrcpy(full_name    , "");
366                 pstrcpy(logon_script , "");
367                 pstrcpy(profile_path , "");
368                 pstrcpy(home_drive   , "");
369                 pstrcpy(home_dir     , "");
370                 pstrcpy(acct_desc    , "");
371                 pstrcpy(workstations , "");
372         }
373
374         user.smb_name     = pw_buf->smb_name;
375         user.full_name    = full_name;
376         user.home_dir     = home_dir;
377         user.dir_drive    = home_drive;
378         user.logon_script = logon_script;
379         user.profile_path = profile_path;
380         user.acct_desc    = acct_desc;
381         user.workstations = workstations;
382
383         user.unknown_str = NULL; /* don't know, yet! */
384         user.munged_dial = NULL; /* "munged" dial-back telephone number */
385
386         user.smb_nt_passwd = pw_buf->smb_nt_passwd;
387         user.smb_passwd    = pw_buf->smb_passwd;
388                         
389         user.acct_ctrl = pw_buf->acct_ctrl;
390
391         user.unknown_3 = 0xffffff; /* don't know */
392         user.logon_divs = 168; /* hours per week */
393         user.hours_len = 21; /* 21 times 8 bits = 168 */
394         memset(user.hours, 0xff, user.hours_len); /* available at all hours */
395         user.unknown_5 = 0x00020000; /* don't know */
396         user.unknown_5 = 0x000004ec; /* don't know */
397
398         return &user;
399 }
400
401 /*************************************************************************
402  Return the current position in the smbpasswd list as an unsigned long.
403  This must be treated as an opaque token.
404 *************************************************************************/
405
406 static unsigned long getsmbfilepwpos(void *vp)
407 {
408   return (unsigned long)ftell((FILE *)vp);
409 }
410
411 /*************************************************************************
412  Set the current position in the smbpasswd list from unsigned long.
413  This must be treated as an opaque token.
414 *************************************************************************/
415
416 static BOOL setsmbfilepwpos(void *vp, unsigned long tok)
417 {
418   return !fseek((FILE *)vp, tok, SEEK_SET);
419 }
420
421 /************************************************************************
422  Routine to add an entry to the smbpasswd file.
423 *************************************************************************/
424
425 static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
426 {
427   char *pfile = lp_smb_passwd_file();
428   struct smb_passwd *pwd = NULL;
429   FILE *fp = NULL;
430
431   int i;
432   int wr_len;
433
434   int fd;
435   int new_entry_length;
436   char *new_entry;
437   long offpos;
438   char *p;
439
440   /* Open the smbpassword file - for update. */
441   fp = startsmbfilepwent(True);
442
443   if (fp == NULL) {
444     DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
445     return False;
446   }
447
448   /*
449    * Scan the file, a line at a time and check if the name matches.
450    */
451
452   while ((pwd = getsmbfilepwent(fp)) != NULL) {
453     if (strequal(newpwd->smb_name, pwd->smb_name)) {
454       DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
455       endsmbfilepwent(fp);
456       return False;
457     }
458   }
459
460   /* Ok - entry doesn't exist. We can add it */
461
462   /* Create a new smb passwd entry and set it to the given password. */
463   /* 
464    * The add user write needs to be atomic - so get the fd from 
465    * the fp and do a raw write() call.
466    */
467   fd = fileno(fp);
468
469   if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
470     DEBUG(0, ("add_smbfilepwd_entry(lseek): Failed to add entry for user %s to file %s. \
471 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
472     endsmbfilepwent(fp);
473     return False;
474   }
475
476   new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 5 + 1 + 13 + 2;
477
478   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
479     DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
480 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
481     endsmbfilepwent(fp);
482     return False;
483   }
484
485   slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
486   p = &new_entry[strlen(new_entry)];
487
488   if(newpwd->smb_passwd != NULL) {
489     for( i = 0; i < 16; i++) {
490       slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
491     }
492   } else {
493     i=0;
494     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
495       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
496     else
497       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
498   }
499   
500   p += 32;
501
502   *p++ = ':';
503
504   if(newpwd->smb_nt_passwd != NULL) {
505     for( i = 0; i < 16; i++) {
506       slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
507     }
508   } else {
509     if(newpwd->acct_ctrl & ACB_PWNOTREQ)
510       safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
511     else
512       safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
513   }
514
515   p += 32;
516
517   *p++ = ':';
518
519   /* Add the account encoding and the last change time. */
520   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
521                                   pdb_encode_acct_ctrl(newpwd->acct_ctrl),
522                      (uint32)time(NULL));
523
524 #ifdef DEBUG_PASSWORD
525   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d entry_len %d made line |%s|", 
526                              fd, new_entry_length, strlen(new_entry), new_entry));
527 #endif
528
529   if ((wr_len = write(fd, new_entry, strlen(new_entry))) != strlen(new_entry)) {
530     DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
531 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
532
533     /* Remove the entry we just wrote. */
534     if(ftruncate(fd, offpos) == -1) {
535       DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
536 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
537              newpwd->smb_name, strerror(errno)));
538     }
539
540     endsmbfilepwent(fp);
541     return False;
542   }
543
544   endsmbfilepwent(fp);
545   return True;
546 }
547
548 /************************************************************************
549  Routine to search the smbpasswd file for an entry matching the username.
550  and then modify its password entry. We can't use the startsmbpwent()/
551  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
552  in the actual file to decide how much room we have to write data.
553  override = False, normal
554  override = True, override XXXXXXXX'd out password or NO PASS
555 ************************************************************************/
556
557 static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
558 {
559   /* Static buffers we will return. */
560   static pstring  user_name;
561
562   char            linebuf[256];
563   char            readbuf[1024];
564   unsigned char   c;
565   fstring         ascii_p16;
566   fstring         encode_bits;
567   unsigned char  *p = NULL;
568   long            linebuf_len = 0;
569   FILE           *fp;
570   int             lockfd;
571   char           *pfile = lp_smb_passwd_file();
572   BOOL found_entry = False;
573   BOOL got_pass_last_set_time = False;
574
575   long pwd_seekpos = 0;
576
577   int i;
578   int wr_len;
579   int fd;
580
581   if (!*pfile) {
582     DEBUG(0, ("No SMB password file set\n"));
583     return False;
584   }
585   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
586
587   fp = fopen(pfile, "r+");
588
589   if (fp == NULL) {
590     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
591     return False;
592   }
593   /* Set a buffer to do more efficient reads */
594   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
595
596   lockfd = fileno(fp);
597
598   if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
599     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
600     fclose(fp);
601     return False;
602   }
603
604   /* Make sure it is only rw by the owner */
605   chmod(pfile, 0600);
606
607   /* We have a write lock on the file. */
608   /*
609    * Scan the file, a line at a time and check if the name matches.
610    */
611   while (!feof(fp)) {
612     pwd_seekpos = ftell(fp);
613
614     linebuf[0] = '\0';
615
616     fgets(linebuf, sizeof(linebuf), fp);
617     if (ferror(fp)) {
618       pw_file_unlock(lockfd, &pw_file_lock_depth);
619       fclose(fp);
620       return False;
621     }
622
623     /*
624      * Check if the string is terminated with a newline - if not
625      * then we must keep reading and discard until we get one.
626      */
627     linebuf_len = strlen(linebuf);
628     if (linebuf[linebuf_len - 1] != '\n') {
629       c = '\0';
630       while (!ferror(fp) && !feof(fp)) {
631         c = fgetc(fp);
632         if (c == '\n') {
633           break;
634         }
635       }
636     } else {
637       linebuf[linebuf_len - 1] = '\0';
638     }
639
640 #ifdef DEBUG_PASSWORD
641     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
642 #endif
643
644     if ((linebuf[0] == 0) && feof(fp)) {
645       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
646       break;
647     }
648
649     /*
650      * The line we have should be of the form :-
651      * 
652      * username:uid:[32hex bytes]:....other flags presently
653      * ignored....
654      * 
655      * or,
656      *
657      * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
658      *
659      * if Windows NT compatible passwords are also present.
660      */
661
662     if (linebuf[0] == '#' || linebuf[0] == '\0') {
663       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
664       continue;
665     }
666
667     p = (unsigned char *) strchr(linebuf, ':');
668
669     if (p == NULL) {
670       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
671       continue;
672     }
673
674     /*
675      * As 256 is shorter than a pstring we don't need to check
676      * length here - if this ever changes....
677      */
678     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
679     user_name[PTR_DIFF(p, linebuf)] = '\0';
680     if (strequal(user_name, pwd->smb_name)) {
681       found_entry = True;
682       break;
683     }
684   }
685
686   if (!found_entry) return False;
687
688   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
689
690   /* User name matches - get uid and password */
691   p++;          /* Go past ':' */
692
693   if (!isdigit(*p)) {
694     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
695     pw_file_unlock(lockfd, &pw_file_lock_depth);
696     fclose(fp);
697     return False;
698   }
699
700   while (*p && isdigit(*p))
701     p++;
702   if (*p != ':') {
703     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
704     pw_file_unlock(lockfd, &pw_file_lock_depth);
705     fclose(fp);
706     return False;
707   }
708
709   /*
710    * Now get the password value - this should be 32 hex digits
711    * which are the ascii representations of a 16 byte string.
712    * Get two at a time and put them into the password.
713    */
714   p++;
715
716   /* Record exact password position */
717   pwd_seekpos += PTR_DIFF(p, linebuf);
718
719   if (!override && (*p == '*' || *p == 'X')) {
720     /* Password deliberately invalid - end here. */
721     DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
722     pw_file_unlock(lockfd, &pw_file_lock_depth);
723     fclose(fp);
724     return False;
725   }
726
727   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
728     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
729     pw_file_unlock(lockfd,&pw_file_lock_depth);
730     fclose(fp);
731     return (False);
732   }
733
734   if (p[32] != ':') {
735     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
736     pw_file_unlock(lockfd,&pw_file_lock_depth);
737     fclose(fp);
738     return False;
739   }
740
741   if (!override && (*p == '*' || *p == 'X')) {
742     pw_file_unlock(lockfd,&pw_file_lock_depth);
743     fclose(fp);
744     return False;
745   }
746
747   /* Now check if the NT compatible password is
748      available. */
749   p += 33; /* Move to the first character of the line after
750               the lanman password. */
751   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
752     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
753     pw_file_unlock(lockfd,&pw_file_lock_depth);
754     fclose(fp);
755     return (False);
756   }
757
758   if (p[32] != ':') {
759     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
760     pw_file_unlock(lockfd,&pw_file_lock_depth);
761     fclose(fp);
762     return False;
763   }
764
765   /* 
766    * Now check if the account info and the password last
767    * change time is available.
768    */
769   p += 33; /* Move to the first character of the line after
770               the NT password. */
771
772   if (*p == '[') {
773
774     i = 0;
775     p++;
776     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
777       encode_bits[i++] = *p++;
778
779     encode_bits[i] = '\0';
780
781     /* Go past the ']' */
782     if(linebuf_len > PTR_DIFF(p, linebuf))
783       p++;
784
785     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
786       p++;
787
788       /* We should be pointing at the TLC entry. */
789       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && StrnCaseCmp((char *)p, "LCT-", 4)) {
790
791         p += 4;
792         for(i = 0; i < 8; i++) {
793           if(p[i] == '\0' || !isxdigit(p[i]))
794             break;
795         }
796         if(i == 8) {
797           /*
798            * p points at 8 characters of hex digits -
799            * read into a time_t as the seconds since
800            * 1970 that the password was last changed.
801            */
802           got_pass_last_set_time = True;
803         } /* i == 8 */
804       } /* *p && StrnCaseCmp() */
805     } /* p == ':' */
806   } /* p == '[' */
807
808   /* Entry is correctly formed. */
809
810   /*
811    * Do an atomic write into the file at the position defined by
812    * seekpos.
813    */
814
815   /* The mod user write needs to be atomic - so get the fd from 
816      the fp and do a raw write() call.
817    */
818
819   fd = fileno(fp);
820
821   if (lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
822     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
823     pw_file_unlock(lockfd,&pw_file_lock_depth);
824     fclose(fp);
825     return False;
826   }
827
828   /* Sanity check - ensure the character is a ':' */
829   if (read(fd, &c, 1) != 1) {
830     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
831     pw_file_unlock(lockfd,&pw_file_lock_depth);
832     fclose(fp);
833     return False;
834   }
835
836   if (c != ':') {
837     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
838     pw_file_unlock(lockfd,&pw_file_lock_depth);
839     fclose(fp);
840     return False;
841   }
842  
843   /* Create the 32 byte representation of the new p16 */
844   if(pwd->smb_passwd != NULL) {
845     for (i = 0; i < 16; i++) {
846       slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
847     }
848   } else {
849     if(pwd->acct_ctrl & ACB_PWNOTREQ)
850       fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
851     else
852       fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
853   }
854
855   /* Add on the NT md4 hash */
856   ascii_p16[32] = ':';
857   wr_len = 65;
858   if (pwd->smb_nt_passwd != NULL) {
859     for (i = 0; i < 16; i++) {
860       slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
861     }
862   } else {
863     if(pwd->acct_ctrl & ACB_PWNOTREQ)
864       fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
865     else
866       fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
867   }
868
869   /* Add on the account info bits and the time of last
870      password change. */
871
872   pwd->pass_last_set_time = time(NULL);
873
874   if(got_pass_last_set_time) {
875     slprintf(&ascii_p16[strlen(ascii_p16)], 
876              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
877              ":[%s]:TLC-%08X:", 
878                      encode_bits, (uint32)pwd->pass_last_set_time );
879     wr_len = strlen(ascii_p16);
880   }
881
882 #ifdef DEBUG_PASSWORD
883   DEBUG(100,("mod_smbfilepwd_entry: "));
884   dump_data(100, ascii_p16, wr_len);
885 #endif
886
887   if (write(fd, ascii_p16, wr_len) != wr_len) {
888     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
889     pw_file_unlock(lockfd,&pw_file_lock_depth);
890     fclose(fp);
891     return False;
892   }
893
894   pw_file_unlock(lockfd,&pw_file_lock_depth);
895   fclose(fp);
896   return True;
897 }
898
899 /*
900  * Stub functions - implemented in terms of others.
901  */
902
903 static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
904 {
905         return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override);
906 }
907
908 static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
909 {
910         return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd));
911 }
912
913 static struct sam_disp_info *getsmbfiledispnam(char *name)
914 {
915         return pdb_sam_to_dispinfo(getsam21pwnam(name));
916 }
917
918 static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
919 {
920         return pdb_sam_to_dispinfo(getsam21pwrid(rid));
921 }
922
923 static struct sam_disp_info *getsmbfiledispent(void *vp)
924 {
925         return pdb_sam_to_dispinfo(getsam21pwent(vp));
926 }
927
928 static struct passdb_ops file_ops = {
929   startsmbfilepwent,
930   endsmbfilepwent,
931   getsmbfilepwpos,
932   setsmbfilepwpos,
933   iterate_getsmbpwnam,          /* In passdb.c */
934   iterate_getsmbpwuid,          /* In passdb.c */
935   getsmbfilepwent,
936   add_smbfilepwd_entry,
937   mod_smbfilepwd_entry,
938   getsmbfile21pwent,
939   iterate_getsam21pwnam,
940   iterate_getsam21pwuid,
941   iterate_getsam21pwrid, 
942   add_smbfile21pwd_entry,
943   mod_smbfile21pwd_entry,
944   getsmbfiledispnam,
945   getsmbfiledisprid,
946   getsmbfiledispent
947 };
948
949 struct passdb_ops *file_initialize_password_db(void)
950 {    
951   return &file_ops;
952 }
953
954 #else
955  /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
956  void smbpass_dummy_function(void) { } /* stop some compilers complaining */
957 #endif /* USE_SMBPASS_DB */