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