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