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