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