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