updated the 3.0 branch from the head branch - ready for alpha18
[nivanova/samba-autobuild/.git] / source3 / passdb / pdb_smbpasswd.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell 1992-1998 
5  * Modified by Jeremy Allison 1995.
6  * Modified by Gerald (Jerry) Carter 2000-2001
7  * Modified by Andrew Bartlett 2002.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 2 of the License, or (at your option)
12  * any later version.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  * 
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc., 675
21  * Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "includes.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_PASSDB
28
29 /* 
30    smb_passwd is analogous to sam_passwd used everywhere
31    else.  However, smb_passwd is limited to the information
32    stored by an smbpasswd entry 
33  */
34  
35 struct smb_passwd
36 {
37         BOOL smb_userid_set;     /* this is actually the unix uid_t */
38         uint32 smb_userid;     /* this is actually the unix uid_t */
39         const char *smb_name;     /* username string */
40
41         const unsigned char *smb_passwd; /* Null if no password */
42         const unsigned char *smb_nt_passwd; /* Null if no password */
43
44         uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
45         time_t pass_last_set_time;    /* password last set time */
46 };
47
48 struct smbpasswd_privates
49 {
50         /* used for maintain locks on the smbpasswd file */
51         int     pw_file_lock_depth;
52         
53         /* Global File pointer */
54         FILE    *pw_file;
55         
56         /* formerly static variables */
57         struct smb_passwd pw_buf;
58         pstring  user_name;
59         unsigned char smbpwd[16];
60         unsigned char smbntpwd[16];
61
62         /* retrive-once info */
63         const char *smbpasswd_file;
64
65         BOOL permit_non_unix_accounts;
66
67         uint32 low_nua_userid; 
68         uint32 high_nua_userid; 
69
70 };
71
72 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
73
74 /***************************************************************
75  Lock an fd. Abandon after waitsecs seconds.
76 ****************************************************************/
77
78 static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
79 {
80   if (fd < 0)
81     return False;
82
83   if(*plock_depth == 0) {
84     if (!do_file_lock(fd, secs, type)) {
85       DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
86                  strerror(errno)));
87       return False;
88     }
89   }
90
91   (*plock_depth)++;
92
93   return True;
94 }
95
96 /***************************************************************
97  Unlock an fd. Abandon after waitsecs seconds.
98 ****************************************************************/
99
100 static BOOL pw_file_unlock(int fd, int *plock_depth)
101 {
102   BOOL ret=True;
103
104   if (fd == 0 || *plock_depth == 0) {
105           return True;
106   }
107
108   if(*plock_depth == 1)
109     ret = do_file_lock(fd, 5, F_UNLCK);
110
111   if (*plock_depth > 0)
112     (*plock_depth)--;
113
114   if(!ret)
115     DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
116                  strerror(errno)));
117   return ret;
118 }
119
120
121 /**************************************************************
122  Intialize a smb_passwd struct
123  *************************************************************/
124
125 static void pdb_init_smb(struct smb_passwd *user)
126 {
127         if (user == NULL) 
128                 return;
129         ZERO_STRUCTP (user);
130         
131         user->pass_last_set_time = (time_t)0;
132 }
133
134 /***************************************************************
135  Internal fn to enumerate the smbpasswd list. Returns a void pointer
136  to ensure no modification outside this module. Checks for atomic
137  rename of smbpasswd file on update or create once the lock has
138  been granted to prevent race conditions. JRA.
139 ****************************************************************/
140
141 static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
142 {
143   FILE *fp = NULL;
144   const char *open_mode = NULL;
145   int race_loop = 0;
146   int lock_type = F_RDLCK;
147
148   if (!*pfile) {
149     DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
150     return (NULL);
151   }
152
153   switch(type) {
154   case PWF_READ:
155     open_mode = "rb";
156     lock_type = F_RDLCK;
157     break;
158   case PWF_UPDATE:
159     open_mode = "r+b";
160     lock_type = F_WRLCK;
161     break;
162   case PWF_CREATE:
163     /*
164      * Ensure atomic file creation.
165      */
166     {
167       int i, fd = -1;
168
169       for(i = 0; i < 5; i++) {
170         if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
171           break;
172         sys_usleep(200); /* Spin, spin... */
173       }
174       if(fd == -1) {
175         DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
176         return NULL;
177       }
178       close(fd);
179       open_mode = "r+b";
180       lock_type = F_WRLCK;
181       break;
182     }
183   }
184                        
185   for(race_loop = 0; race_loop < 5; race_loop++) {
186     DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
187
188     if((fp = sys_fopen(pfile, open_mode)) == NULL) {
189       DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) ));
190       return NULL;
191     }
192
193     if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
194       DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
195       fclose(fp);
196       return NULL;
197     }
198
199     /*
200      * Only check for replacement races on update or create.
201      * For read we don't mind if the data is one record out of date.
202      */
203
204     if(type == PWF_READ) {
205       break;
206     } else {
207       SMB_STRUCT_STAT sbuf1, sbuf2;
208
209       /*
210        * Avoid the potential race condition between the open and the lock
211        * by doing a stat on the filename and an fstat on the fd. If the
212        * two inodes differ then someone did a rename between the open and
213        * the lock. Back off and try the open again. Only do this 5 times to
214        * prevent infinate loops. JRA.
215        */
216
217       if (sys_stat(pfile,&sbuf1) != 0) {
218         DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
219         pw_file_unlock(fileno(fp), lock_depth);
220         fclose(fp);
221         return NULL;
222       }
223
224       if (sys_fstat(fileno(fp),&sbuf2) != 0) {
225         DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
226         pw_file_unlock(fileno(fp), lock_depth);
227         fclose(fp);
228         return NULL;
229       }
230
231       if( sbuf1.st_ino == sbuf2.st_ino) {
232         /* No race. */
233         break;
234       }
235
236       /*
237        * Race occurred - back off and try again...
238        */
239
240       pw_file_unlock(fileno(fp), lock_depth);
241       fclose(fp);
242     }
243   }
244
245   if(race_loop == 5) {
246     DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
247     return NULL;
248   }
249
250   /* Set a buffer to do more efficient reads */
251   setvbuf(fp, (char *)NULL, _IOFBF, 1024);
252
253   /* Make sure it is only rw by the owner */
254   if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
255     DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
256 Error was %s\n.", pfile, strerror(errno) ));
257     pw_file_unlock(fileno(fp), lock_depth);
258     fclose(fp);
259     return NULL;
260   }
261
262   /* We have a lock on the file. */
263   return fp;
264 }
265
266 /***************************************************************
267  End enumeration of the smbpasswd list.
268 ****************************************************************/
269 static void endsmbfilepwent(FILE *fp, int *lock_depth)
270 {
271         if (!fp) {
272                 return;
273         }
274
275         pw_file_unlock(fileno(fp), lock_depth);
276         fclose(fp);
277         DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
278 }
279
280 /*************************************************************************
281  Routine to return the next entry in the smbpasswd list.
282  *************************************************************************/
283
284 static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
285 {
286   /* Static buffers we will return. */
287   struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
288   char  *user_name = smbpasswd_state->user_name;
289   unsigned char *smbpwd = smbpasswd_state->smbpwd;
290   unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
291   char            linebuf[256];
292   unsigned char   c;
293   unsigned char  *p;
294   long            uidval;
295   size_t            linebuf_len;
296
297   if(fp == NULL) {
298     DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
299     return NULL;
300   }
301
302   pdb_init_smb(pw_buf);
303
304   pw_buf->acct_ctrl = ACB_NORMAL;  
305
306   /*
307    * Scan the file, a line at a time and check if the name matches.
308    */
309   while (!feof(fp)) {
310     linebuf[0] = '\0';
311
312     fgets(linebuf, 256, fp);
313     if (ferror(fp)) {
314       return NULL;
315     }
316
317     /*
318      * Check if the string is terminated with a newline - if not
319      * then we must keep reading and discard until we get one.
320      */
321     if ((linebuf_len = strlen(linebuf)) == 0)
322                 continue;
323
324     if (linebuf[linebuf_len - 1] != '\n') {
325       c = '\0';
326       while (!ferror(fp) && !feof(fp)) {
327         c = fgetc(fp);
328         if (c == '\n')
329           break;
330       }
331     } else
332       linebuf[linebuf_len - 1] = '\0';
333
334 #ifdef DEBUG_PASSWORD
335     DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
336 #endif
337     if ((linebuf[0] == 0) && feof(fp)) {
338       DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
339       break;
340     }
341     /*
342      * The line we have should be of the form :-
343      * 
344      * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
345      * ignored....
346      * 
347      * or,
348      *
349      * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
350      *
351      * if Windows NT compatible passwords are also present.
352      * [Account type] is an ascii encoding of the type of account.
353      * LCT-(8 hex digits) is the time_t value of the last change time.
354      */
355
356     if (linebuf[0] == '#' || linebuf[0] == '\0') {
357       DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
358       continue;
359     }
360     p = (unsigned char *) strchr_m(linebuf, ':');
361     if (p == NULL) {
362       DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
363       continue;
364     }
365     /*
366      * As 256 is shorter than a pstring we don't need to check
367      * length here - if this ever changes....
368      */
369     SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
370
371     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
372     user_name[PTR_DIFF(p, linebuf)] = '\0';
373
374     /* Get smb uid. */
375
376     p++;                /* Go past ':' */
377
378     if(*p == '-') {
379       DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
380       continue;
381     }
382
383     if (!isdigit(*p)) {
384       DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
385       continue;
386     }
387
388     uidval = atoi((char *) p);
389
390     while (*p && isdigit(*p))
391       p++;
392
393     if (*p != ':') {
394       DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
395       continue;
396     }
397
398     pw_buf->smb_name = user_name;
399     pw_buf->smb_userid = uidval;
400
401     /*
402      * Now get the password value - this should be 32 hex digits
403      * which are the ascii representations of a 16 byte string.
404      * Get two at a time and put them into the password.
405      */
406
407     /* Skip the ':' */
408     p++;
409
410     if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
411       DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
412       continue;
413     }
414
415     if (p[32] != ':') {
416       DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
417       continue;
418     }
419
420     if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
421       pw_buf->smb_passwd = NULL;
422       pw_buf->acct_ctrl |= ACB_PWNOTREQ;
423     } else {
424             if (*p == '*' || *p == 'X') {
425                     /* NULL LM password */
426                     pw_buf->smb_passwd = NULL;
427                     DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
428             } else if (pdb_gethexpwd((char *)p, smbpwd)) {
429                     pw_buf->smb_passwd = smbpwd;
430             } else {
431                     pw_buf->smb_passwd = NULL;
432                     DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
433             }
434     }
435
436     /* 
437      * Now check if the NT compatible password is
438      * available.
439      */
440     pw_buf->smb_nt_passwd = NULL;
441
442     p += 33; /* Move to the first character of the line after
443                 the lanman password. */
444     if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
445       if (*p != '*' && *p != 'X') {
446         if(pdb_gethexpwd((char *)p,smbntpwd))
447           pw_buf->smb_nt_passwd = smbntpwd;
448       }
449       p += 33; /* Move to the first character of the line after
450                   the NT password. */
451     }
452
453     DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
454              user_name, uidval));
455
456     if (*p == '[')
457         {
458       unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
459       pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
460
461       /* Must have some account type set. */
462       if(pw_buf->acct_ctrl == 0)
463         pw_buf->acct_ctrl = ACB_NORMAL;
464
465       /* Now try and get the last change time. */
466       if(end_p)
467         p = end_p + 1;
468       if(*p == ':') {
469         p++;
470         if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
471           int i;
472           p += 4;
473           for(i = 0; i < 8; i++) {
474             if(p[i] == '\0' || !isxdigit(p[i]))
475               break;
476           }
477           if(i == 8) {
478             /*
479              * p points at 8 characters of hex digits - 
480              * read into a time_t as the seconds since
481              * 1970 that the password was last changed.
482              */
483             pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
484           }
485         }
486       }
487     } else {
488       /* 'Old' style file. Fake up based on user name. */
489       /*
490        * Currently trust accounts are kept in the same
491        * password file as 'normal accounts'. If this changes
492        * we will have to fix this code. JRA.
493        */
494       if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
495         pw_buf->acct_ctrl &= ~ACB_NORMAL;
496         pw_buf->acct_ctrl |= ACB_WSTRUST;
497       }
498     }
499
500     return pw_buf;
501   }
502
503   DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
504   return NULL;
505 }
506
507 /************************************************************************
508  Create a new smbpasswd entry - malloced space returned.
509 *************************************************************************/
510
511 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
512 {
513   int new_entry_length;
514   char *new_entry;
515   char *p;
516
517   new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
518
519   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
520     DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
521     return NULL;
522   }
523
524   slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
525
526   p = new_entry+strlen(new_entry);
527   
528   pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
529
530   p+=strlen(p); *p = ':'; p++;
531
532   pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
533
534   p+=strlen(p); *p = ':'; p++;
535
536   /* Add the account encoding and the last change time. */
537   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
538            pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
539            (uint32)newpwd->pass_last_set_time);
540
541   return new_entry;
542 }
543
544 /************************************************************************
545  Routine to add an entry to the smbpasswd file.
546 *************************************************************************/
547
548 static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
549 {
550   const char *pfile = smbpasswd_state->smbpasswd_file;
551   struct smb_passwd *pwd = NULL;
552   FILE *fp = NULL;
553   int wr_len;
554   int fd;
555   size_t new_entry_length;
556   char *new_entry;
557   SMB_OFF_T offpos;
558   uint32 max_found_uid = 0;
559  
560   /* Open the smbpassword file - for update. */
561   fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth));
562
563   if (fp == NULL && errno == ENOENT) {
564         /* Try again - create. */
565         fp = startsmbfilepwent(pfile, PWF_CREATE, &(smbpasswd_state->pw_file_lock_depth));
566   }
567
568   if (fp == NULL) {
569     DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
570     return False;
571   }
572
573   /*
574    * Scan the file, a line at a time and check if the name matches.
575    */
576
577   while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) 
578   {
579     if (strequal(newpwd->smb_name, pwd->smb_name)) 
580     {
581         DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
582         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
583         return False;
584     }
585     
586     /* Look for a free uid for use in non-unix accounts */
587     if (pwd->smb_userid > max_found_uid) {
588            max_found_uid = pwd->smb_userid;
589     }
590    }
591
592   /* Ok - entry doesn't exist. We can add it */
593
594   /* Account not in /etc/passwd hack!!! */
595   if (!newpwd->smb_userid_set) {
596           if (!smbpasswd_state->permit_non_unix_accounts) {
597                   DEBUG(0, ("add_smbfilepwd_entry: cannot add account %s without unix identity\n", newpwd->smb_name));
598                   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
599                   return False;
600           }
601
602           if (max_found_uid < smbpasswd_state->low_nua_userid) {
603                   newpwd->smb_userid = smbpasswd_state->low_nua_userid;
604                   newpwd->smb_userid_set = True;
605           } else if (max_found_uid >= smbpasswd_state->high_nua_userid) {
606                   DEBUG(0, ("add_smbfilepwd_entry: cannot add machine %s, no uids are free! \n", newpwd->smb_name));
607                   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
608                   return False;           
609           } else {
610                   newpwd->smb_userid = max_found_uid + 1;
611                   newpwd->smb_userid_set = True;
612          }
613   }
614
615
616   /* Create a new smb passwd entry and set it to the given password. */
617   /* 
618    * The add user write needs to be atomic - so get the fd from 
619    * the fp and do a raw write() call.
620    */
621   fd = fileno(fp);
622
623   if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) 
624   {
625         DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
626 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
627         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
628         return False;
629   }
630
631   if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) 
632   {
633         DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
634 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
635         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
636         return False;
637   }
638
639   new_entry_length = strlen(new_entry);
640
641 #ifdef DEBUG_PASSWORD
642   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
643                              fd, new_entry_length, new_entry));
644 #endif
645
646   if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) 
647   {
648         DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
649 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
650
651         /* Remove the entry we just wrote. */
652         if(sys_ftruncate(fd, offpos) == -1) 
653         {
654                 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
655 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
656                 newpwd->smb_name, strerror(errno)));
657         }
658
659         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
660         free(new_entry);
661         return False;
662   }
663
664   free(new_entry);
665   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
666   return True;
667 }
668
669 /************************************************************************
670  Routine to search the smbpasswd file for an entry matching the username.
671  and then modify its password entry. We can't use the startsmbpwent()/
672  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
673  in the actual file to decide how much room we have to write data.
674  override = False, normal
675  override = True, override XXXXXXXX'd out password or NO PASS
676 ************************************************************************/
677
678 static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
679 {
680   /* Static buffers we will return. */
681         pstring user_name;
682
683   char            linebuf[256];
684   char            readbuf[1024];
685   unsigned char   c;
686   fstring         ascii_p16;
687   fstring         encode_bits;
688   unsigned char  *p = NULL;
689   size_t            linebuf_len = 0;
690   FILE           *fp;
691   int             lockfd;
692   const char     *pfile = smbpasswd_state->smbpasswd_file;
693   BOOL found_entry = False;
694   BOOL got_pass_last_set_time = False;
695
696   SMB_OFF_T pwd_seekpos = 0;
697
698   int i;
699   int wr_len;
700   int fd;
701
702   if (!*pfile) {
703     DEBUG(0, ("No SMB password file set\n"));
704     return False;
705   }
706   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
707
708   fp = sys_fopen(pfile, "r+");
709
710   if (fp == NULL) {
711     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
712     return False;
713   }
714   /* Set a buffer to do more efficient reads */
715   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
716
717   lockfd = fileno(fp);
718
719   if (!pw_file_lock(lockfd, F_WRLCK, 5, &(smbpasswd_state->pw_file_lock_depth))) {
720     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
721     fclose(fp);
722     return False;
723   }
724
725   /* Make sure it is only rw by the owner */
726   chmod(pfile, 0600);
727
728   /* We have a write lock on the file. */
729   /*
730    * Scan the file, a line at a time and check if the name matches.
731    */
732   while (!feof(fp)) {
733     pwd_seekpos = sys_ftell(fp);
734
735     linebuf[0] = '\0';
736
737     fgets(linebuf, sizeof(linebuf), fp);
738     if (ferror(fp)) {
739       pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
740       fclose(fp);
741       return False;
742     }
743
744     /*
745      * Check if the string is terminated with a newline - if not
746      * then we must keep reading and discard until we get one.
747      */
748     linebuf_len = strlen(linebuf);
749     if (linebuf[linebuf_len - 1] != '\n') {
750       c = '\0';
751       while (!ferror(fp) && !feof(fp)) {
752         c = fgetc(fp);
753         if (c == '\n') {
754           break;
755         }
756       }
757     } else {
758       linebuf[linebuf_len - 1] = '\0';
759     }
760
761 #ifdef DEBUG_PASSWORD
762     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
763 #endif
764
765     if ((linebuf[0] == 0) && feof(fp)) {
766       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
767       break;
768     }
769
770     /*
771      * The line we have should be of the form :-
772      * 
773      * username:uid:[32hex bytes]:....other flags presently
774      * ignored....
775      * 
776      * or,
777      *
778      * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
779      *
780      * if Windows NT compatible passwords are also present.
781      */
782
783     if (linebuf[0] == '#' || linebuf[0] == '\0') {
784       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
785       continue;
786     }
787
788     p = (unsigned char *) strchr_m(linebuf, ':');
789
790     if (p == NULL) {
791       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
792       continue;
793     }
794
795     /*
796      * As 256 is shorter than a pstring we don't need to check
797      * length here - if this ever changes....
798      */
799
800     SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
801
802     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
803     user_name[PTR_DIFF(p, linebuf)] = '\0';
804     if (strequal(user_name, pwd->smb_name)) {
805       found_entry = True;
806       break;
807     }
808   }
809
810   if (!found_entry) {
811     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
812     fclose(fp);
813
814     DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
815               pwd->smb_name));
816     return False;
817   }
818
819   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
820
821   /* User name matches - get uid and password */
822   p++;          /* Go past ':' */
823
824   if (!isdigit(*p)) {
825     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
826     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
827     fclose(fp);
828     return False;
829   }
830
831   while (*p && isdigit(*p))
832     p++;
833   if (*p != ':') {
834     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
835     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
836     fclose(fp);
837     return False;
838   }
839
840   /*
841    * Now get the password value - this should be 32 hex digits
842    * which are the ascii representations of a 16 byte string.
843    * Get two at a time and put them into the password.
844    */
845   p++;
846
847   /* Record exact password position */
848   pwd_seekpos += PTR_DIFF(p, linebuf);
849
850   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
851     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
852     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
853     fclose(fp);
854     return (False);
855   }
856
857   if (p[32] != ':') {
858     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
859     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
860     fclose(fp);
861     return False;
862   }
863
864   /* Now check if the NT compatible password is
865      available. */
866   p += 33; /* Move to the first character of the line after
867               the lanman password. */
868   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
869     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
870     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
871     fclose(fp);
872     return (False);
873   }
874
875   if (p[32] != ':') {
876     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
877     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
878     fclose(fp);
879     return False;
880   }
881
882   /* 
883    * Now check if the account info and the password last
884    * change time is available.
885    */
886   p += 33; /* Move to the first character of the line after
887               the NT password. */
888
889   if (*p == '[') {
890
891     i = 0;
892     encode_bits[i++] = *p++;
893     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
894       encode_bits[i++] = *p++;
895
896     encode_bits[i++] = ']';
897     encode_bits[i++] = '\0';
898
899     if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
900       /*
901        * We are using a new format, space padded
902        * acct ctrl field. Encode the given acct ctrl
903        * bits into it.
904        */
905       fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
906     } else {
907             DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format.  This is no longer supported.!\n"));
908             DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
909             return False;
910     }
911
912     /* Go past the ']' */
913     if(linebuf_len > PTR_DIFF(p, linebuf))
914       p++;
915
916     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
917       p++;
918
919       /* We should be pointing at the LCT entry. */
920       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
921
922         p += 4;
923         for(i = 0; i < 8; i++) {
924           if(p[i] == '\0' || !isxdigit(p[i]))
925             break;
926         }
927         if(i == 8) {
928           /*
929            * p points at 8 characters of hex digits -
930            * read into a time_t as the seconds since
931            * 1970 that the password was last changed.
932            */
933           got_pass_last_set_time = True;
934         } /* i == 8 */
935       } /* *p && StrnCaseCmp() */
936     } /* p == ':' */
937   } /* p == '[' */
938
939   /* Entry is correctly formed. */
940
941   /* Create the 32 byte representation of the new p16 */
942   pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
943
944   /* Add on the NT md4 hash */
945   ascii_p16[32] = ':';
946   wr_len = 66;
947   pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
948   ascii_p16[65] = ':';
949   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
950
951   /* Add on the account info bits and the time of last
952      password change. */
953
954   if(got_pass_last_set_time) {
955     slprintf(&ascii_p16[strlen(ascii_p16)], 
956              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
957              "%s:LCT-%08X:", 
958                      encode_bits, (uint32)pwd->pass_last_set_time );
959     wr_len = strlen(ascii_p16);
960   }
961
962 #ifdef DEBUG_PASSWORD
963   DEBUG(100,("mod_smbfilepwd_entry: "));
964   dump_data(100, ascii_p16, wr_len);
965 #endif
966
967   if(wr_len > sizeof(linebuf)) {
968     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
969     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
970     fclose(fp);
971     return (False);
972   }
973
974   /*
975    * Do an atomic write into the file at the position defined by
976    * seekpos.
977    */
978
979   /* The mod user write needs to be atomic - so get the fd from 
980      the fp and do a raw write() call.
981    */
982
983   fd = fileno(fp);
984
985   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
986     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
987     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
988     fclose(fp);
989     return False;
990   }
991
992   /* Sanity check - ensure the areas we are writing are framed by ':' */
993   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
994     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
995     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
996     fclose(fp);
997     return False;
998   }
999
1000   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
1001     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1002     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1003     fclose(fp);
1004     return False;
1005   }
1006  
1007   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1008     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1009     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1010     fclose(fp);
1011     return False;
1012   }
1013
1014   if (write(fd, ascii_p16, wr_len) != wr_len) {
1015     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1016     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1017     fclose(fp);
1018     return False;
1019   }
1020
1021   pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1022   fclose(fp);
1023   return True;
1024 }
1025
1026 /************************************************************************
1027  Routine to delete an entry in the smbpasswd file by name.
1028 *************************************************************************/
1029
1030 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1031 {
1032         const char *pfile = smbpasswd_state->smbpasswd_file;
1033   pstring pfile2;
1034   struct smb_passwd *pwd = NULL;
1035   FILE *fp = NULL;
1036   FILE *fp_write = NULL;
1037   int pfile2_lockdepth = 0;
1038
1039   slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1040
1041   /*
1042    * Open the smbpassword file - for update. It needs to be update
1043    * as we need any other processes to wait until we have replaced
1044    * it.
1045    */
1046
1047   if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
1048     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1049     return False;
1050   }
1051
1052   /*
1053    * Create the replacement password file.
1054    */
1055   if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1056     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1057     endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1058     return False;
1059   }
1060
1061   /*
1062    * Scan the file, a line at a time and check if the name matches.
1063    */
1064
1065   while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1066     char *new_entry;
1067     size_t new_entry_length;
1068
1069     if (strequal(name, pwd->smb_name)) {
1070       DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1071       continue;
1072     }
1073
1074     /*
1075      * We need to copy the entry out into the second file.
1076      */
1077
1078     if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) 
1079     {
1080         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1081 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1082         unlink(pfile2);
1083         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1084         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1085         return False;
1086     }
1087
1088     new_entry_length = strlen(new_entry);
1089
1090     if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) 
1091     {
1092         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1093 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1094         unlink(pfile2);
1095         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1096         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1097         free(new_entry);
1098         return False;
1099     }
1100
1101     free(new_entry);
1102   }
1103
1104   /*
1105    * Ensure pfile2 is flushed before rename.
1106    */
1107
1108   if(fflush(fp_write) != 0) 
1109   {
1110         DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1111         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1112         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1113         return False;
1114   }
1115
1116   /*
1117    * Do an atomic rename - then release the locks.
1118    */
1119
1120   if(rename(pfile2,pfile) != 0) {
1121     unlink(pfile2);
1122   }
1123   
1124   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1125   endsmbfilepwent(fp_write,&pfile2_lockdepth);
1126   return True;
1127 }
1128
1129 /*********************************************************************
1130  Create a smb_passwd struct from a SAM_ACCOUNT.
1131  We will not allocate any new memory.  The smb_passwd struct
1132  should only stay around as long as the SAM_ACCOUNT does.
1133  ********************************************************************/
1134 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1135 {
1136         uid_t uid;
1137
1138         if (sampass == NULL) 
1139                 return False;
1140
1141         ZERO_STRUCTP(smb_pw);
1142  
1143         if (!IS_SAM_UNIX_USER(sampass)) {
1144                 smb_pw->smb_userid_set = False;
1145                 DEBUG(5,("build_smb_pass: storing user without a UNIX uid or gid. \n"));
1146         } else {
1147                 uint32 rid = pdb_get_user_rid(sampass);
1148                 smb_pw->smb_userid_set = True;
1149                 uid = pdb_get_uid(sampass);
1150
1151                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1152                 if (rid && uid != fallback_pdb_user_rid_to_uid(rid)) {
1153                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1154                         return False;
1155                 }
1156
1157                 smb_pw->smb_userid=uid;
1158         }
1159
1160         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1161
1162         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1163         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1164
1165         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1166         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1167
1168 #if 0
1169         /*
1170          * ifdef'out by JFM on 11/29/2001.
1171          * this assertion is no longer valid
1172          * and I don't understand the goal 
1173          * and doing the same thing with the group mapping code
1174          * is hairy !
1175          *
1176          * We just have the RID, in which SID is it valid ?
1177          * our domain SID ? well known SID ? local SID ?
1178          */
1179
1180         if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) {
1181                 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n"));
1182                 DEBUG(0,("build_sam_pass: %d %d %d. \n", *gid, pdb_group_rid_to_gid(pdb_get_group_rid(sampass)), pdb_get_group_rid(sampass)));
1183                 return False;
1184         }
1185 #endif
1186
1187         return True;
1188 }       
1189
1190 /*********************************************************************
1191  Create a SAM_ACCOUNT from a smb_passwd struct
1192  ********************************************************************/
1193 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1194                               SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1195 {
1196         struct passwd *pwfile;
1197         
1198         if (sam_pass==NULL) {
1199                 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1200                 return False;
1201         }
1202                 
1203         if ((smbpasswd_state->permit_non_unix_accounts) 
1204             && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) 
1205             && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) {
1206
1207                 pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid));
1208
1209                 /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. 
1210                    
1211                    This was down the bottom for machines, but it looks pretty good as
1212                    a general default for non-unix users. --abartlet 2002-01-08
1213                 */
1214                 pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS); 
1215                 pdb_set_username (sam_pass, pw_buf->smb_name);
1216                 pdb_set_domain (sam_pass, lp_workgroup());
1217         } else {
1218
1219                 pwfile = getpwnam_alloc(pw_buf->smb_name);
1220                 if (pwfile == NULL) {
1221                         DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid %u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1222                         return False;
1223                 }
1224
1225                 if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) {
1226                         return False;
1227                 }
1228                 
1229                 passwd_free(&pwfile);
1230         }
1231         
1232         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd);
1233         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd);                   
1234         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl);
1235         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time);
1236         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, True);
1237         
1238 #if 0   /* JERRY */
1239         /* the smbpasswd format doesn't have a must change time field, so
1240            we can't get this right. The best we can do is to set this to 
1241            some time in the future. 21 days seems as reasonable as any other value :) 
1242         */
1243         pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE);
1244 #endif
1245         return True;
1246 }
1247
1248 /*****************************************************************
1249  Functions to be implemented by the new passdb API 
1250  ****************************************************************/
1251 static BOOL smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
1252 {
1253         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1254         
1255         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1256                                                        update ? PWF_UPDATE : PWF_READ, 
1257                                                        &(smbpasswd_state->pw_file_lock_depth));
1258                                    
1259         /* did we fail?  Should we try to create it? */
1260         if (!smbpasswd_state->pw_file && update && errno == ENOENT) 
1261         {
1262                 FILE *fp;
1263                 /* slprintf(msg_str,msg_str_len-1,
1264                    "smbpasswd file did not exist - attempting to create it.\n"); */
1265                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1266                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1267                 if (fp) 
1268                 {
1269                         fprintf(fp, "# Samba SMB password file\n");
1270                         fclose(fp);
1271                 }
1272                 
1273                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1274                                                              update ? PWF_UPDATE : PWF_READ, 
1275                                                              &(smbpasswd_state->pw_file_lock_depth));
1276         }
1277         
1278         return (smbpasswd_state->pw_file != NULL);                 
1279 }
1280
1281 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1282 {
1283         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1284         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1285 }
1286  
1287 /*****************************************************************
1288  ****************************************************************/
1289 static BOOL smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1290 {
1291         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1292         struct smb_passwd *pw_buf=NULL;
1293         BOOL done = False;
1294         DEBUG(5,("pdb_getsampwent\n"));
1295
1296         if (user==NULL) {
1297                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1298 #if 0
1299                 smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1300 #endif
1301                 return False;
1302         }
1303
1304         while (!done)
1305         {
1306                 /* do we have an entry? */
1307                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1308                 if (pw_buf == NULL) 
1309                         return False;
1310
1311                 /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
1312                    We loop in case the user in the pdb does not exist in 
1313                    the local system password file */
1314                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1315                         done = True;
1316         }
1317
1318         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1319
1320         /* success */
1321         return True;
1322 }
1323
1324
1325 /****************************************************************
1326  Search smbpasswd file by iterating over the entries.  Do not
1327  call getpwnam() for unix account information until we have found
1328  the correct entry
1329  ***************************************************************/
1330 static BOOL smbpasswd_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const char *username)
1331 {
1332         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1333         struct smb_passwd *smb_pw;
1334         void *fp = NULL;
1335
1336         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1337
1338         /* startsmbfilepwent() is used here as we don't want to lookup
1339            the UNIX account in the local system password file until
1340            we have a match.  */
1341         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1342
1343         if (fp == NULL) {
1344                 DEBUG(0, ("unable to open passdb database.\n"));
1345                 return False;
1346         }
1347
1348         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1349                 /* do nothing....another loop */ ;
1350         
1351         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1352
1353
1354         /* did we locate the username in smbpasswd  */
1355         if (smb_pw == NULL)
1356                 return False;
1357         
1358         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1359
1360         if (!sam_acct) {
1361                 DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1362 #if 0
1363                 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1364 #endif
1365                 return False;
1366         }
1367                 
1368         /* now build the SAM_ACCOUNT */
1369         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1370                 return False;
1371
1372         /* success */
1373         return True;
1374 }
1375
1376 static BOOL smbpasswd_getsampwrid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct,uint32 rid)
1377 {
1378         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1379         struct smb_passwd *smb_pw;
1380         void *fp = NULL;
1381
1382         DEBUG(10, ("pdb_getsampwrid: search by rid: %d\n", rid));
1383
1384         /* Open the sam password file - not for update. */
1385         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1386
1387         if (fp == NULL) {
1388                 DEBUG(0, ("unable to open passdb database.\n"));
1389                 return False;
1390         }
1391
1392         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1393                 /* do nothing */ ;
1394
1395         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1396
1397
1398         /* did we locate the username in smbpasswd  */
1399         if (smb_pw == NULL)
1400                 return False;
1401         
1402         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1403                 
1404         if (!sam_acct) {
1405                 DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1406 #if 0
1407                 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1408 #endif
1409                 return False;
1410         }
1411
1412         /* now build the SAM_ACCOUNT */
1413         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1414                 return False;
1415
1416         /* success */
1417         return True;
1418 }
1419
1420 static BOOL smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, DOM_SID *sid)
1421 {
1422         uint32 rid;
1423         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1424                 return False;
1425         return smbpasswd_getsampwrid(my_methods, user, rid);
1426 }
1427
1428 static BOOL smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1429 {
1430         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1431         struct smb_passwd smb_pw;
1432         
1433         /* convert the SAM_ACCOUNT */
1434         if (!build_smb_pass(&smb_pw, sampass)) {
1435                 return False;
1436         }
1437         
1438         /* add the entry */
1439         if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1440                 return False;
1441         }
1442         
1443         return True;
1444 }
1445
1446 static BOOL smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1447 {
1448         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1449         struct smb_passwd smb_pw;
1450         
1451         /* convert the SAM_ACCOUNT */
1452         if (!build_smb_pass(&smb_pw, sampass)) {
1453                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1454                 return False;
1455         }
1456         
1457         /* update the entry */
1458         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1459                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1460                 return False;
1461         }
1462         
1463         return True;
1464 }
1465
1466 static BOOL smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1467 {
1468         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1469
1470         const char *username = pdb_get_username(sampass);
1471
1472         return del_smbfilepwd_entry(smbpasswd_state, username);
1473 }
1474
1475 static void free_private_data(void **vp) 
1476 {
1477         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1478         
1479         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1480         
1481         *privates = NULL;
1482         /* No need to free any further, as it is talloc()ed */
1483 }
1484
1485
1486 NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1487 {
1488         NTSTATUS nt_status;
1489         struct smbpasswd_privates *privates;
1490
1491         if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1492                 return nt_status;
1493         }
1494
1495         (*pdb_method)->name = "smbpasswd";
1496
1497         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1498         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1499         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1500         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1501         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1502         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1503         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1504         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1505
1506         /* Setup private data and free function */
1507
1508         privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
1509
1510         if (!privates) {
1511                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1512                 return NT_STATUS_NO_MEMORY;
1513         }
1514
1515         /* Store some config details */
1516
1517         if (location) {
1518                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1519         } else {
1520                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1521         }
1522         
1523         if (!privates->smbpasswd_file) {
1524                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1525                 return NT_STATUS_NO_MEMORY;
1526         }
1527
1528         (*pdb_method)->private_data = privates;
1529
1530         (*pdb_method)->free_private_data = free_private_data;
1531
1532         return NT_STATUS_OK;
1533 }
1534
1535 NTSTATUS pdb_init_smbpasswd_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1536 {
1537         NTSTATUS nt_status;
1538         struct smbpasswd_privates *privates;
1539
1540         if (!NT_STATUS_IS_OK(nt_status = pdb_init_smbpasswd(pdb_context, pdb_method, location))) {
1541                 return nt_status;
1542         }
1543
1544         (*pdb_method)->name = "smbpasswd_nua";
1545
1546         privates = (*pdb_method)->private_data;
1547         
1548         privates->permit_non_unix_accounts = True;
1549
1550         if (!lp_non_unix_account_range(&privates->low_nua_userid, &privates->high_nua_userid)) {
1551                 DEBUG(0, ("cannot use smbpasswd_nua without 'non unix account range' in smb.conf!\n"));
1552                 return NT_STATUS_UNSUCCESSFUL;
1553         }
1554
1555         return NT_STATUS_OK;
1556 }