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