Patch from ctrlsoft to use the pdb_sethexpwd function in smbpasswd - instead
[kai/samba.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 (*p == '*' || *p == 'X') {
411       /* Password deliberately invalid - end here. */
412       DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
413       pw_buf->smb_nt_passwd = NULL;
414       pw_buf->smb_passwd = NULL;
415       pw_buf->acct_ctrl |= ACB_DISABLED;
416       return pw_buf;
417     }
418
419     if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
420       DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
421       continue;
422     }
423
424     if (p[32] != ':') {
425       DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
426       continue;
427     }
428
429     if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
430       pw_buf->smb_passwd = NULL;
431       pw_buf->acct_ctrl |= ACB_PWNOTREQ;
432     } else {
433       if (!pdb_gethexpwd((char *)p, smbpwd)) {
434         DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
435         continue;
436       }
437       pw_buf->smb_passwd = smbpwd;
438     }
439
440     /* 
441      * Now check if the NT compatible password is
442      * available.
443      */
444     pw_buf->smb_nt_passwd = NULL;
445
446     p += 33; /* Move to the first character of the line after
447                 the lanman password. */
448     if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
449       if (*p != '*' && *p != 'X') {
450         if(pdb_gethexpwd((char *)p,smbntpwd))
451           pw_buf->smb_nt_passwd = smbntpwd;
452       }
453       p += 33; /* Move to the first character of the line after
454                   the NT password. */
455     }
456
457     DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
458              user_name, uidval));
459
460     if (*p == '[')
461         {
462       unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
463       pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
464
465       /* Must have some account type set. */
466       if(pw_buf->acct_ctrl == 0)
467         pw_buf->acct_ctrl = ACB_NORMAL;
468
469       /* Now try and get the last change time. */
470       if(end_p)
471         p = end_p + 1;
472       if(*p == ':') {
473         p++;
474         if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
475           int i;
476           p += 4;
477           for(i = 0; i < 8; i++) {
478             if(p[i] == '\0' || !isxdigit(p[i]))
479               break;
480           }
481           if(i == 8) {
482             /*
483              * p points at 8 characters of hex digits - 
484              * read into a time_t as the seconds since
485              * 1970 that the password was last changed.
486              */
487             pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
488           }
489         }
490       }
491     } else {
492       /* 'Old' style file. Fake up based on user name. */
493       /*
494        * Currently trust accounts are kept in the same
495        * password file as 'normal accounts'. If this changes
496        * we will have to fix this code. JRA.
497        */
498       if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
499         pw_buf->acct_ctrl &= ~ACB_NORMAL;
500         pw_buf->acct_ctrl |= ACB_WSTRUST;
501       }
502     }
503
504     return pw_buf;
505   }
506
507   DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
508   return NULL;
509 }
510
511 /************************************************************************
512  Create a new smbpasswd entry - malloced space returned.
513 *************************************************************************/
514
515 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
516 {
517   int new_entry_length;
518   char *new_entry;
519   char *p;
520
521   new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
522
523   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
524     DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
525     return NULL;
526   }
527
528   slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
529
530   p = new_entry+strlen(new_entry);
531   
532   pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
533
534   p+=strlen(p); *p = ':'; p++;
535
536   pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
537
538   p+=strlen(p); *p = ':'; p++;
539
540   /* Add the account encoding and the last change time. */
541   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
542            pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
543            (uint32)newpwd->pass_last_set_time);
544
545   return new_entry;
546 }
547
548 /************************************************************************
549  Routine to add an entry to the smbpasswd file.
550 *************************************************************************/
551
552 static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
553 {
554   const char *pfile = smbpasswd_state->smbpasswd_file;
555   struct smb_passwd *pwd = NULL;
556   FILE *fp = NULL;
557   int wr_len;
558   int fd;
559   size_t new_entry_length;
560   char *new_entry;
561   SMB_OFF_T offpos;
562   uint32 max_found_uid = 0;
563  
564   /* Open the smbpassword file - for update. */
565   fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth));
566
567   if (fp == NULL && errno == ENOENT) {
568         /* Try again - create. */
569         fp = startsmbfilepwent(pfile, PWF_CREATE, &(smbpasswd_state->pw_file_lock_depth));
570   }
571
572   if (fp == NULL) {
573     DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
574     return False;
575   }
576
577   /*
578    * Scan the file, a line at a time and check if the name matches.
579    */
580
581   while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) 
582   {
583     if (strequal(newpwd->smb_name, pwd->smb_name)) 
584     {
585         DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
586         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
587         return False;
588     }
589     
590     /* Look for a free uid for use in non-unix accounts */
591     if (pwd->smb_userid > max_found_uid) {
592            max_found_uid = pwd->smb_userid;
593     }
594    }
595
596   /* Ok - entry doesn't exist. We can add it */
597
598   /* Account not in /etc/passwd hack!!! */
599   if (!newpwd->smb_userid_set) {
600           if (!smbpasswd_state->permit_non_unix_accounts) {
601                   DEBUG(0, ("add_smbfilepwd_entry: cannot add account %s without unix identity\n", newpwd->smb_name));
602                   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
603                   return False;
604           }
605
606           if (max_found_uid < smbpasswd_state->low_nua_userid) {
607                   newpwd->smb_userid = smbpasswd_state->low_nua_userid;
608                   newpwd->smb_userid_set = True;
609           } else if (max_found_uid >= smbpasswd_state->high_nua_userid) {
610                   DEBUG(0, ("add_smbfilepwd_entry: cannot add machine %s, no uids are free! \n", newpwd->smb_name));
611                   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
612                   return False;           
613           } else {
614                   newpwd->smb_userid = max_found_uid + 1;
615                   newpwd->smb_userid_set = True;
616          }
617   }
618
619
620   /* Create a new smb passwd entry and set it to the given password. */
621   /* 
622    * The add user write needs to be atomic - so get the fd from 
623    * the fp and do a raw write() call.
624    */
625   fd = fileno(fp);
626
627   if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) 
628   {
629         DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
630 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
631         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
632         return False;
633   }
634
635   if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) 
636   {
637         DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
638 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
639         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
640         return False;
641   }
642
643   new_entry_length = strlen(new_entry);
644
645 #ifdef DEBUG_PASSWORD
646   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
647                              fd, new_entry_length, new_entry));
648 #endif
649
650   if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) 
651   {
652         DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
653 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
654
655         /* Remove the entry we just wrote. */
656         if(sys_ftruncate(fd, offpos) == -1) 
657         {
658                 DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
659 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
660                 newpwd->smb_name, strerror(errno)));
661         }
662
663         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
664         free(new_entry);
665         return False;
666   }
667
668   free(new_entry);
669   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
670   return True;
671 }
672
673 /************************************************************************
674  Routine to search the smbpasswd file for an entry matching the username.
675  and then modify its password entry. We can't use the startsmbpwent()/
676  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
677  in the actual file to decide how much room we have to write data.
678  override = False, normal
679  override = True, override XXXXXXXX'd out password or NO PASS
680 ************************************************************************/
681
682 static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
683 {
684   /* Static buffers we will return. */
685         pstring user_name;
686
687   char            linebuf[256];
688   char            readbuf[1024];
689   unsigned char   c;
690   fstring         ascii_p16;
691   fstring         encode_bits;
692   unsigned char  *p = NULL;
693   size_t            linebuf_len = 0;
694   FILE           *fp;
695   int             lockfd;
696   const char     *pfile = smbpasswd_state->smbpasswd_file;
697   BOOL found_entry = False;
698   BOOL got_pass_last_set_time = False;
699
700   SMB_OFF_T pwd_seekpos = 0;
701
702   int i;
703   int wr_len;
704   int fd;
705
706   if (!*pfile) {
707     DEBUG(0, ("No SMB password file set\n"));
708     return False;
709   }
710   DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
711
712   fp = sys_fopen(pfile, "r+");
713
714   if (fp == NULL) {
715     DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
716     return False;
717   }
718   /* Set a buffer to do more efficient reads */
719   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
720
721   lockfd = fileno(fp);
722
723   if (!pw_file_lock(lockfd, F_WRLCK, 5, &(smbpasswd_state->pw_file_lock_depth))) {
724     DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
725     fclose(fp);
726     return False;
727   }
728
729   /* Make sure it is only rw by the owner */
730   chmod(pfile, 0600);
731
732   /* We have a write lock on the file. */
733   /*
734    * Scan the file, a line at a time and check if the name matches.
735    */
736   while (!feof(fp)) {
737     pwd_seekpos = sys_ftell(fp);
738
739     linebuf[0] = '\0';
740
741     fgets(linebuf, sizeof(linebuf), fp);
742     if (ferror(fp)) {
743       pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
744       fclose(fp);
745       return False;
746     }
747
748     /*
749      * Check if the string is terminated with a newline - if not
750      * then we must keep reading and discard until we get one.
751      */
752     linebuf_len = strlen(linebuf);
753     if (linebuf[linebuf_len - 1] != '\n') {
754       c = '\0';
755       while (!ferror(fp) && !feof(fp)) {
756         c = fgetc(fp);
757         if (c == '\n') {
758           break;
759         }
760       }
761     } else {
762       linebuf[linebuf_len - 1] = '\0';
763     }
764
765 #ifdef DEBUG_PASSWORD
766     DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
767 #endif
768
769     if ((linebuf[0] == 0) && feof(fp)) {
770       DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
771       break;
772     }
773
774     /*
775      * The line we have should be of the form :-
776      * 
777      * username:uid:[32hex bytes]:....other flags presently
778      * ignored....
779      * 
780      * or,
781      *
782      * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
783      *
784      * if Windows NT compatible passwords are also present.
785      */
786
787     if (linebuf[0] == '#' || linebuf[0] == '\0') {
788       DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
789       continue;
790     }
791
792     p = (unsigned char *) strchr_m(linebuf, ':');
793
794     if (p == NULL) {
795       DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
796       continue;
797     }
798
799     /*
800      * As 256 is shorter than a pstring we don't need to check
801      * length here - if this ever changes....
802      */
803
804     SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
805
806     strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
807     user_name[PTR_DIFF(p, linebuf)] = '\0';
808     if (strequal(user_name, pwd->smb_name)) {
809       found_entry = True;
810       break;
811     }
812   }
813
814   if (!found_entry) {
815     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
816     fclose(fp);
817
818     DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
819               pwd->smb_name));
820     return False;
821   }
822
823   DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
824
825   /* User name matches - get uid and password */
826   p++;          /* Go past ':' */
827
828   if (!isdigit(*p)) {
829     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
830     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
831     fclose(fp);
832     return False;
833   }
834
835   while (*p && isdigit(*p))
836     p++;
837   if (*p != ':') {
838     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
839     pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
840     fclose(fp);
841     return False;
842   }
843
844   /*
845    * Now get the password value - this should be 32 hex digits
846    * which are the ascii representations of a 16 byte string.
847    * Get two at a time and put them into the password.
848    */
849   p++;
850
851   /* Record exact password position */
852   pwd_seekpos += PTR_DIFF(p, linebuf);
853
854   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
855     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
856     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
857     fclose(fp);
858     return (False);
859   }
860
861   if (p[32] != ':') {
862     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
863     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
864     fclose(fp);
865     return False;
866   }
867
868   /* Now check if the NT compatible password is
869      available. */
870   p += 33; /* Move to the first character of the line after
871               the lanman password. */
872   if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
873     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
874     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
875     fclose(fp);
876     return (False);
877   }
878
879   if (p[32] != ':') {
880     DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
881     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
882     fclose(fp);
883     return False;
884   }
885
886   /* 
887    * Now check if the account info and the password last
888    * change time is available.
889    */
890   p += 33; /* Move to the first character of the line after
891               the NT password. */
892
893   if (*p == '[') {
894
895     i = 0;
896     encode_bits[i++] = *p++;
897     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
898       encode_bits[i++] = *p++;
899
900     encode_bits[i++] = ']';
901     encode_bits[i++] = '\0';
902
903     if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
904       /*
905        * We are using a new format, space padded
906        * acct ctrl field. Encode the given acct ctrl
907        * bits into it.
908        */
909       fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
910     } else {
911             DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format.  This is no longer supported.!\n"));
912             DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
913             return False;
914     }
915
916     /* Go past the ']' */
917     if(linebuf_len > PTR_DIFF(p, linebuf))
918       p++;
919
920     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
921       p++;
922
923       /* We should be pointing at the LCT entry. */
924       if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
925
926         p += 4;
927         for(i = 0; i < 8; i++) {
928           if(p[i] == '\0' || !isxdigit(p[i]))
929             break;
930         }
931         if(i == 8) {
932           /*
933            * p points at 8 characters of hex digits -
934            * read into a time_t as the seconds since
935            * 1970 that the password was last changed.
936            */
937           got_pass_last_set_time = True;
938         } /* i == 8 */
939       } /* *p && StrnCaseCmp() */
940     } /* p == ':' */
941   } /* p == '[' */
942
943   /* Entry is correctly formed. */
944
945   /* Create the 32 byte representation of the new p16 */
946   pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
947
948   /* Add on the NT md4 hash */
949   ascii_p16[32] = ':';
950   wr_len = 66;
951   pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
952   ascii_p16[65] = ':';
953   ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
954
955   /* Add on the account info bits and the time of last
956      password change. */
957
958   if(got_pass_last_set_time) {
959     slprintf(&ascii_p16[strlen(ascii_p16)], 
960              sizeof(ascii_p16)-(strlen(ascii_p16)+1),
961              "%s:LCT-%08X:", 
962                      encode_bits, (uint32)pwd->pass_last_set_time );
963     wr_len = strlen(ascii_p16);
964   }
965
966 #ifdef DEBUG_PASSWORD
967   DEBUG(100,("mod_smbfilepwd_entry: "));
968   dump_data(100, ascii_p16, wr_len);
969 #endif
970
971   if(wr_len > sizeof(linebuf)) {
972     DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
973     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
974     fclose(fp);
975     return (False);
976   }
977
978   /*
979    * Do an atomic write into the file at the position defined by
980    * seekpos.
981    */
982
983   /* The mod user write needs to be atomic - so get the fd from 
984      the fp and do a raw write() call.
985    */
986
987   fd = fileno(fp);
988
989   if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
990     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
991     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
992     fclose(fp);
993     return False;
994   }
995
996   /* Sanity check - ensure the areas we are writing are framed by ':' */
997   if (read(fd, linebuf, wr_len+1) != wr_len+1) {
998     DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
999     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1000     fclose(fp);
1001     return False;
1002   }
1003
1004   if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))  {
1005     DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1006     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1007     fclose(fp);
1008     return False;
1009   }
1010  
1011   if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1012     DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1013     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1014     fclose(fp);
1015     return False;
1016   }
1017
1018   if (write(fd, ascii_p16, wr_len) != wr_len) {
1019     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1020     pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1021     fclose(fp);
1022     return False;
1023   }
1024
1025   pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
1026   fclose(fp);
1027   return True;
1028 }
1029
1030 /************************************************************************
1031  Routine to delete an entry in the smbpasswd file by name.
1032 *************************************************************************/
1033
1034 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1035 {
1036         const char *pfile = smbpasswd_state->smbpasswd_file;
1037   pstring pfile2;
1038   struct smb_passwd *pwd = NULL;
1039   FILE *fp = NULL;
1040   FILE *fp_write = NULL;
1041   int pfile2_lockdepth = 0;
1042
1043   slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1044
1045   /*
1046    * Open the smbpassword file - for update. It needs to be update
1047    * as we need any other processes to wait until we have replaced
1048    * it.
1049    */
1050
1051   if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
1052     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1053     return False;
1054   }
1055
1056   /*
1057    * Create the replacement password file.
1058    */
1059   if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1060     DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1061     endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1062     return False;
1063   }
1064
1065   /*
1066    * Scan the file, a line at a time and check if the name matches.
1067    */
1068
1069   while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1070     char *new_entry;
1071     size_t new_entry_length;
1072
1073     if (strequal(name, pwd->smb_name)) {
1074       DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1075       continue;
1076     }
1077
1078     /*
1079      * We need to copy the entry out into the second file.
1080      */
1081
1082     if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) 
1083     {
1084         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1085 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1086         unlink(pfile2);
1087         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1088         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1089         return False;
1090     }
1091
1092     new_entry_length = strlen(new_entry);
1093
1094     if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) 
1095     {
1096         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1097 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1098         unlink(pfile2);
1099         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1100         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1101         free(new_entry);
1102         return False;
1103     }
1104
1105     free(new_entry);
1106   }
1107
1108   /*
1109    * Ensure pfile2 is flushed before rename.
1110    */
1111
1112   if(fflush(fp_write) != 0) 
1113   {
1114         DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1115         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1116         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1117         return False;
1118   }
1119
1120   /*
1121    * Do an atomic rename - then release the locks.
1122    */
1123
1124   if(rename(pfile2,pfile) != 0) {
1125     unlink(pfile2);
1126   }
1127   
1128   endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1129   endsmbfilepwent(fp_write,&pfile2_lockdepth);
1130   return True;
1131 }
1132
1133 /*********************************************************************
1134  Create a smb_passwd struct from a SAM_ACCOUNT.
1135  We will not allocate any new memory.  The smb_passwd struct
1136  should only stay around as long as the SAM_ACCOUNT does.
1137  ********************************************************************/
1138 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1139 {
1140         uid_t uid;
1141
1142         if (sampass == NULL) 
1143                 return False;
1144
1145         ZERO_STRUCTP(smb_pw);
1146  
1147         if (!IS_SAM_UNIX_USER(sampass)) {
1148                 smb_pw->smb_userid_set = False;
1149                 DEBUG(5,("build_smb_pass: storing user without a UNIX uid or gid. \n"));
1150         } else {
1151                 uint32 rid = pdb_get_user_rid(sampass);
1152                 smb_pw->smb_userid_set = True;
1153                 uid = pdb_get_uid(sampass);
1154
1155                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1156                 if (rid && uid != fallback_pdb_user_rid_to_uid(rid)) {
1157                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1158                         return False;
1159                 }
1160
1161                 smb_pw->smb_userid=uid;
1162         }
1163
1164         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1165
1166         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1167         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1168
1169         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1170         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1171
1172 #if 0
1173         /*
1174          * ifdef'out by JFM on 11/29/2001.
1175          * this assertion is no longer valid
1176          * and I don't understand the goal 
1177          * and doing the same thing with the group mapping code
1178          * is hairy !
1179          *
1180          * We just have the RID, in which SID is it valid ?
1181          * our domain SID ? well known SID ? local SID ?
1182          */
1183
1184         if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) {
1185                 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n"));
1186                 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)));
1187                 return False;
1188         }
1189 #endif
1190
1191         return True;
1192 }       
1193
1194 /*********************************************************************
1195  Create a SAM_ACCOUNT from a smb_passwd struct
1196  ********************************************************************/
1197 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1198                               SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1199 {
1200         struct passwd *pwfile;
1201         
1202         if (sam_pass==NULL) {
1203                 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1204                 return False;
1205         }
1206                 
1207         if ((smbpasswd_state->permit_non_unix_accounts) 
1208             && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) 
1209             && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) {
1210
1211                 pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid));
1212
1213                 /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. 
1214                    
1215                    This was down the bottom for machines, but it looks pretty good as
1216                    a general default for non-unix users. --abartlet 2002-01-08
1217                 */
1218                 pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS); 
1219                 pdb_set_username (sam_pass, pw_buf->smb_name);
1220                 pdb_set_domain (sam_pass, lp_workgroup());
1221         } else {
1222
1223                 pwfile = getpwnam_alloc(pw_buf->smb_name);
1224                 if (pwfile == NULL) {
1225                         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));
1226                         return False;
1227                 }
1228
1229                 if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) {
1230                         return False;
1231                 }
1232                 
1233                 passwd_free(&pwfile);
1234         }
1235         
1236         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd);
1237         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd);                   
1238         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl);
1239         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time);
1240         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, True);
1241         
1242 #if 0   /* JERRY */
1243         /* the smbpasswd format doesn't have a must change time field, so
1244            we can't get this right. The best we can do is to set this to 
1245            some time in the future. 21 days seems as reasonable as any other value :) 
1246         */
1247         pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE);
1248 #endif
1249         return True;
1250 }
1251
1252 /*****************************************************************
1253  Functions to be implemented by the new passdb API 
1254  ****************************************************************/
1255 static BOOL smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
1256 {
1257         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1258         
1259         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1260                                                        update ? PWF_UPDATE : PWF_READ, 
1261                                                        &(smbpasswd_state->pw_file_lock_depth));
1262                                    
1263         /* did we fail?  Should we try to create it? */
1264         if (!smbpasswd_state->pw_file && update && errno == ENOENT) 
1265         {
1266                 FILE *fp;
1267                 /* slprintf(msg_str,msg_str_len-1,
1268                    "smbpasswd file did not exist - attempting to create it.\n"); */
1269                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1270                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1271                 if (fp) 
1272                 {
1273                         fprintf(fp, "# Samba SMB password file\n");
1274                         fclose(fp);
1275                 }
1276                 
1277                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1278                                                              update ? PWF_UPDATE : PWF_READ, 
1279                                                              &(smbpasswd_state->pw_file_lock_depth));
1280         }
1281         
1282         return (smbpasswd_state->pw_file != NULL);                 
1283 }
1284
1285 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1286 {
1287         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1288         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1289 }
1290  
1291 /*****************************************************************
1292  ****************************************************************/
1293 static BOOL smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1294 {
1295         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1296         struct smb_passwd *pw_buf=NULL;
1297         BOOL done = False;
1298         DEBUG(5,("pdb_getsampwent\n"));
1299
1300         if (user==NULL) {
1301                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1302 #if 0
1303                 smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1304 #endif
1305                 return False;
1306         }
1307
1308         while (!done)
1309         {
1310                 /* do we have an entry? */
1311                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1312                 if (pw_buf == NULL) 
1313                         return False;
1314
1315                 /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
1316                    We loop in case the user in the pdb does not exist in 
1317                    the local system password file */
1318                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1319                         done = True;
1320         }
1321
1322         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1323
1324         /* success */
1325         return True;
1326 }
1327
1328
1329 /****************************************************************
1330  Search smbpasswd file by iterating over the entries.  Do not
1331  call getpwnam() for unix account information until we have found
1332  the correct entry
1333  ***************************************************************/
1334 static BOOL smbpasswd_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const char *username)
1335 {
1336         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1337         struct smb_passwd *smb_pw;
1338         void *fp = NULL;
1339
1340         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1341
1342         /* startsmbfilepwent() is used here as we don't want to lookup
1343            the UNIX account in the local system password file until
1344            we have a match.  */
1345         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1346
1347         if (fp == NULL) {
1348                 DEBUG(0, ("unable to open passdb database.\n"));
1349                 return False;
1350         }
1351
1352         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1353                 /* do nothing....another loop */ ;
1354         
1355         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1356
1357
1358         /* did we locate the username in smbpasswd  */
1359         if (smb_pw == NULL)
1360                 return False;
1361         
1362         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1363
1364         if (!sam_acct) {
1365                 DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1366 #if 0
1367                 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1368 #endif
1369                 return False;
1370         }
1371                 
1372         /* now build the SAM_ACCOUNT */
1373         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1374                 return False;
1375
1376         /* success */
1377         return True;
1378 }
1379
1380 static BOOL smbpasswd_getsampwrid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct,uint32 rid)
1381 {
1382         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1383         struct smb_passwd *smb_pw;
1384         void *fp = NULL;
1385
1386         DEBUG(10, ("pdb_getsampwrid: search by rid: %d\n", rid));
1387
1388         /* Open the sam password file - not for update. */
1389         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1390
1391         if (fp == NULL) {
1392                 DEBUG(0, ("unable to open passdb database.\n"));
1393                 return False;
1394         }
1395
1396         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1397                 /* do nothing */ ;
1398
1399         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1400
1401
1402         /* did we locate the username in smbpasswd  */
1403         if (smb_pw == NULL)
1404                 return False;
1405         
1406         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1407                 
1408         if (!sam_acct) {
1409                 DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1410 #if 0
1411                 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1412 #endif
1413                 return False;
1414         }
1415
1416         /* now build the SAM_ACCOUNT */
1417         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1418                 return False;
1419
1420         /* success */
1421         return True;
1422 }
1423
1424 static BOOL smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, DOM_SID *sid)
1425 {
1426         uint32 rid;
1427         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1428                 return False;
1429         return smbpasswd_getsampwrid(my_methods, user, rid);
1430 }
1431
1432 static BOOL smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1433 {
1434         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1435         struct smb_passwd smb_pw;
1436         
1437         /* convert the SAM_ACCOUNT */
1438         if (!build_smb_pass(&smb_pw, sampass)) {
1439                 return False;
1440         }
1441         
1442         /* add the entry */
1443         if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1444                 return False;
1445         }
1446         
1447         return True;
1448 }
1449
1450 static BOOL smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1451 {
1452         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1453         struct smb_passwd smb_pw;
1454         
1455         /* convert the SAM_ACCOUNT */
1456         if (!build_smb_pass(&smb_pw, sampass)) {
1457                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1458                 return False;
1459         }
1460         
1461         /* update the entry */
1462         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1463                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1464                 return False;
1465         }
1466         
1467         return True;
1468 }
1469
1470 static BOOL smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1471 {
1472         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1473
1474         const char *username = pdb_get_username(sampass);
1475
1476         return del_smbfilepwd_entry(smbpasswd_state, username);
1477 }
1478
1479 static void free_private_data(void **vp) 
1480 {
1481         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1482         
1483         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1484         
1485         *privates = NULL;
1486         /* No need to free any further, as it is talloc()ed */
1487 }
1488
1489
1490 NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1491 {
1492         NTSTATUS nt_status;
1493         struct smbpasswd_privates *privates;
1494
1495         if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1496                 return nt_status;
1497         }
1498
1499         (*pdb_method)->name = "smbpasswd";
1500
1501         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1502         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1503         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1504         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1505         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1506         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1507         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1508         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1509
1510         /* Setup private data and free function */
1511
1512         privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
1513
1514         if (!privates) {
1515                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1516                 return NT_STATUS_NO_MEMORY;
1517         }
1518
1519         /* Store some config details */
1520
1521         if (location) {
1522                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1523         } else {
1524                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1525         }
1526         
1527         if (!privates->smbpasswd_file) {
1528                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1529                 return NT_STATUS_NO_MEMORY;
1530         }
1531
1532         (*pdb_method)->private_data = privates;
1533
1534         (*pdb_method)->free_private_data = free_private_data;
1535
1536         return NT_STATUS_OK;
1537 }
1538
1539 NTSTATUS pdb_init_smbpasswd_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1540 {
1541         NTSTATUS nt_status;
1542         struct smbpasswd_privates *privates;
1543
1544         if (!NT_STATUS_IS_OK(nt_status = pdb_init_smbpasswd(pdb_context, pdb_method, location))) {
1545                 return nt_status;
1546         }
1547
1548         (*pdb_method)->name = "smbpasswd_nua";
1549
1550         privates = (*pdb_method)->private_data;
1551         
1552         privates->permit_non_unix_accounts = True;
1553
1554         if (!lp_non_unix_account_range(&privates->low_nua_userid, &privates->high_nua_userid)) {
1555                 DEBUG(0, ("cannot use smbpasswd_nua without 'non unix account range' in smb.conf!\n"));
1556                 return NT_STATUS_UNSUCCESSFUL;
1557         }
1558
1559         return NT_STATUS_OK;
1560 }