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