r17246: BUG 3964: lower case username prior to getpwnam() call in smbpasswd
[metze/samba/wip.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 NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
588                                      struct smb_passwd *newpwd)
589 {
590         const char *pfile = smbpasswd_state->smbpasswd_file;
591         struct smb_passwd *pwd = NULL;
592         FILE *fp = NULL;
593         int wr_len;
594         int fd;
595         size_t new_entry_length;
596         char *new_entry;
597         SMB_OFF_T offpos;
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 map_nt_error_from_unix(errno);
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 NT_STATUS_USER_EXISTS;
621                 }
622         }
623
624         /* Ok - entry doesn't exist. We can add it */
625
626         /* Create a new smb passwd entry and set it to the given password. */
627         /* 
628          * The add user write needs to be atomic - so get the fd from 
629          * the fp and do a raw write() call.
630          */
631         fd = fileno(fp);
632
633         if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
634                 NTSTATUS result = map_nt_error_from_unix(errno);
635                 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
636 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
637                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
638                 return result;
639         }
640
641         if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
642                 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
643 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
644                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
645                 return NT_STATUS_NO_MEMORY;
646         }
647
648         new_entry_length = strlen(new_entry);
649
650 #ifdef DEBUG_PASSWORD
651         DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
652                         fd, (int)new_entry_length, new_entry));
653 #endif
654
655         if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
656                 NTSTATUS result = map_nt_error_from_unix(errno);
657                 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
658 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
659
660                 /* Remove the entry we just wrote. */
661                 if(sys_ftruncate(fd, offpos) == -1) {
662                         DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
663 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
664                                 newpwd->smb_name, strerror(errno)));
665                 }
666
667                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
668                 free(new_entry);
669                 return result;
670         }
671
672         free(new_entry);
673         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
674         return NT_STATUS_OK;
675 }
676
677 /************************************************************************
678  Routine to search the smbpasswd file for an entry matching the username.
679  and then modify its password entry. We can't use the startsmbpwent()/
680  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
681  in the actual file to decide how much room we have to write data.
682  override = False, normal
683  override = True, override XXXXXXXX'd out password or NO PASS
684 ************************************************************************/
685
686 static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
687 {
688         /* Static buffers we will return. */
689         pstring user_name;
690
691         char *status;
692         char linebuf[256];
693         char readbuf[1024];
694         int c;
695         fstring ascii_p16;
696         fstring encode_bits;
697         unsigned char *p = NULL;
698         size_t linebuf_len = 0;
699         FILE *fp;
700         int lockfd;
701         const char *pfile = smbpasswd_state->smbpasswd_file;
702         BOOL found_entry = False;
703         BOOL got_pass_last_set_time = False;
704
705         SMB_OFF_T pwd_seekpos = 0;
706
707         int i;
708         int wr_len;
709         int fd;
710
711         if (!*pfile) {
712                 DEBUG(0, ("No SMB password file set\n"));
713                 return False;
714         }
715         DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
716
717         fp = sys_fopen(pfile, "r+");
718
719         if (fp == NULL) {
720                 DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
721                 return False;
722         }
723         /* Set a buffer to do more efficient reads */
724         setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
725
726         lockfd = fileno(fp);
727
728         if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
729                 DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
730                 fclose(fp);
731                 return False;
732         }
733
734         /* Make sure it is only rw by the owner */
735         chmod(pfile, 0600);
736
737         /* We have a write lock on the file. */
738         /*
739          * Scan the file, a line at a time and check if the name matches.
740          */
741         status = linebuf;
742         while (status && !feof(fp)) {
743                 pwd_seekpos = sys_ftell(fp);
744
745                 linebuf[0] = '\0';
746
747                 status = fgets(linebuf, sizeof(linebuf), fp);
748                 if (status == NULL && ferror(fp)) {
749                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
750                         fclose(fp);
751                         return False;
752                 }
753
754                 /*
755                  * Check if the string is terminated with a newline - if not
756                  * then we must keep reading and discard until we get one.
757                  */
758                 linebuf_len = strlen(linebuf);
759                 if (linebuf[linebuf_len - 1] != '\n') {
760                         c = '\0';
761                         while (!ferror(fp) && !feof(fp)) {
762                                 c = fgetc(fp);
763                                 if (c == '\n') {
764                                         break;
765                                 }
766                         }
767                 } else {
768                         linebuf[linebuf_len - 1] = '\0';
769                 }
770
771 #ifdef DEBUG_PASSWORD
772                 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
773 #endif
774
775                 if ((linebuf[0] == 0) && feof(fp)) {
776                         DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
777                         break;
778                 }
779
780                 /*
781                  * The line we have should be of the form :-
782                  * 
783                  * username:uid:[32hex bytes]:....other flags presently
784                  * ignored....
785                  * 
786                  * or,
787                  *
788                  * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
789                  *
790                  * if Windows NT compatible passwords are also present.
791                  */
792
793                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
794                         DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
795                         continue;
796                 }
797
798                 p = (unsigned char *) strchr_m(linebuf, ':');
799
800                 if (p == NULL) {
801                         DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
802                         continue;
803                 }
804
805                 /*
806                  * As 256 is shorter than a pstring we don't need to check
807                  * length here - if this ever changes....
808                  */
809
810                 SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
811
812                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
813                 user_name[PTR_DIFF(p, linebuf)] = '\0';
814                 if (strequal(user_name, pwd->smb_name)) {
815                         found_entry = True;
816                         break;
817                 }
818         }
819
820         if (!found_entry) {
821                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
822                 fclose(fp);
823
824                 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
825                         pwd->smb_name));
826                 return False;
827         }
828
829         DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
830
831         /* User name matches - get uid and password */
832         p++; /* Go past ':' */
833
834         if (!isdigit(*p)) {
835                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
836                         pwd->smb_name));
837                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
838                 fclose(fp);
839                 return False;
840         }
841
842         while (*p && isdigit(*p)) {
843                 p++;
844         }
845         if (*p != ':') {
846                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
847                         pwd->smb_name));
848                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
849                 fclose(fp);
850                 return False;
851         }
852
853         /*
854          * Now get the password value - this should be 32 hex digits
855          * which are the ascii representations of a 16 byte string.
856          * Get two at a time and put them into the password.
857          */
858         p++;
859
860         /* Record exact password position */
861         pwd_seekpos += PTR_DIFF(p, linebuf);
862
863         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
864                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
865                         pwd->smb_name));
866                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
867                 fclose(fp);
868                 return (False);
869         }
870
871         if (p[32] != ':') {
872                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
873                         pwd->smb_name));
874                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
875                 fclose(fp);
876                 return False;
877         }
878
879         /* Now check if the NT compatible password is available. */
880         p += 33; /* Move to the first character of the line after the lanman password. */
881         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
882                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
883                         pwd->smb_name));
884                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
885                 fclose(fp);
886                 return (False);
887         }
888
889         if (p[32] != ':') {
890                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
891                         pwd->smb_name));
892                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
893                 fclose(fp);
894                 return False;
895         }
896
897         /* 
898          * Now check if the account info and the password last
899          * change time is available.
900          */
901         p += 33; /* Move to the first character of the line after the NT password. */
902
903         if (*p == '[') {
904                 i = 0;
905                 encode_bits[i++] = *p++;
906                 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
907                         encode_bits[i++] = *p++;
908                 }
909
910                 encode_bits[i++] = ']';
911                 encode_bits[i++] = '\0';
912
913                 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
914                         /*
915                          * We are using a new format, space padded
916                          * acct ctrl field. Encode the given acct ctrl
917                          * bits into it.
918                          */
919                         fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
920                 } else {
921                         DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format for user %s. \
922 This is no longer supported.!\n", pwd->smb_name));
923                         DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
924                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
925                         fclose(fp);
926                         return False;
927                 }
928
929                 /* Go past the ']' */
930                 if(linebuf_len > PTR_DIFF(p, linebuf)) {
931                         p++;
932                 }
933
934                 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
935                         p++;
936
937                         /* We should be pointing at the LCT entry. */
938                         if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
939                                 p += 4;
940                                 for(i = 0; i < 8; i++) {
941                                         if(p[i] == '\0' || !isxdigit(p[i])) {
942                                                 break;
943                                         }
944                                 }
945                                 if(i == 8) {
946                                         /*
947                                          * p points at 8 characters of hex digits -
948                                          * read into a time_t as the seconds since
949                                          * 1970 that the password was last changed.
950                                          */
951                                         got_pass_last_set_time = True;
952                                 } /* i == 8 */
953                         } /* *p && StrnCaseCmp() */
954                 } /* p == ':' */
955         } /* p == '[' */
956
957         /* Entry is correctly formed. */
958
959         /* Create the 32 byte representation of the new p16 */
960         pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
961
962         /* Add on the NT md4 hash */
963         ascii_p16[32] = ':';
964         wr_len = 66;
965         pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
966         ascii_p16[65] = ':';
967         ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
968
969         /* Add on the account info bits and the time of last password change. */
970         if(got_pass_last_set_time) {
971                 slprintf(&ascii_p16[strlen(ascii_p16)], 
972                         sizeof(ascii_p16)-(strlen(ascii_p16)+1),
973                         "%s:LCT-%08X:", 
974                         encode_bits, (uint32)pwd->pass_last_set_time );
975                 wr_len = strlen(ascii_p16);
976         }
977
978 #ifdef DEBUG_PASSWORD
979         DEBUG(100,("mod_smbfilepwd_entry: "));
980         dump_data(100, ascii_p16, wr_len);
981 #endif
982
983         if(wr_len > sizeof(linebuf)) {
984                 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
985                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
986                 fclose(fp);
987                 return (False);
988         }
989
990         /*
991          * Do an atomic write into the file at the position defined by
992          * seekpos.
993          */
994
995         /* The mod user write needs to be atomic - so get the fd from 
996                 the fp and do a raw write() call.
997          */
998
999         fd = fileno(fp);
1000
1001         if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1002                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1003                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1004                 fclose(fp);
1005                 return False;
1006         }
1007
1008         /* Sanity check - ensure the areas we are writing are framed by ':' */
1009         if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1010                 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1011                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1012                 fclose(fp);
1013                 return False;
1014         }
1015
1016         if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))    {
1017                 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1018                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1019                 fclose(fp);
1020                 return False;
1021         }
1022  
1023         if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1024                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1025                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1026                 fclose(fp);
1027                 return False;
1028         }
1029
1030         if (write(fd, ascii_p16, wr_len) != wr_len) {
1031                 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1032                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1033                 fclose(fp);
1034                 return False;
1035         }
1036
1037         pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1038         fclose(fp);
1039         return True;
1040 }
1041
1042 /************************************************************************
1043  Routine to delete an entry in the smbpasswd file by name.
1044 *************************************************************************/
1045
1046 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1047 {
1048         const char *pfile = smbpasswd_state->smbpasswd_file;
1049         pstring pfile2;
1050         struct smb_passwd *pwd = NULL;
1051         FILE *fp = NULL;
1052         FILE *fp_write = NULL;
1053         int pfile2_lockdepth = 0;
1054
1055         slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1056
1057         /*
1058          * Open the smbpassword file - for update. It needs to be update
1059          * as we need any other processes to wait until we have replaced
1060          * it.
1061          */
1062
1063         if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1064                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1065                 return False;
1066         }
1067
1068         /*
1069          * Create the replacement password file.
1070          */
1071         if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1072                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1073                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1074                 return False;
1075         }
1076
1077         /*
1078          * Scan the file, a line at a time and check if the name matches.
1079          */
1080
1081         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1082                 char *new_entry;
1083                 size_t new_entry_length;
1084
1085                 if (strequal(name, pwd->smb_name)) {
1086                         DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1087                         continue;
1088                 }
1089
1090                 /*
1091                  * We need to copy the entry out into the second file.
1092                  */
1093
1094                 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1095                         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1096 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1097                         unlink(pfile2);
1098                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1099                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1100                         return False;
1101                 }
1102
1103                 new_entry_length = strlen(new_entry);
1104
1105                 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1106                         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1107 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1108                         unlink(pfile2);
1109                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1110                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1111                         free(new_entry);
1112                         return False;
1113                 }
1114
1115                 free(new_entry);
1116         }
1117
1118         /*
1119          * Ensure pfile2 is flushed before rename.
1120          */
1121
1122         if(fflush(fp_write) != 0) {
1123                 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1124                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1125                 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1126                 return False;
1127         }
1128
1129         /*
1130          * Do an atomic rename - then release the locks.
1131          */
1132
1133         if(rename(pfile2,pfile) != 0) {
1134                 unlink(pfile2);
1135         }
1136   
1137         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1138         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1139         return True;
1140 }
1141
1142 /*********************************************************************
1143  Create a smb_passwd struct from a struct samu.
1144  We will not allocate any new memory.  The smb_passwd struct
1145  should only stay around as long as the struct samu does.
1146  ********************************************************************/
1147
1148 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
1149 {
1150         uint32 rid;
1151
1152         if (sampass == NULL) 
1153                 return False;
1154         ZERO_STRUCTP(smb_pw);
1155
1156         if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1157                 rid = pdb_get_user_rid(sampass);
1158                 
1159                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1160                 if (rid == DOMAIN_USER_RID_GUEST) {
1161                         struct passwd *passwd = getpwnam_alloc(NULL, lp_guestaccount());
1162                         if (!passwd) {
1163                                 DEBUG(0, ("Could not find guest account via getpwnam()! (%s)\n", lp_guestaccount()));
1164                                 return False;
1165                         }
1166                         smb_pw->smb_userid=passwd->pw_uid;
1167                         TALLOC_FREE(passwd);
1168                 } else if (algorithmic_pdb_rid_is_user(rid)) {
1169                         smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
1170                 } else {
1171                         DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
1172                         return False;
1173                 }
1174         }
1175
1176         smb_pw->smb_name=(const char*)pdb_get_username(sampass);
1177
1178         smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
1179         smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
1180
1181         smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
1182         smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
1183
1184         return True;
1185 }       
1186
1187 /*********************************************************************
1188  Create a struct samu from a smb_passwd struct
1189  ********************************************************************/
1190
1191 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1192                               struct samu *sam_pass, const struct smb_passwd *pw_buf)
1193 {
1194         struct passwd *pwfile;
1195         fstring unix_username;
1196         
1197         if ( !sam_pass ) {
1198                 DEBUG(5,("build_sam_account: struct samu is NULL\n"));
1199                 return False;
1200         }
1201
1202         /* verify the user account exists */
1203
1204         fstrcpy( unix_username, pw_buf->smb_name );
1205         strlower_m( unix_username );
1206                         
1207         if ( !(pwfile = getpwnam_alloc(NULL, unix_username )) ) {
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( samu_set_unix(sam_pass, pwfile )) )
1214                 return False;
1215                 
1216         TALLOC_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, uint32 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, struct samu *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
1280         DEBUG(5,("pdb_getsampwent\n"));
1281
1282         if ( !user ) {
1283                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1284                 return nt_status;
1285         }
1286
1287         while (!done) {
1288                 /* do we have an entry? */
1289                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1290                 if (pw_buf == NULL) 
1291                         return nt_status;
1292
1293                 /* build the struct samu entry from the smb_passwd struct. 
1294                    We loop in case the user in the pdb does not exist in 
1295                    the local system password file */
1296                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1297                         done = True;
1298         }
1299
1300         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1301
1302         /* success */
1303         return NT_STATUS_OK;
1304 }
1305
1306 /****************************************************************
1307  Search smbpasswd file by iterating over the entries.  Do not
1308  call getpwnam() for unix account information until we have found
1309  the correct entry
1310  ***************************************************************/
1311
1312 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1313                                   struct samu *sam_acct, const char *username)
1314 {
1315         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1316         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1317         struct smb_passwd *smb_pw;
1318         FILE *fp = NULL;
1319
1320         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1321
1322         /* startsmbfilepwent() is used here as we don't want to lookup
1323            the UNIX account in the local system password file until
1324            we have a match.  */
1325         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1326
1327         if (fp == NULL) {
1328                 DEBUG(0, ("Unable to open passdb database.\n"));
1329                 return nt_status;
1330         }
1331
1332         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1333                 /* do nothing....another loop */ ;
1334         
1335         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1336
1337
1338         /* did we locate the username in smbpasswd  */
1339         if (smb_pw == NULL)
1340                 return nt_status;
1341         
1342         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1343
1344         if (!sam_acct) {
1345                 DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
1346                 return nt_status;
1347         }
1348                 
1349         /* now build the struct samu */
1350         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1351                 return nt_status;
1352
1353         /* success */
1354         return NT_STATUS_OK;
1355 }
1356
1357 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const DOM_SID *sid)
1358 {
1359         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1360         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1361         struct smb_passwd *smb_pw;
1362         FILE *fp = NULL;
1363         fstring sid_str;
1364         uint32 rid;
1365         
1366         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1367
1368         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1369                 return NT_STATUS_UNSUCCESSFUL;
1370
1371         /* More special case 'guest account' hacks... */
1372         if (rid == DOMAIN_USER_RID_GUEST) {
1373                 const char *guest_account = lp_guestaccount();
1374                 if (!(guest_account && *guest_account)) {
1375                         DEBUG(1, ("Guest account not specfied!\n"));
1376                         return nt_status;
1377                 }
1378                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1379         }
1380
1381         /* Open the sam password file - not for update. */
1382         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1383
1384         if (fp == NULL) {
1385                 DEBUG(0, ("Unable to open passdb database.\n"));
1386                 return nt_status;
1387         }
1388
1389         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1390                 /* do nothing */ ;
1391
1392         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1393
1394
1395         /* did we locate the username in smbpasswd  */
1396         if (smb_pw == NULL)
1397                 return nt_status;
1398         
1399         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1400                 
1401         if (!sam_acct) {
1402                 DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
1403                 return nt_status;
1404         }
1405
1406         /* now build the struct samu */
1407         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1408                 return nt_status;
1409
1410         /* build_sam_account might change the SID on us, if the name was for the guest account */
1411         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1412                 fstring sid_string1, sid_string2;
1413                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1414                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1415                 return NT_STATUS_NO_SUCH_USER;
1416         }
1417
1418         /* success */
1419         return NT_STATUS_OK;
1420 }
1421
1422 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1423 {
1424         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1425         struct smb_passwd smb_pw;
1426         
1427         /* convert the struct samu */
1428         if (!build_smb_pass(&smb_pw, sampass)) {
1429                 return NT_STATUS_UNSUCCESSFUL;
1430         }
1431         
1432         /* add the entry */
1433         return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
1434 }
1435
1436 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
1437 {
1438         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1439         struct smb_passwd smb_pw;
1440         
1441         /* convert the struct samu */
1442         if (!build_smb_pass(&smb_pw, sampass)) {
1443                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1444                 return NT_STATUS_UNSUCCESSFUL;
1445         }
1446         
1447         /* update the entry */
1448         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1449                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1450                 return NT_STATUS_UNSUCCESSFUL;
1451         }
1452         
1453         return NT_STATUS_OK;
1454 }
1455
1456 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
1457 {
1458         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1459
1460         const char *username = pdb_get_username(sampass);
1461
1462         if (del_smbfilepwd_entry(smbpasswd_state, username))
1463                 return NT_STATUS_OK;
1464
1465         return NT_STATUS_UNSUCCESSFUL;
1466 }
1467
1468 static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods, 
1469                                               struct samu *old_acct,
1470                                               const char *newname)
1471 {
1472         pstring rename_script;
1473         struct samu *new_acct = NULL;
1474         BOOL interim_account = False;
1475         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
1476
1477         if (!*(lp_renameuser_script()))
1478                 goto done;
1479
1480         if ( !(new_acct = samu_new( NULL )) ) {
1481                 return NT_STATUS_NO_MEMORY;
1482         }
1483         
1484         if ( !pdb_copy_sam_account( new_acct, old_acct ) 
1485                 || !pdb_set_username(new_acct, newname, PDB_CHANGED)) 
1486         {
1487                 goto done;
1488         }
1489         
1490         ret = smbpasswd_add_sam_account(my_methods, new_acct);
1491         if (!NT_STATUS_IS_OK(ret))
1492                 goto done;
1493
1494         interim_account = True;
1495
1496         /* rename the posix user */
1497         pstrcpy(rename_script, lp_renameuser_script());
1498
1499         if (*rename_script) {
1500                 int rename_ret;
1501
1502                 string_sub2(rename_script, "%unew", newname, sizeof(pstring), 
1503                             True, False, True);
1504                 string_sub2(rename_script, "%uold", pdb_get_username(old_acct), 
1505                             sizeof(pstring), True, False, True);
1506
1507                 rename_ret = smbrun(rename_script, NULL);
1508
1509                 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
1510
1511                 if (rename_ret) 
1512                         goto done; 
1513         } else {
1514                 goto done;
1515         }
1516
1517         smbpasswd_delete_sam_account(my_methods, old_acct);
1518         interim_account = False;
1519
1520 done:   
1521         /* cleanup */
1522         if (interim_account)
1523                 smbpasswd_delete_sam_account(my_methods, new_acct);
1524
1525         if (new_acct)
1526                 TALLOC_FREE(new_acct);
1527         
1528         return (ret);   
1529 }
1530
1531 static BOOL smbpasswd_rid_algorithm(struct pdb_methods *methods)
1532 {
1533         return True;
1534 }
1535
1536 static void free_private_data(void **vp) 
1537 {
1538         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1539         
1540         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1541         
1542         *privates = NULL;
1543         /* No need to free any further, as it is talloc()ed */
1544 }
1545
1546 static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
1547 {
1548         NTSTATUS nt_status;
1549         struct smbpasswd_privates *privates;
1550
1551         if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
1552                 return nt_status;
1553         }
1554
1555         (*pdb_method)->name = "smbpasswd";
1556
1557         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1558         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1559         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1560         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1561         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1562         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1563         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1564         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1565         (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
1566
1567         (*pdb_method)->rid_algorithm = smbpasswd_rid_algorithm;
1568
1569         /* Setup private data and free function */
1570
1571         if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
1572                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1573                 return NT_STATUS_NO_MEMORY;
1574         }
1575
1576         /* Store some config details */
1577
1578         if (location) {
1579                 privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
1580         } else {
1581                 privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
1582         }
1583         
1584         if (!privates->smbpasswd_file) {
1585                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1586                 return NT_STATUS_NO_MEMORY;
1587         }
1588
1589         (*pdb_method)->private_data = privates;
1590
1591         (*pdb_method)->free_private_data = free_private_data;
1592
1593         return NT_STATUS_OK;
1594 }
1595
1596 NTSTATUS pdb_smbpasswd_init(void) 
1597 {
1598         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1599 }