r4088: Get medieval on our ass about malloc.... :-). Take control of all our allocation
[bbaumbach/samba-autobuild/.git] / source3 / passdb / pdb_smbpasswd.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell       1992-1998 
5  * Modified by Jeremy Allison          1995.
6  * Modified by Gerald (Jerry) Carter   2000-2001,2003
7  * Modified by Andrew Bartlett         2002.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 2 of the License, or (at your option)
12  * any later version.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  * 
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc., 675
21  * Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "includes.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_PASSDB
28
29 /* 
30    smb_passwd is analogous to sam_passwd used everywhere
31    else.  However, smb_passwd is limited to the information
32    stored by an smbpasswd entry 
33  */
34  
35 struct smb_passwd
36 {
37         uint32 smb_userid;        /* this is actually the unix uid_t */
38         const char *smb_name;     /* username string */
39
40         const unsigned char *smb_passwd;    /* Null if no password */
41         const unsigned char *smb_nt_passwd; /* Null if no password */
42
43         uint16 acct_ctrl;             /* account info (ACB_xxxx bit-mask) */
44         time_t pass_last_set_time;    /* password last set time */
45 };
46
47 struct smbpasswd_privates
48 {
49         /* used for maintain locks on the smbpasswd file */
50         int     pw_file_lock_depth;
51         
52         /* Global File pointer */
53         FILE    *pw_file;
54         
55         /* formerly static variables */
56         struct smb_passwd pw_buf;
57         pstring  user_name;
58         unsigned char smbpwd[16];
59         unsigned char smbntpwd[16];
60
61         /* retrive-once info */
62         const char *smbpasswd_file;
63 };
64
65 enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
66
67 /***************************************************************
68  Lock an fd. Abandon after waitsecs seconds.
69 ****************************************************************/
70
71 static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
72 {
73         if (fd < 0) {
74                 return False;
75         }
76
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         unsigned char c;
317         unsigned char *p;
318         long uidval;
319         size_t linebuf_len;
320
321         if(fp == NULL) {
322                 DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
323                 return NULL;
324         }
325
326         pdb_init_smb(pw_buf);
327         pw_buf->acct_ctrl = ACB_NORMAL;  
328
329         /*
330          * Scan the file, a line at a time and check if the name matches.
331          */
332         while (!feof(fp)) {
333                 linebuf[0] = '\0';
334
335                 fgets(linebuf, 256, fp);
336                 if (ferror(fp)) {
337                         return NULL;
338                 }
339
340                 /*
341                  * Check if the string is terminated with a newline - if not
342                  * then we must keep reading and discard until we get one.
343                  */
344                 if ((linebuf_len = strlen(linebuf)) == 0) {
345                         continue;
346                 }
347
348                 if (linebuf[linebuf_len - 1] != '\n') {
349                         c = '\0';
350                         while (!ferror(fp) && !feof(fp)) {
351                                 c = fgetc(fp);
352                                 if (c == '\n') {
353                                         break;
354                                 }
355                         }
356                 } else {
357                         linebuf[linebuf_len - 1] = '\0';
358                 }
359
360 #ifdef DEBUG_PASSWORD
361                 DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
362 #endif
363                 if ((linebuf[0] == 0) && feof(fp)) {
364                         DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
365                         break;
366                 }
367
368                 /*
369                  * The line we have should be of the form :-
370                  * 
371                  * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
372                  * ignored....
373                  * 
374                  * or,
375                  *
376                  * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
377                  *
378                  * if Windows NT compatible passwords are also present.
379                  * [Account type] is an ascii encoding of the type of account.
380                  * LCT-(8 hex digits) is the time_t value of the last change time.
381                  */
382
383                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
384                         DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
385                         continue;
386                 }
387                 p = (unsigned char *) strchr_m(linebuf, ':');
388                 if (p == NULL) {
389                         DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
390                         continue;
391                 }
392
393                 /*
394                  * As 256 is shorter than a pstring we don't need to check
395                  * length here - if this ever changes....
396                  */
397                 SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
398
399                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
400                 user_name[PTR_DIFF(p, linebuf)] = '\0';
401
402                 /* Get smb uid. */
403
404                 p++; /* Go past ':' */
405
406                 if(*p == '-') {
407                         DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
408                         continue;
409                 }
410
411                 if (!isdigit(*p)) {
412                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
413                                 user_name));
414                         continue;
415                 }
416
417                 uidval = atoi((char *) p);
418
419                 while (*p && isdigit(*p)) {
420                         p++;
421                 }
422
423                 if (*p != ':') {
424                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
425                                 user_name));
426                         continue;
427                 }
428
429                 pw_buf->smb_name = user_name;
430                 pw_buf->smb_userid = uidval;
431
432                 /*
433                  * Now get the password value - this should be 32 hex digits
434                  * which are the ascii representations of a 16 byte string.
435                  * Get two at a time and put them into the password.
436                  */
437
438                 /* Skip the ':' */
439                 p++;
440
441                 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
442                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
443                                 user_name ));
444                         continue;
445                 }
446
447                 if (p[32] != ':') {
448                         DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
449                                 user_name));
450                         continue;
451                 }
452
453                 if (strnequal((char *) p, "NO PASSWORD", 11)) {
454                         pw_buf->smb_passwd = NULL;
455                         pw_buf->acct_ctrl |= ACB_PWNOTREQ;
456                 } else {
457                         if (*p == '*' || *p == 'X') {
458                                 /* NULL LM password */
459                                 pw_buf->smb_passwd = NULL;
460                                 DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
461                         } else if (pdb_gethexpwd((char *)p, smbpwd)) {
462                                 pw_buf->smb_passwd = smbpwd;
463                         } else {
464                                 pw_buf->smb_passwd = NULL;
465                                 DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
466 (non hex chars)\n", user_name));
467                         }
468                 }
469
470                 /* 
471                  * Now check if the NT compatible password is
472                  * available.
473                  */
474                 pw_buf->smb_nt_passwd = NULL;
475                 p += 33; /* Move to the first character of the line after the lanman password. */
476                 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
477                         if (*p != '*' && *p != 'X') {
478                                 if(pdb_gethexpwd((char *)p,smbntpwd)) {
479                                         pw_buf->smb_nt_passwd = smbntpwd;
480                                 }
481                         }
482                         p += 33; /* Move to the first character of the line after the NT password. */
483                 }
484
485                 DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
486                         user_name, uidval));
487
488                 if (*p == '[') {
489                         unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
490                         pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
491
492                         /* Must have some account type set. */
493                         if(pw_buf->acct_ctrl == 0) {
494                                 pw_buf->acct_ctrl = ACB_NORMAL;
495                         }
496
497                         /* Now try and get the last change time. */
498                         if(end_p) {
499                                 p = end_p + 1;
500                         }
501                         if(*p == ':') {
502                                 p++;
503                                 if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
504                                         int i;
505                                         p += 4;
506                                         for(i = 0; i < 8; i++) {
507                                                 if(p[i] == '\0' || !isxdigit(p[i])) {
508                                                         break;
509                                                 }
510                                         }
511                                         if(i == 8) {
512                                                 /*
513                                                  * p points at 8 characters of hex digits - 
514                                                  * read into a time_t as the seconds since
515                                                  * 1970 that the password was last changed.
516                                                  */
517                                                 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
518                                         }
519                                 }
520                         }
521                 } else {
522                         /* 'Old' style file. Fake up based on user name. */
523                         /*
524                          * Currently trust accounts are kept in the same
525                          * password file as 'normal accounts'. If this changes
526                          * we will have to fix this code. JRA.
527                          */
528                         if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
529                                 pw_buf->acct_ctrl &= ~ACB_NORMAL;
530                                 pw_buf->acct_ctrl |= ACB_WSTRUST;
531                         }
532                 }
533
534                 return pw_buf;
535         }
536
537         DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
538         return NULL;
539 }
540
541 /************************************************************************
542  Create a new smbpasswd entry - malloced space returned.
543 *************************************************************************/
544
545 static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
546 {
547         int new_entry_length;
548         char *new_entry;
549         char *p;
550
551         new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 
552                                 NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
553
554         if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
555                 DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
556                         newpwd->smb_name ));
557                 return NULL;
558         }
559
560         slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
561
562         p = new_entry+strlen(new_entry);
563         pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
564         p+=strlen(p);
565         *p = ':';
566         p++;
567
568         pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
569         p+=strlen(p);
570         *p = ':';
571         p++;
572
573         /* Add the account encoding and the last change time. */
574         slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
575                 pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
576                 (uint32)newpwd->pass_last_set_time);
577
578         return new_entry;
579 }
580
581 /************************************************************************
582  Routine to add an entry to the smbpasswd file.
583 *************************************************************************/
584
585 static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
586 {
587         const char *pfile = smbpasswd_state->smbpasswd_file;
588         struct smb_passwd *pwd = NULL;
589         FILE *fp = NULL;
590         int wr_len;
591         int fd;
592         size_t new_entry_length;
593         char *new_entry;
594         SMB_OFF_T offpos;
595         uint32 max_found_uid = 0;
596  
597         /* Open the smbpassword file - for update. */
598         fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
599
600         if (fp == NULL && errno == ENOENT) {
601                 /* Try again - create. */
602                 fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
603         }
604
605         if (fp == NULL) {
606                 DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
607                 return False;
608         }
609
610         /*
611          * Scan the file, a line at a time and check if the name matches.
612          */
613
614         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
615                 if (strequal(newpwd->smb_name, pwd->smb_name)) {
616                         DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
617                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
618                         return False;
619                 }
620     
621                 /* Look for a free uid for use in non-unix accounts */
622                 if (pwd->smb_userid > max_found_uid) {
623                         max_found_uid = pwd->smb_userid;
624                 }
625         }
626
627         /* Ok - entry doesn't exist. We can add it */
628
629         /* Create a new smb passwd entry and set it to the given password. */
630         /* 
631          * The add user write needs to be atomic - so get the fd from 
632          * the fp and do a raw write() call.
633          */
634         fd = fileno(fp);
635
636         if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
637                 DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
638 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
639                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
640                 return False;
641         }
642
643         if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
644                 DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
645 Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
646                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
647                 return False;
648         }
649
650         new_entry_length = strlen(new_entry);
651
652 #ifdef DEBUG_PASSWORD
653         DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
654                         fd, new_entry_length, new_entry));
655 #endif
656
657         if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
658                 DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
659 Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
660
661                 /* Remove the entry we just wrote. */
662                 if(sys_ftruncate(fd, offpos) == -1) {
663                         DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
664 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
665                                 newpwd->smb_name, strerror(errno)));
666                 }
667
668                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
669                 free(new_entry);
670                 return False;
671         }
672
673         free(new_entry);
674         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
675         return True;
676 }
677
678 /************************************************************************
679  Routine to search the smbpasswd file for an entry matching the username.
680  and then modify its password entry. We can't use the startsmbpwent()/
681  getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
682  in the actual file to decide how much room we have to write data.
683  override = False, normal
684  override = True, override XXXXXXXX'd out password or NO PASS
685 ************************************************************************/
686
687 static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
688 {
689         /* Static buffers we will return. */
690         pstring user_name;
691
692         char linebuf[256];
693         char readbuf[1024];
694         unsigned char 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         while (!feof(fp)) {
742                 pwd_seekpos = sys_ftell(fp);
743
744                 linebuf[0] = '\0';
745
746                 fgets(linebuf, sizeof(linebuf), fp);
747                 if (ferror(fp)) {
748                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
749                         fclose(fp);
750                         return False;
751                 }
752
753                 /*
754                  * Check if the string is terminated with a newline - if not
755                  * then we must keep reading and discard until we get one.
756                  */
757                 linebuf_len = strlen(linebuf);
758                 if (linebuf[linebuf_len - 1] != '\n') {
759                         c = '\0';
760                         while (!ferror(fp) && !feof(fp)) {
761                                 c = fgetc(fp);
762                                 if (c == '\n') {
763                                         break;
764                                 }
765                         }
766                 } else {
767                         linebuf[linebuf_len - 1] = '\0';
768                 }
769
770 #ifdef DEBUG_PASSWORD
771                 DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
772 #endif
773
774                 if ((linebuf[0] == 0) && feof(fp)) {
775                         DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
776                         break;
777                 }
778
779                 /*
780                  * The line we have should be of the form :-
781                  * 
782                  * username:uid:[32hex bytes]:....other flags presently
783                  * ignored....
784                  * 
785                  * or,
786                  *
787                  * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
788                  *
789                  * if Windows NT compatible passwords are also present.
790                  */
791
792                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
793                         DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
794                         continue;
795                 }
796
797                 p = (unsigned char *) strchr_m(linebuf, ':');
798
799                 if (p == NULL) {
800                         DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
801                         continue;
802                 }
803
804                 /*
805                  * As 256 is shorter than a pstring we don't need to check
806                  * length here - if this ever changes....
807                  */
808
809                 SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
810
811                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
812                 user_name[PTR_DIFF(p, linebuf)] = '\0';
813                 if (strequal(user_name, pwd->smb_name)) {
814                         found_entry = True;
815                         break;
816                 }
817         }
818
819         if (!found_entry) {
820                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
821                 fclose(fp);
822
823                 DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
824                         pwd->smb_name));
825                 return False;
826         }
827
828         DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
829
830         /* User name matches - get uid and password */
831         p++; /* Go past ':' */
832
833         if (!isdigit(*p)) {
834                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
835                         pwd->smb_name));
836                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
837                 fclose(fp);
838                 return False;
839         }
840
841         while (*p && isdigit(*p)) {
842                 p++;
843         }
844         if (*p != ':') {
845                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
846                         pwd->smb_name));
847                 pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
848                 fclose(fp);
849                 return False;
850         }
851
852         /*
853          * Now get the password value - this should be 32 hex digits
854          * which are the ascii representations of a 16 byte string.
855          * Get two at a time and put them into the password.
856          */
857         p++;
858
859         /* Record exact password position */
860         pwd_seekpos += PTR_DIFF(p, linebuf);
861
862         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
863                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
864                         pwd->smb_name));
865                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
866                 fclose(fp);
867                 return (False);
868         }
869
870         if (p[32] != ':') {
871                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
872                         pwd->smb_name));
873                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
874                 fclose(fp);
875                 return False;
876         }
877
878         /* Now check if the NT compatible password is available. */
879         p += 33; /* Move to the first character of the line after the lanman password. */
880         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
881                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
882                         pwd->smb_name));
883                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
884                 fclose(fp);
885                 return (False);
886         }
887
888         if (p[32] != ':') {
889                 DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
890                         pwd->smb_name));
891                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
892                 fclose(fp);
893                 return False;
894         }
895
896         /* 
897          * Now check if the account info and the password last
898          * change time is available.
899          */
900         p += 33; /* Move to the first character of the line after the NT password. */
901
902         if (*p == '[') {
903                 i = 0;
904                 encode_bits[i++] = *p++;
905                 while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
906                         encode_bits[i++] = *p++;
907                 }
908
909                 encode_bits[i++] = ']';
910                 encode_bits[i++] = '\0';
911
912                 if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
913                         /*
914                          * We are using a new format, space padded
915                          * acct ctrl field. Encode the given acct ctrl
916                          * bits into it.
917                          */
918                         fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
919                 } else {
920                         DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format for user %s. \
921 This is no longer supported.!\n", pwd->smb_name));
922                         DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
923                         pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
924                         fclose(fp);
925                         return False;
926                 }
927
928                 /* Go past the ']' */
929                 if(linebuf_len > PTR_DIFF(p, linebuf)) {
930                         p++;
931                 }
932
933                 if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
934                         p++;
935
936                         /* We should be pointing at the LCT entry. */
937                         if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
938                                 p += 4;
939                                 for(i = 0; i < 8; i++) {
940                                         if(p[i] == '\0' || !isxdigit(p[i])) {
941                                                 break;
942                                         }
943                                 }
944                                 if(i == 8) {
945                                         /*
946                                          * p points at 8 characters of hex digits -
947                                          * read into a time_t as the seconds since
948                                          * 1970 that the password was last changed.
949                                          */
950                                         got_pass_last_set_time = True;
951                                 } /* i == 8 */
952                         } /* *p && StrnCaseCmp() */
953                 } /* p == ':' */
954         } /* p == '[' */
955
956         /* Entry is correctly formed. */
957
958         /* Create the 32 byte representation of the new p16 */
959         pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
960
961         /* Add on the NT md4 hash */
962         ascii_p16[32] = ':';
963         wr_len = 66;
964         pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
965         ascii_p16[65] = ':';
966         ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
967
968         /* Add on the account info bits and the time of last password change. */
969         if(got_pass_last_set_time) {
970                 slprintf(&ascii_p16[strlen(ascii_p16)], 
971                         sizeof(ascii_p16)-(strlen(ascii_p16)+1),
972                         "%s:LCT-%08X:", 
973                         encode_bits, (uint32)pwd->pass_last_set_time );
974                 wr_len = strlen(ascii_p16);
975         }
976
977 #ifdef DEBUG_PASSWORD
978         DEBUG(100,("mod_smbfilepwd_entry: "));
979         dump_data(100, ascii_p16, wr_len);
980 #endif
981
982         if(wr_len > sizeof(linebuf)) {
983                 DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
984                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
985                 fclose(fp);
986                 return (False);
987         }
988
989         /*
990          * Do an atomic write into the file at the position defined by
991          * seekpos.
992          */
993
994         /* The mod user write needs to be atomic - so get the fd from 
995                 the fp and do a raw write() call.
996          */
997
998         fd = fileno(fp);
999
1000         if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1001                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1002                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1003                 fclose(fp);
1004                 return False;
1005         }
1006
1007         /* Sanity check - ensure the areas we are writing are framed by ':' */
1008         if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1009                 DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1010                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1011                 fclose(fp);
1012                 return False;
1013         }
1014
1015         if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))    {
1016                 DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1017                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1018                 fclose(fp);
1019                 return False;
1020         }
1021  
1022         if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1023                 DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1024                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1025                 fclose(fp);
1026                 return False;
1027         }
1028
1029         if (write(fd, ascii_p16, wr_len) != wr_len) {
1030                 DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1031                 pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1032                 fclose(fp);
1033                 return False;
1034         }
1035
1036         pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
1037         fclose(fp);
1038         return True;
1039 }
1040
1041 /************************************************************************
1042  Routine to delete an entry in the smbpasswd file by name.
1043 *************************************************************************/
1044
1045 static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
1046 {
1047         const char *pfile = smbpasswd_state->smbpasswd_file;
1048         pstring pfile2;
1049         struct smb_passwd *pwd = NULL;
1050         FILE *fp = NULL;
1051         FILE *fp_write = NULL;
1052         int pfile2_lockdepth = 0;
1053
1054         slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
1055
1056         /*
1057          * Open the smbpassword file - for update. It needs to be update
1058          * as we need any other processes to wait until we have replaced
1059          * it.
1060          */
1061
1062         if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
1063                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1064                 return False;
1065         }
1066
1067         /*
1068          * Create the replacement password file.
1069          */
1070         if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1071                 DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1072                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1073                 return False;
1074         }
1075
1076         /*
1077          * Scan the file, a line at a time and check if the name matches.
1078          */
1079
1080         while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
1081                 char *new_entry;
1082                 size_t new_entry_length;
1083
1084                 if (strequal(name, pwd->smb_name)) {
1085                         DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1086                         continue;
1087                 }
1088
1089                 /*
1090                  * We need to copy the entry out into the second file.
1091                  */
1092
1093                 if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1094                         DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1095 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1096                         unlink(pfile2);
1097                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1098                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1099                         return False;
1100                 }
1101
1102                 new_entry_length = strlen(new_entry);
1103
1104                 if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1105                         DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1106 Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1107                         unlink(pfile2);
1108                         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1109                         endsmbfilepwent(fp_write, &pfile2_lockdepth);
1110                         free(new_entry);
1111                         return False;
1112                 }
1113
1114                 free(new_entry);
1115         }
1116
1117         /*
1118          * Ensure pfile2 is flushed before rename.
1119          */
1120
1121         if(fflush(fp_write) != 0) {
1122                 DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1123                 endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1124                 endsmbfilepwent(fp_write,&pfile2_lockdepth);
1125                 return False;
1126         }
1127
1128         /*
1129          * Do an atomic rename - then release the locks.
1130          */
1131
1132         if(rename(pfile2,pfile) != 0) {
1133                 unlink(pfile2);
1134         }
1135   
1136         endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
1137         endsmbfilepwent(fp_write,&pfile2_lockdepth);
1138         return True;
1139 }
1140
1141 /*********************************************************************
1142  Create a smb_passwd struct from a SAM_ACCOUNT.
1143  We will not allocate any new memory.  The smb_passwd struct
1144  should only stay around as long as the SAM_ACCOUNT does.
1145  ********************************************************************/
1146
1147 static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
1148 {
1149         uint32 rid;
1150
1151         if (sampass == NULL) 
1152                 return False;
1153         ZERO_STRUCTP(smb_pw);
1154
1155         if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
1156                 rid = pdb_get_user_rid(sampass);
1157                 
1158                 /* If the user specified a RID, make sure its able to be both stored and retreived */
1159                 if (rid == DOMAIN_USER_RID_GUEST) {
1160                         struct passwd *passwd = getpwnam_alloc(lp_guestaccount());
1161                         if (!passwd) {
1162                                 DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount()));
1163                                 return False;
1164                         }
1165                         smb_pw->smb_userid=passwd->pw_uid;
1166                         passwd_free(&passwd);
1167
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 SAM_ACCOUNT from a smb_passwd struct
1189  ********************************************************************/
1190
1191 static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
1192                               SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
1193 {
1194         struct passwd *pwfile;
1195         
1196         if (sam_pass==NULL) {
1197                 DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
1198                 return False;
1199         }
1200
1201         /* verify the user account exists */
1202                         
1203         if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) {
1204                 DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid "
1205                 "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
1206                         return False;
1207         }
1208         
1209         if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile)))
1210                 return False;
1211                 
1212         passwd_free(&pwfile);
1213
1214         /* set remaining fields */
1215                 
1216         pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
1217         pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
1218         pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
1219         pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1220         pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
1221         
1222         return True;
1223 }
1224
1225 /*****************************************************************
1226  Functions to be implemented by the new passdb API 
1227  ****************************************************************/
1228
1229 static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
1230 {
1231         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1232         
1233         smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1234                                                        update ? PWF_UPDATE : PWF_READ, 
1235                                                        &(smbpasswd_state->pw_file_lock_depth));
1236                                    
1237         /* did we fail?  Should we try to create it? */
1238         if (!smbpasswd_state->pw_file && update && errno == ENOENT) {
1239                 FILE *fp;
1240                 /* slprintf(msg_str,msg_str_len-1,
1241                    "smbpasswd file did not exist - attempting to create it.\n"); */
1242                 DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
1243                 fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
1244                 if (fp) {
1245                         fprintf(fp, "# Samba SMB password file\n");
1246                         fclose(fp);
1247                 }
1248                 
1249                 smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
1250                                                              update ? PWF_UPDATE : PWF_READ, 
1251                                                              &(smbpasswd_state->pw_file_lock_depth));
1252         }
1253         
1254         if (smbpasswd_state->pw_file != NULL)
1255                 return NT_STATUS_OK;
1256         else
1257                 return NT_STATUS_UNSUCCESSFUL;  
1258 }
1259
1260 static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
1261 {
1262         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1263         endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
1264 }
1265  
1266 /*****************************************************************
1267  ****************************************************************/
1268
1269 static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
1270 {
1271         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1272         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1273         struct smb_passwd *pw_buf=NULL;
1274         BOOL done = False;
1275         DEBUG(5,("pdb_getsampwent\n"));
1276
1277         if (user==NULL) {
1278                 DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
1279 #if 0
1280                 smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
1281 #endif
1282                 return nt_status;
1283         }
1284
1285         while (!done) {
1286                 /* do we have an entry? */
1287                 pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
1288                 if (pw_buf == NULL) 
1289                         return nt_status;
1290
1291                 /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
1292                    We loop in case the user in the pdb does not exist in 
1293                    the local system password file */
1294                 if (build_sam_account(smbpasswd_state, user, pw_buf))
1295                         done = True;
1296         }
1297
1298         DEBUG(5,("getsampwent (smbpasswd): done\n"));
1299
1300         /* success */
1301         return NT_STATUS_OK;
1302 }
1303
1304 /****************************************************************
1305  Search smbpasswd file by iterating over the entries.  Do not
1306  call getpwnam() for unix account information until we have found
1307  the correct entry
1308  ***************************************************************/
1309
1310 static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
1311                                   SAM_ACCOUNT *sam_acct, const char *username)
1312 {
1313         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1314         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1315         struct smb_passwd *smb_pw;
1316         void *fp = NULL;
1317
1318         DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
1319
1320         /* startsmbfilepwent() is used here as we don't want to lookup
1321            the UNIX account in the local system password file until
1322            we have a match.  */
1323         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1324
1325         if (fp == NULL) {
1326                 DEBUG(0, ("Unable to open passdb database.\n"));
1327                 return nt_status;
1328         }
1329
1330         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
1331                 /* do nothing....another loop */ ;
1332         
1333         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1334
1335
1336         /* did we locate the username in smbpasswd  */
1337         if (smb_pw == NULL)
1338                 return nt_status;
1339         
1340         DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1341
1342         if (!sam_acct) {
1343                 DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
1344 #if 0
1345                 smb_panic("NULL pointer passed to pdb_getsampwnam\n");
1346 #endif
1347                 return nt_status;
1348         }
1349                 
1350         /* now build the SAM_ACCOUNT */
1351         if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
1352                 return nt_status;
1353
1354         /* success */
1355         return NT_STATUS_OK;
1356 }
1357
1358 static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
1359 {
1360         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1361         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1362         struct smb_passwd *smb_pw;
1363         void *fp = NULL;
1364         fstring sid_str;
1365         uint32 rid;
1366         
1367         DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
1368
1369         if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
1370                 return NT_STATUS_UNSUCCESSFUL;
1371
1372         /* More special case 'guest account' hacks... */
1373         if (rid == DOMAIN_USER_RID_GUEST) {
1374                 const char *guest_account = lp_guestaccount();
1375                 if (!(guest_account && *guest_account)) {
1376                         DEBUG(1, ("Guest account not specfied!\n"));
1377                         return nt_status;
1378                 }
1379                 return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
1380         }
1381
1382         /* Open the sam password file - not for update. */
1383         fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
1384
1385         if (fp == NULL) {
1386                 DEBUG(0, ("Unable to open passdb database.\n"));
1387                 return nt_status;
1388         }
1389
1390         while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
1391                 /* do nothing */ ;
1392
1393         endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
1394
1395
1396         /* did we locate the username in smbpasswd  */
1397         if (smb_pw == NULL)
1398                 return nt_status;
1399         
1400         DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
1401                 
1402         if (!sam_acct) {
1403                 DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
1404 #if 0
1405                 smb_panic("NULL pointer passed to pdb_getsampwrid\n");
1406 #endif
1407                 return nt_status;
1408         }
1409
1410         /* now build the SAM_ACCOUNT */
1411         if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
1412                 return nt_status;
1413
1414         /* build_sam_account might change the SID on us, if the name was for the guest account */
1415         if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
1416                 fstring sid_string1, sid_string2;
1417                 DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
1418                           sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
1419                 return NT_STATUS_NO_SUCH_USER;
1420         }
1421
1422         /* success */
1423         return NT_STATUS_OK;
1424 }
1425
1426 static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1427 {
1428         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1429         struct smb_passwd smb_pw;
1430         
1431         /* convert the SAM_ACCOUNT */
1432         if (!build_smb_pass(&smb_pw, sampass)) {
1433                 return NT_STATUS_UNSUCCESSFUL;
1434         }
1435         
1436         /* add the entry */
1437         if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1438                 return NT_STATUS_UNSUCCESSFUL;
1439         }
1440         
1441         return NT_STATUS_OK;
1442 }
1443
1444 static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1445 {
1446         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1447         struct smb_passwd smb_pw;
1448         
1449         /* convert the SAM_ACCOUNT */
1450         if (!build_smb_pass(&smb_pw, sampass)) {
1451                 DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
1452                 return NT_STATUS_UNSUCCESSFUL;
1453         }
1454         
1455         /* update the entry */
1456         if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
1457                 DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
1458                 return NT_STATUS_UNSUCCESSFUL;
1459         }
1460         
1461         return NT_STATUS_OK;
1462 }
1463
1464 static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
1465 {
1466         struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
1467
1468         const char *username = pdb_get_username(sampass);
1469
1470         if (del_smbfilepwd_entry(smbpasswd_state, username))
1471                 return NT_STATUS_OK;
1472
1473         return NT_STATUS_UNSUCCESSFUL;
1474 }
1475
1476 static void free_private_data(void **vp) 
1477 {
1478         struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
1479         
1480         endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
1481         
1482         *privates = NULL;
1483         /* No need to free any further, as it is talloc()ed */
1484 }
1485
1486 static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
1487 {
1488         NTSTATUS nt_status;
1489         struct smbpasswd_privates *privates;
1490
1491         if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
1492                 return nt_status;
1493         }
1494
1495         (*pdb_method)->name = "smbpasswd";
1496
1497         (*pdb_method)->setsampwent = smbpasswd_setsampwent;
1498         (*pdb_method)->endsampwent = smbpasswd_endsampwent;
1499         (*pdb_method)->getsampwent = smbpasswd_getsampwent;
1500         (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
1501         (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
1502         (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
1503         (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
1504         (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
1505
1506         /* Setup private data and free function */
1507
1508         privates = TALLOC_ZERO_P(pdb_context->mem_ctx, struct smbpasswd_privates);
1509
1510         if (!privates) {
1511                 DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
1512                 return NT_STATUS_NO_MEMORY;
1513         }
1514
1515         /* Store some config details */
1516
1517         if (location) {
1518                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
1519         } else {
1520                 privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
1521         }
1522         
1523         if (!privates->smbpasswd_file) {
1524                 DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
1525                 return NT_STATUS_NO_MEMORY;
1526         }
1527
1528         (*pdb_method)->private_data = privates;
1529
1530         (*pdb_method)->free_private_data = free_private_data;
1531
1532         return NT_STATUS_OK;
1533 }
1534
1535 NTSTATUS pdb_smbpasswd_init(void) 
1536 {
1537         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
1538 }