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