c9e66a4715873edc71590b724439942f93f5bcaa
[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 (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         uint32 rid;
1138
1139         if (sampass == NULL) 
1140                 return False;
1141
1142         rid = pdb_get_user_rid(sampass);
1143
1144         /* If the user specified a RID, make sure its able to be both stored and retreived */
1145         if (rid && rid != DOMAIN_USER_RID_GUEST && uid != fallback_pdb_user_rid_to_uid(rid)) {
1146                 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1147                 return False;
1148         }
1149
1150         ZERO_STRUCTP(smb_pw);
1151
1152         smb_pw->smb_userid_set = True;
1153         smb_pw->smb_userid=uid;
1154
1155         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1156
1157         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1158         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1159
1160         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1161         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1162
1163 #if 0
1164         /*
1165          * ifdef'out by JFM on 11/29/2001.
1166          * this assertion is no longer valid
1167          * and I don't understand the goal 
1168          * and doing the same thing with the group mapping code
1169          * is hairy !
1170          *
1171          * We just have the RID, in which SID is it valid ?
1172          * our domain SID ? well known SID ? local SID ?
1173          */
1174
1175         if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) {
1176                 DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n"));
1177                 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)));
1178                 return False;
1179         }
1180 #endif
1181
1182         return True;
1183 }       
1184
1185 /*********************************************************************
1186  Create a SAM_ACCOUNT from a smb_passwd struct
1187  ********************************************************************/
1188 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1189                               SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1190 {
1191         struct passwd *pwfile;
1192         
1193         if (sam_pass==NULL) {
1194                 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1195                 return False;
1196         }
1197                 
1198         pwfile = getpwnam_alloc(pw_buf->smb_name);
1199         if (pwfile == NULL) {
1200                 if ((smbpasswd_state->permit_non_unix_accounts) 
1201                     && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) 
1202                     && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) {
1203
1204                         pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid), PDB_SET);
1205                         
1206                         /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. 
1207                            
1208                         This was down the bottom for machines, but it looks pretty good as
1209                         a general default for non-unix users. --abartlet 2002-01-08
1210                         */
1211                         pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS, PDB_SET); 
1212                         pdb_set_username (sam_pass, pw_buf->smb_name, PDB_SET);
1213                         pdb_set_domain (sam_pass, get_global_sam_name(), PDB_DEFAULT);
1214                         
1215                 } else {
1216                         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));
1217                         return False;
1218                 }
1219         } else {
1220                 if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) {
1221                         return False;
1222                 }
1223                 
1224                 passwd_free(&pwfile);
1225         }
1226         
1227         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1228         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
1229         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1230         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1231         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1232         
1233 #if 0   /* JERRY */
1234         /* the smbpasswd format doesn't have a must change time field, so
1235            we can't get this right. The best we can do is to set this to 
1236            some time in the future. 21 days seems as reasonable as any other value :) 
1237         */
1238         pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE, PDB_DEFAULT);
1239 #endif
1240         return True;
1241 }
1242
1243 /*****************************************************************
1244  Functions to be implemented by the new passdb API 
1245  ****************************************************************/
1246 static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
1247 {
1248         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1249         
1250         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1251                                                        update ? PWF_UPDATE : PWF_READ, 
1252                                                        &(smbpasswd_state->pw_file_lock_depth));
1253                                    
1254         /* did we fail?  Should we try to create it? */
1255         if (!smbpasswd_state->pw_file && update && errno == ENOENT) 
1256         {
1257                 FILE *fp;
1258                 /* slprintf(msg_str,msg_str_len-1,
1259                    "smbpasswd file did not exist - attempting to create it.\n"); */
1260                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1261                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1262                 if (fp) 
1263                 {
1264                         fprintf(fp, "# Samba SMB password file\n");
1265                         fclose(fp);
1266                 }
1267                 
1268                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1269                                                              update ? PWF_UPDATE : PWF_READ, 
1270                                                              &(smbpasswd_state->pw_file_lock_depth));
1271         }
1272         
1273         if (smbpasswd_state->pw_file != NULL)
1274                 return NT_STATUS_OK;
1275         else
1276                 return NT_STATUS_UNSUCCESSFUL;  
1277 }
1278
1279 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1280 {
1281         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1282         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1283 }
1284  
1285 /*****************************************************************
1286  ****************************************************************/
1287 static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1288 {
1289         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1290         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1291         struct smb_passwd *pw_buf=NULL;
1292         BOOL done = False;
1293         DEBUG(5,("pdb_getsampwent\n"));
1294
1295         if (user==NULL) {
1296                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1297 #if 0
1298                 smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1299 #endif
1300                 return nt_status;
1301         }
1302
1303         while (!done)
1304         {
1305                 /* do we have an entry? */
1306                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1307                 if (pw_buf == NULL) 
1308                         return nt_status;
1309
1310                 /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
1311                    We loop in case the user in the pdb does not exist in 
1312                    the local system password file */
1313                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1314                         done = True;
1315         }
1316
1317         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1318
1319         /* success */
1320         return NT_STATUS_OK;
1321 }
1322
1323
1324 /****************************************************************
1325  Search smbpasswd file by iterating over the entries.  Do not
1326  call getpwnam() for unix account information until we have found
1327  the correct entry
1328  ***************************************************************/
1329 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1330                                   SAM_ACCOUNT *sam_acct, const char *username)
1331 {
1332         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1333         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1334         struct smb_passwd *smb_pw;
1335         void *fp = NULL;
1336
1337         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1338
1339         /* startsmbfilepwent() is used here as we don't want to lookup
1340            the UNIX account in the local system password file until
1341            we have a match.  */
1342         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1343
1344         if (fp == NULL) {
1345                 DEBUG(0, ("unable to open passdb database.\n"));
1346                 return nt_status;
1347         }
1348
1349         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1350                 /* do nothing....another loop */ ;
1351         
1352         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1353
1354
1355         /* did we locate the username in smbpasswd  */
1356         if (smb_pw == NULL)
1357                 return nt_status;
1358         
1359         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1360
1361         if (!sam_acct) {
1362                 DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1363 #if 0
1364                 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1365 #endif
1366                 return nt_status;
1367         }
1368                 
1369         /* now build the SAM_ACCOUNT */
1370         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1371                 return nt_status;
1372
1373         /* success */
1374         return NT_STATUS_OK;
1375 }
1376
1377 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
1378 {
1379         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1380         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1381         struct smb_passwd *smb_pw;
1382         void *fp = NULL;
1383         fstring sid_str;
1384         uint32 rid;
1385         
1386         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1387
1388         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1389                 return NT_STATUS_UNSUCCESSFUL;
1390
1391         /* More special case 'guest account' hacks... */
1392         if (rid == DOMAIN_USER_RID_GUEST) {
1393                 const char *guest_account = lp_guestaccount();
1394                 if (!(guest_account && *guest_account)) {
1395                         DEBUG(1, ("Guest account not specfied!\n"));
1396                         return nt_status;
1397                 }
1398                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1399         }
1400
1401         /* Open the sam password file - not for update. */
1402         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1403
1404         if (fp == NULL) {
1405                 DEBUG(0, ("unable to open passdb database.\n"));
1406                 return nt_status;
1407         }
1408
1409         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1410                 /* do nothing */ ;
1411
1412         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1413
1414
1415         /* did we locate the username in smbpasswd  */
1416         if (smb_pw == NULL)
1417                 return nt_status;
1418         
1419         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1420                 
1421         if (!sam_acct) {
1422                 DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1423 #if 0
1424                 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1425 #endif
1426                 return nt_status;
1427         }
1428
1429         /* now build the SAM_ACCOUNT */
1430         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1431                 return nt_status;
1432
1433         /* build_sam_account might change the SID on us, if the name was for the guest account */
1434         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1435                 fstring sid_string1, sid_string2;
1436                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1437                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1438                 return NT_STATUS_NO_SUCH_USER;
1439         }
1440
1441         /* success */
1442         return NT_STATUS_OK;
1443 }
1444
1445 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1446 {
1447         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1448         struct smb_passwd smb_pw;
1449         
1450         /* convert the SAM_ACCOUNT */
1451         if (!build_smb_pass(&smb_pw, sampass)) {
1452                 return NT_STATUS_UNSUCCESSFUL;
1453         }
1454         
1455         /* add the entry */
1456         if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1457                 return NT_STATUS_UNSUCCESSFUL;
1458         }
1459         
1460         return NT_STATUS_OK;
1461 }
1462
1463 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1464 {
1465         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1466         struct smb_passwd smb_pw;
1467         
1468         /* convert the SAM_ACCOUNT */
1469         if (!build_smb_pass(&smb_pw, sampass)) {
1470                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1471                 return NT_STATUS_UNSUCCESSFUL;
1472         }
1473         
1474         /* update the entry */
1475         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1476                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1477                 return NT_STATUS_UNSUCCESSFUL;
1478         }
1479         
1480         return NT_STATUS_OK;
1481 }
1482
1483 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1484 {
1485         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1486
1487         const char *username = pdb_get_username(sampass);
1488
1489         if (del_smbfilepwd_entry(smbpasswd_state, username))
1490                 return NT_STATUS_OK;
1491
1492         return NT_STATUS_UNSUCCESSFUL;
1493 }
1494
1495 static void free_private_data(void **vp) 
1496 {
1497         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1498         
1499         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1500         
1501         *privates = NULL;
1502         /* No need to free any further, as it is talloc()ed */
1503 }
1504
1505 NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1506 {
1507         NTSTATUS nt_status;
1508         struct smbpasswd_privates *privates;
1509
1510         if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1511                 return nt_status;
1512         }
1513
1514         (*pdb_method)->name = "smbpasswd";
1515
1516         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1517         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1518         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1519         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1520         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1521         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1522         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1523         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1524
1525         /* Setup private data and free function */
1526
1527         privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
1528
1529         if (!privates) {
1530                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1531                 return NT_STATUS_NO_MEMORY;
1532         }
1533
1534         /* Store some config details */
1535
1536         if (location) {
1537                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1538         } else {
1539                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1540         }
1541         
1542         if (!privates->smbpasswd_file) {
1543                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1544                 return NT_STATUS_NO_MEMORY;
1545         }
1546
1547         (*pdb_method)->private_data = privates;
1548
1549         (*pdb_method)->free_private_data = free_private_data;
1550
1551         if (lp_idmap_uid(&privates->low_nua_userid, &privates->high_nua_userid)) {
1552                 DEBUG(0, ("idmap uid range defined, non unix accounts enabled\n"));
1553                 privates->permit_non_unix_accounts = True;
1554         }
1555
1556         return NT_STATUS_OK;
1557 }
1558
1559 int pdb_smbpasswd_init(void) 
1560 {
1561         smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1562         return True;
1563 }