• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba/source/passdb/
1/*
2 * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
3 * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 675
17 * Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include "includes.h"
21
22#ifdef USE_SMBPASS_DB
23
24extern int DEBUGLEVEL;
25extern pstring samlogon_user;
26extern BOOL sam_logon_in_ssb;
27
28static int pw_file_lock_depth;
29
30enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
31
32/***************************************************************
33 Internal fn to enumerate the smbpasswd list. Returns a void pointer
34 to ensure no modification outside this module. Checks for atomic
35 rename of smbpasswd file on update or create once the lock has
36 been granted to prevent race conditions. JRA.
37****************************************************************/
38
39static void *startsmbfilepwent_internal(const char *pfile, enum pwf_access_type type, int *lock_depth)
40{
41  FILE *fp = NULL;
42  const char *open_mode = NULL;
43  int race_loop = 0;
44  int lock_type;
45
46  if (!*pfile) {
47    DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
48    return (NULL);
49  }
50
51  switch(type) {
52  case PWF_READ:
53    open_mode = "rb";
54    lock_type = F_RDLCK;
55    break;
56  case PWF_UPDATE:
57    open_mode = "r+b";
58    lock_type = F_WRLCK;
59    break;
60  case PWF_CREATE:
61    /*
62     * Ensure atomic file creation.
63     */
64    {
65      int i, fd = -1;
66
67      for(i = 0; i < 5; i++) {
68        if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
69          break;
70        sys_usleep(200); /* Spin, spin... */
71      }
72      if(fd == -1) {
73        DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
74        return NULL;
75      }
76      close(fd);
77      open_mode = "r+b";
78      lock_type = F_WRLCK;
79      break;
80    }
81  }
82
83  for(race_loop = 0; race_loop < 5; race_loop++) {
84    DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
85
86    if((fp = sys_fopen(pfile, open_mode)) == NULL) {
87      DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) ));
88      return NULL;
89    }
90
91    if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
92      DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
93      fclose(fp);
94      return NULL;
95    }
96
97    /*
98     * Only check for replacement races on update or create.
99     * For read we don't mind if the data is one record out of date.
100     */
101
102    if(type == PWF_READ) {
103      break;
104    } else {
105      SMB_STRUCT_STAT sbuf1, sbuf2;
106
107      /*
108       * Avoid the potential race condition between the open and the lock
109       * by doing a stat on the filename and an fstat on the fd. If the
110       * two inodes differ then someone did a rename between the open and
111       * the lock. Back off and try the open again. Only do this 5 times to
112       * prevent infinate loops. JRA.
113       */
114
115      if (sys_stat(pfile,&sbuf1) != 0) {
116        DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
117        pw_file_unlock(fileno(fp), lock_depth);
118        fclose(fp);
119        return NULL;
120      }
121
122      if (sys_fstat(fileno(fp),&sbuf2) != 0) {
123        DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
124        pw_file_unlock(fileno(fp), lock_depth);
125        fclose(fp);
126        return NULL;
127      }
128
129      if( sbuf1.st_ino == sbuf2.st_ino) {
130        /* No race. */
131        break;
132      }
133
134      /*
135       * Race occurred - back off and try again...
136       */
137
138      pw_file_unlock(fileno(fp), lock_depth);
139      fclose(fp);
140    }
141  }
142
143  if(race_loop == 5) {
144    DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
145    return NULL;
146  }
147
148  /* Set a buffer to do more efficient reads */
149  setvbuf(fp, (char *)NULL, _IOFBF, 1024);
150
151  /* Make sure it is only rw by the owner */
152  if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
153    DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
154Error was %s\n.", pfile, strerror(errno) ));
155    pw_file_unlock(fileno(fp), lock_depth);
156    fclose(fp);
157    return NULL;
158  }
159
160  /* We have a lock on the file. */
161  return (void *)fp;
162}
163
164/***************************************************************
165 Start to enumerate the smbpasswd list. Returns a void pointer
166 to ensure no modification outside this module.
167****************************************************************/
168
169static void *startsmbfilepwent(BOOL update)
170{
171  return startsmbfilepwent_internal(lp_smb_passwd_file(), update ? PWF_UPDATE : PWF_READ, &pw_file_lock_depth);
172}
173
174/***************************************************************
175 End enumeration of the smbpasswd list.
176****************************************************************/
177
178static void endsmbfilepwent_internal(void *vp, int *lock_depth)
179{
180  FILE *fp = (FILE *)vp;
181
182  pw_file_unlock(fileno(fp), lock_depth);
183  fclose(fp);
184  DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
185}
186
187/***************************************************************
188 End enumeration of the smbpasswd list - operate on the default
189 lock_depth.
190****************************************************************/
191
192static void endsmbfilepwent(void *vp)
193{
194  endsmbfilepwent_internal(vp, &pw_file_lock_depth);
195}
196
197/*************************************************************************
198 Routine to return the next entry in the smbpasswd list.
199 *************************************************************************/
200
201static struct smb_passwd *getsmbfilepwent(void *vp)
202{
203  /* Static buffers we will return. */
204  static struct smb_passwd pw_buf;
205  static pstring  user_name;
206  static unsigned char smbpwd[16];
207  static unsigned char smbntpwd[16];
208  FILE *fp = (FILE *)vp;
209  char            linebuf[256];
210  unsigned char   c;
211  unsigned char  *p;
212  long            uidval;
213  size_t            linebuf_len;
214
215  if(fp == NULL) {
216    DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
217    return NULL;
218  }
219
220  pdb_init_smb(&pw_buf);
221
222  pw_buf.acct_ctrl = ACB_NORMAL;
223
224  /*
225   * Scan the file, a line at a time and check if the name matches.
226   */
227  while (!feof(fp)) {
228    linebuf[0] = '\0';
229
230    fgets(linebuf, 256, fp);
231    if (ferror(fp)) {
232      return NULL;
233    }
234
235    /*
236     * Check if the string is terminated with a newline - if not
237     * then we must keep reading and discard until we get one.
238     */
239    linebuf_len = strlen(linebuf);
240    if (linebuf[linebuf_len - 1] != '\n') {
241      c = '\0';
242      while (!ferror(fp) && !feof(fp)) {
243        c = fgetc(fp);
244        if (c == '\n')
245          break;
246      }
247    } else
248      linebuf[linebuf_len - 1] = '\0';
249
250#ifdef DEBUG_PASSWORD
251    DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
252#endif
253    if ((linebuf[0] == 0) && feof(fp)) {
254      DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
255      break;
256    }
257    /*
258     * The line we have should be of the form :-
259     *
260     * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
261     * ignored....
262     *
263     * or,
264     *
265     * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
266     *
267     * if Windows NT compatible passwords are also present.
268     * [Account type] is an ascii encoding of the type of account.
269     * LCT-(8 hex digits) is the time_t value of the last change time.
270     */
271
272    if (linebuf[0] == '#' || linebuf[0] == '\0') {
273      DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
274      continue;
275    }
276    p = (unsigned char *) strchr(linebuf, ':');
277    if (p == NULL) {
278      DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
279      continue;
280    }
281    /*
282     * As 256 is shorter than a pstring we don't need to check
283     * length here - if this ever changes....
284     */
285    strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
286    user_name[PTR_DIFF(p, linebuf)] = '\0';
287
288    /* Get smb uid. */
289
290    p++;		/* Go past ':' */
291
292    if(*p == '-') {
293      DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
294      continue;
295    }
296
297    if (!isdigit(*p)) {
298      DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
299      continue;
300    }
301
302    uidval = atoi((char *) p);
303
304    while (*p && isdigit(*p))
305      p++;
306
307    if (*p != ':') {
308      DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
309      continue;
310    }
311
312    pw_buf.smb_name = user_name;
313    pw_buf.smb_userid = uidval;
314
315    /*
316     * Now get the password value - this should be 32 hex digits
317     * which are the ascii representations of a 16 byte string.
318     * Get two at a time and put them into the password.
319     */
320
321    /* Skip the ':' */
322    p++;
323
324    if (*p == '*' || *p == 'X') {
325      /* Password deliberately invalid - end here. */
326      DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name));
327      pw_buf.smb_nt_passwd = NULL;
328      pw_buf.smb_passwd = NULL;
329      pw_buf.acct_ctrl |= ACB_DISABLED;
330      return &pw_buf;
331    }
332
333    if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
334      DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
335      continue;
336    }
337
338    if (p[32] != ':') {
339      DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
340      continue;
341    }
342
343    if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
344      pw_buf.smb_passwd = NULL;
345      pw_buf.acct_ctrl |= ACB_PWNOTREQ;
346    } else {
347      if (!pdb_gethexpwd((char *)p, smbpwd)) {
348        DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
349        continue;
350      }
351      pw_buf.smb_passwd = smbpwd;
352    }
353
354    /*
355     * Now check if the NT compatible password is
356     * available.
357     */
358    pw_buf.smb_nt_passwd = NULL;
359
360    p += 33; /* Move to the first character of the line after
361                the lanman password. */
362    if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
363      if (*p != '*' && *p != 'X') {
364        if(pdb_gethexpwd((char *)p,smbntpwd))
365          pw_buf.smb_nt_passwd = smbntpwd;
366      }
367      p += 33; /* Move to the first character of the line after
368                  the NT password. */
369    }
370
371    DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
372	     user_name, uidval));
373
374    if (*p == '[')
375	{
376      unsigned char *end_p = (unsigned char *)strchr((char *)p, ']');
377      pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p);
378
379      /* Must have some account type set. */
380      if(pw_buf.acct_ctrl == 0)
381        pw_buf.acct_ctrl = ACB_NORMAL;
382
383      /* Now try and get the last change time. */
384      if(end_p)
385        p = end_p + 1;
386      if(*p == ':') {
387        p++;
388        if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
389          int i;
390          p += 4;
391          for(i = 0; i < 8; i++) {
392            if(p[i] == '\0' || !isxdigit(p[i]))
393              break;
394          }
395          if(i == 8) {
396            /*
397             * p points at 8 characters of hex digits -
398             * read into a time_t as the seconds since
399             * 1970 that the password was last changed.
400             */
401            pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
402          }
403        }
404      }
405    } else {
406      /* 'Old' style file. Fake up based on user name. */
407      /*
408       * Currently trust accounts are kept in the same
409       * password file as 'normal accounts'. If this changes
410       * we will have to fix this code. JRA.
411       */
412      if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') {
413        pw_buf.acct_ctrl &= ~ACB_NORMAL;
414        pw_buf.acct_ctrl |= ACB_WSTRUST;
415      }
416    }
417
418    return &pw_buf;
419  }
420
421  DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
422  return NULL;
423}
424
425/*************************************************************************
426 Routine to return the next entry in the smbpasswd list.
427 this function is a nice, messy combination of reading:
428 - the smbpasswd file
429 - the unix password database
430 - smb.conf options (not done at present).
431 *************************************************************************/
432
433static struct sam_passwd *getsmbfile21pwent(void *vp)
434{
435	struct smb_passwd *pw_buf = getsmbfilepwent(vp);
436	static struct sam_passwd user;
437	struct passwd *pwfile;
438
439	static pstring full_name;
440	static pstring home_dir;
441	static pstring home_drive;
442	static pstring logon_script;
443	static pstring profile_path;
444	static pstring acct_desc;
445	static pstring workstations;
446
447	DEBUG(5,("getsmbfile21pwent\n"));
448
449	if (pw_buf == NULL) return NULL;
450
451	pwfile = sys_getpwnam(pw_buf->smb_name);
452	if (pwfile == NULL)
453	{
454		DEBUG(0,("getsmbfile21pwent: smbpasswd database is corrupt!\n"));
455		DEBUG(0,("getsmbfile21pwent: username %s not in unix passwd database!\n", pw_buf->smb_name));
456		return NULL;
457	}
458
459	pdb_init_sam(&user);
460
461	pstrcpy(samlogon_user, pw_buf->smb_name);
462
463	if (samlogon_user[strlen(samlogon_user)-1] != '$')
464	{
465		/* XXXX hack to get standard_sub_basic() to use sam logon username */
466		/* possibly a better way would be to do a become_user() call */
467		sam_logon_in_ssb = True;
468
469		user.smb_userid    = pw_buf->smb_userid;
470		user.smb_grpid     = pwfile->pw_gid;
471
472		user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
473		user.group_rid = pdb_gid_to_group_rid(user.smb_grpid );
474
475		pstrcpy(full_name    , pwfile->pw_gecos        );
476		pstrcpy(logon_script , lp_logon_script       ());
477		pstrcpy(profile_path , lp_logon_path         ());
478		pstrcpy(home_drive   , lp_logon_drive        ());
479		pstrcpy(home_dir     , lp_logon_home         ());
480		pstrcpy(acct_desc    , "");
481		pstrcpy(workstations , "");
482
483		sam_logon_in_ssb = False;
484	}
485	else
486	{
487		user.smb_userid    = pw_buf->smb_userid;
488		user.smb_grpid     = pwfile->pw_gid;
489
490		user.user_rid  = pdb_uid_to_user_rid (user.smb_userid);
491		user.group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
492
493		pstrcpy(full_name    , "");
494		pstrcpy(logon_script , "");
495		pstrcpy(profile_path , "");
496		pstrcpy(home_drive   , "");
497		pstrcpy(home_dir     , "");
498		pstrcpy(acct_desc    , "");
499		pstrcpy(workstations , "");
500	}
501
502	user.smb_name     = pw_buf->smb_name;
503	user.full_name    = full_name;
504	user.home_dir     = home_dir;
505	user.dir_drive    = home_drive;
506	user.logon_script = logon_script;
507	user.profile_path = profile_path;
508	user.acct_desc    = acct_desc;
509	user.workstations = workstations;
510
511	user.unknown_str = NULL; /* don't know, yet! */
512	user.munged_dial = NULL; /* "munged" dial-back telephone number */
513
514	user.smb_nt_passwd = pw_buf->smb_nt_passwd;
515	user.smb_passwd    = pw_buf->smb_passwd;
516
517	user.acct_ctrl = pw_buf->acct_ctrl;
518
519	user.unknown_3 = 0xffffff; /* don't know */
520	user.logon_divs = 168; /* hours per week */
521	user.hours_len = 21; /* 21 times 8 bits = 168 */
522	memset(user.hours, 0xff, user.hours_len); /* available at all hours */
523	user.unknown_5 = 0x00020000; /* don't know */
524	user.unknown_5 = 0x000004ec; /* don't know */
525
526	return &user;
527}
528
529/*************************************************************************
530 Return the current position in the smbpasswd list as an SMB_BIG_UINT.
531 This must be treated as an opaque token.
532*************************************************************************/
533
534static SMB_BIG_UINT getsmbfilepwpos(void *vp)
535{
536  return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
537}
538
539/*************************************************************************
540 Set the current position in the smbpasswd list from an SMB_BIG_UINT.
541 This must be treated as an opaque token.
542*************************************************************************/
543
544static BOOL setsmbfilepwpos(void *vp, SMB_BIG_UINT tok)
545{
546  return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
547}
548
549/************************************************************************
550 Create a new smbpasswd entry - malloced space returned.
551*************************************************************************/
552
553char *format_new_smbpasswd_entry(struct smb_passwd *newpwd)
554{
555  int new_entry_length;
556  char *new_entry;
557  char *p;
558  int i;
559
560  new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
561
562  if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
563    DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
564    return NULL;
565  }
566
567  slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
568  p = &new_entry[strlen(new_entry)];
569
570  if(newpwd->smb_passwd != NULL) {
571    for( i = 0; i < 16; i++) {
572      slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]);
573    }
574  } else {
575    i=0;
576    if(newpwd->acct_ctrl & ACB_PWNOTREQ)
577      safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
578    else
579      safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
580  }
581
582  p += 32;
583
584  *p++ = ':';
585
586  if(newpwd->smb_nt_passwd != NULL) {
587    for( i = 0; i < 16; i++) {
588      slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]);
589    }
590  } else {
591    if(newpwd->acct_ctrl & ACB_PWNOTREQ)
592      safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
593    else
594      safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry));
595  }
596
597  p += 32;
598
599  *p++ = ':';
600
601  /* Add the account encoding and the last change time. */
602  slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
603           pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
604           (uint32)newpwd->pass_last_set_time);
605
606  return new_entry;
607}
608
609/************************************************************************
610 Routine to add an entry to the smbpasswd file.
611*************************************************************************/
612
613static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd)
614{
615  char *pfile = lp_smb_passwd_file();
616  struct smb_passwd *pwd = NULL;
617  FILE *fp = NULL;
618  int wr_len;
619  int fd;
620  size_t new_entry_length;
621  char *new_entry;
622  SMB_OFF_T offpos;
623
624  /* Open the smbpassword file - for update. */
625  fp = startsmbfilepwent(True);
626
627  if (fp == NULL) {
628    DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
629    return False;
630  }
631
632  /*
633   * Scan the file, a line at a time and check if the name matches.
634   */
635
636  while ((pwd = getsmbfilepwent(fp)) != NULL) {
637    if (strequal(newpwd->smb_name, pwd->smb_name)) {
638      DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
639      endsmbfilepwent(fp);
640      return False;
641    }
642  }
643
644  /* Ok - entry doesn't exist. We can add it */
645
646  /* Create a new smb passwd entry and set it to the given password. */
647  /*
648   * The add user write needs to be atomic - so get the fd from
649   * the fp and do a raw write() call.
650   */
651  fd = fileno(fp);
652
653  if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
654    DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
655Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
656    endsmbfilepwent(fp);
657    return False;
658  }
659
660  if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
661    DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
662Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
663    endsmbfilepwent(fp);
664    return False;
665  }
666
667  new_entry_length = strlen(new_entry);
668
669#ifdef DEBUG_PASSWORD
670  DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
671		             fd, new_entry_length, new_entry));
672#endif
673
674  if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
675    DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
676Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
677
678    /* Remove the entry we just wrote. */
679    if(sys_ftruncate(fd, offpos) == -1) {
680      DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
681Error was %s. Password file may be corrupt ! Please examine by hand !\n",
682             newpwd->smb_name, strerror(errno)));
683    }
684
685    endsmbfilepwent(fp);
686    free(new_entry);
687    return False;
688  }
689
690  free(new_entry);
691  endsmbfilepwent(fp);
692  return True;
693}
694
695/************************************************************************
696 Routine to search the smbpasswd file for an entry matching the username.
697 and then modify its password entry. We can't use the startsmbpwent()/
698 getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
699 in the actual file to decide how much room we have to write data.
700 override = False, normal
701 override = True, override XXXXXXXX'd out password or NO PASS
702************************************************************************/
703
704static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
705{
706  /* Static buffers we will return. */
707  static pstring  user_name;
708
709  char            linebuf[256];
710  char            readbuf[1024];
711  unsigned char   c;
712  fstring         ascii_p16;
713  fstring         encode_bits;
714  unsigned char  *p = NULL;
715  size_t            linebuf_len = 0;
716  FILE           *fp;
717  int             lockfd;
718  char           *pfile = lp_smb_passwd_file();
719  BOOL found_entry = False;
720  BOOL got_pass_last_set_time = False;
721
722  SMB_OFF_T pwd_seekpos = 0;
723
724  int i;
725  int wr_len;
726  int fd;
727
728  if (!*pfile) {
729    DEBUG(0, ("No SMB password file set\n"));
730    return False;
731  }
732  DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
733
734  fp = sys_fopen(pfile, "r+");
735
736  if (fp == NULL) {
737    DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
738    return False;
739  }
740  /* Set a buffer to do more efficient reads */
741  setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
742
743  lockfd = fileno(fp);
744
745  if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) {
746    DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
747    fclose(fp);
748    return False;
749  }
750
751  /* Make sure it is only rw by the owner */
752  chmod(pfile, 0600);
753
754  /* We have a write lock on the file. */
755  /*
756   * Scan the file, a line at a time and check if the name matches.
757   */
758  while (!feof(fp)) {
759    pwd_seekpos = sys_ftell(fp);
760
761    linebuf[0] = '\0';
762
763    fgets(linebuf, sizeof(linebuf), fp);
764    if (ferror(fp)) {
765      pw_file_unlock(lockfd, &pw_file_lock_depth);
766      fclose(fp);
767      return False;
768    }
769
770    /*
771     * Check if the string is terminated with a newline - if not
772     * then we must keep reading and discard until we get one.
773     */
774    linebuf_len = strlen(linebuf);
775    if (linebuf[linebuf_len - 1] != '\n') {
776      c = '\0';
777      while (!ferror(fp) && !feof(fp)) {
778        c = fgetc(fp);
779        if (c == '\n') {
780          break;
781        }
782      }
783    } else {
784      linebuf[linebuf_len - 1] = '\0';
785    }
786
787#ifdef DEBUG_PASSWORD
788    DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
789#endif
790
791    if ((linebuf[0] == 0) && feof(fp)) {
792      DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
793      break;
794    }
795
796    /*
797     * The line we have should be of the form :-
798     *
799     * username:uid:[32hex bytes]:....other flags presently
800     * ignored....
801     *
802     * or,
803     *
804     * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
805     *
806     * if Windows NT compatible passwords are also present.
807     */
808
809    if (linebuf[0] == '#' || linebuf[0] == '\0') {
810      DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
811      continue;
812    }
813
814    p = (unsigned char *) strchr(linebuf, ':');
815
816    if (p == NULL) {
817      DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
818      continue;
819    }
820
821    /*
822     * As 256 is shorter than a pstring we don't need to check
823     * length here - if this ever changes....
824     */
825    strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
826    user_name[PTR_DIFF(p, linebuf)] = '\0';
827    if (strequal(user_name, pwd->smb_name)) {
828      found_entry = True;
829      break;
830    }
831  }
832
833  if (!found_entry) {
834    pw_file_unlock(lockfd, &pw_file_lock_depth);
835    fclose(fp);
836    return False;
837  }
838
839  DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
840
841  /* User name matches - get uid and password */
842  p++;		/* Go past ':' */
843
844  if (!isdigit(*p)) {
845    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
846    pw_file_unlock(lockfd, &pw_file_lock_depth);
847    fclose(fp);
848    return False;
849  }
850
851  while (*p && isdigit(*p))
852    p++;
853  if (*p != ':') {
854    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
855    pw_file_unlock(lockfd, &pw_file_lock_depth);
856    fclose(fp);
857    return False;
858  }
859
860  /*
861   * Now get the password value - this should be 32 hex digits
862   * which are the ascii representations of a 16 byte string.
863   * Get two at a time and put them into the password.
864   */
865  p++;
866
867  /* Record exact password position */
868  pwd_seekpos += PTR_DIFF(p, linebuf);
869
870  if (!override && (*p == '*' || *p == 'X')) {
871    /* Password deliberately invalid - end here. */
872    DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name));
873    pw_file_unlock(lockfd, &pw_file_lock_depth);
874    fclose(fp);
875    return False;
876  }
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,&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,&pw_file_lock_depth);
888    fclose(fp);
889    return False;
890  }
891
892  if (!override && (*p == '*' || *p == 'X')) {
893    pw_file_unlock(lockfd,&pw_file_lock_depth);
894    fclose(fp);
895    return False;
896  }
897
898  /* Now check if the NT compatible password is
899     available. */
900  p += 33; /* Move to the first character of the line after
901              the lanman password. */
902  if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
903    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
904    pw_file_unlock(lockfd,&pw_file_lock_depth);
905    fclose(fp);
906    return (False);
907  }
908
909  if (p[32] != ':') {
910    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
911    pw_file_unlock(lockfd,&pw_file_lock_depth);
912    fclose(fp);
913    return False;
914  }
915
916  /*
917   * Now check if the account info and the password last
918   * change time is available.
919   */
920  p += 33; /* Move to the first character of the line after
921              the NT password. */
922
923  /*
924   * If both NT and lanman passwords are provided - reset password
925   * not required flag.
926   */
927
928  if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) {
929    /* Reqiure password in the future (should ACB_DISABLED also be reset?) */
930    pwd->acct_ctrl &= ~(ACB_PWNOTREQ);
931  }
932
933  if (*p == '[') {
934
935    i = 0;
936    encode_bits[i++] = *p++;
937    while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
938      encode_bits[i++] = *p++;
939
940    encode_bits[i++] = ']';
941    encode_bits[i++] = '\0';
942
943    if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
944      /*
945       * We are using a new format, space padded
946       * acct ctrl field. Encode the given acct ctrl
947       * bits into it.
948       */
949      fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
950    } else {
951      /*
952       * If using the old format and the ACB_DISABLED or
953       * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
954       * here as we have no space to encode the change.
955       */
956      if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
957        pwd->smb_passwd = NULL;
958        pwd->smb_nt_passwd = NULL;
959      }
960    }
961
962    /* Go past the ']' */
963    if(linebuf_len > PTR_DIFF(p, linebuf))
964      p++;
965
966    if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
967      p++;
968
969      /* We should be pointing at the LCT entry. */
970      if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
971
972        p += 4;
973        for(i = 0; i < 8; i++) {
974          if(p[i] == '\0' || !isxdigit(p[i]))
975            break;
976        }
977        if(i == 8) {
978          /*
979           * p points at 8 characters of hex digits -
980           * read into a time_t as the seconds since
981           * 1970 that the password was last changed.
982           */
983          got_pass_last_set_time = True;
984        } /* i == 8 */
985      } /* *p && StrnCaseCmp() */
986    } /* p == ':' */
987  } /* p == '[' */
988
989  /* Entry is correctly formed. */
990
991  /* Create the 32 byte representation of the new p16 */
992  if(pwd->smb_passwd != NULL) {
993    for (i = 0; i < 16; i++) {
994      slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]);
995    }
996  } else {
997    if(pwd->acct_ctrl & ACB_PWNOTREQ)
998      fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
999    else
1000      fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1001  }
1002
1003  /* Add on the NT md4 hash */
1004  ascii_p16[32] = ':';
1005  wr_len = 66;
1006  if (pwd->smb_nt_passwd != NULL) {
1007    for (i = 0; i < 16; i++) {
1008      slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
1009    }
1010  } else {
1011    if(pwd->acct_ctrl & ACB_PWNOTREQ)
1012      fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
1013    else
1014      fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1015  }
1016  ascii_p16[65] = ':';
1017  ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
1018
1019  /* Add on the account info bits and the time of last
1020     password change. */
1021
1022  pwd->pass_last_set_time = time(NULL);
1023
1024  if(got_pass_last_set_time) {
1025    slprintf(&ascii_p16[strlen(ascii_p16)],
1026	     sizeof(ascii_p16)-(strlen(ascii_p16)+1),
1027	     "%s:LCT-%08X:",
1028                     encode_bits, (uint32)pwd->pass_last_set_time );
1029    wr_len = strlen(ascii_p16);
1030  }
1031
1032#ifdef DEBUG_PASSWORD
1033  DEBUG(100,("mod_smbfilepwd_entry: "));
1034  dump_data(100, ascii_p16, wr_len);
1035#endif
1036
1037  if(wr_len > sizeof(linebuf)) {
1038    DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
1039    pw_file_unlock(lockfd,&pw_file_lock_depth);
1040    fclose(fp);
1041    return (False);
1042  }
1043
1044  /*
1045   * Do an atomic write into the file at the position defined by
1046   * seekpos.
1047   */
1048
1049  /* The mod user write needs to be atomic - so get the fd from
1050     the fp and do a raw write() call.
1051   */
1052
1053  fd = fileno(fp);
1054
1055  if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
1056    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1057    pw_file_unlock(lockfd,&pw_file_lock_depth);
1058    fclose(fp);
1059    return False;
1060  }
1061
1062  /* Sanity check - ensure the areas we are writing are framed by ':' */
1063  if (read(fd, linebuf, wr_len+1) != wr_len+1) {
1064    DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
1065    pw_file_unlock(lockfd,&pw_file_lock_depth);
1066    fclose(fp);
1067    return False;
1068  }
1069
1070  if ((linebuf[0] != ':') || (linebuf[wr_len] != ':'))	{
1071    DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
1072    pw_file_unlock(lockfd,&pw_file_lock_depth);
1073    fclose(fp);
1074    return False;
1075  }
1076
1077  if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
1078    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
1079    pw_file_unlock(lockfd,&pw_file_lock_depth);
1080    fclose(fp);
1081    return False;
1082  }
1083
1084  if (write(fd, ascii_p16, wr_len) != wr_len) {
1085    DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
1086    pw_file_unlock(lockfd,&pw_file_lock_depth);
1087    fclose(fp);
1088    return False;
1089  }
1090
1091  pw_file_unlock(lockfd,&pw_file_lock_depth);
1092  fclose(fp);
1093  return True;
1094}
1095
1096/************************************************************************
1097 Routine to delete an entry in the smbpasswd file by name.
1098*************************************************************************/
1099
1100static BOOL del_smbfilepwd_entry(const char *name)
1101{
1102  char *pfile = lp_smb_passwd_file();
1103  pstring pfile2;
1104  struct smb_passwd *pwd = NULL;
1105  FILE *fp = NULL;
1106  FILE *fp_write = NULL;
1107  int pfile2_lockdepth = 0;
1108
1109  slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)getpid() );
1110
1111  /*
1112   * Open the smbpassword file - for update. It needs to be update
1113   * as we need any other processes to wait until we have replaced
1114   * it.
1115   */
1116
1117  if((fp = startsmbfilepwent(True)) == NULL) {
1118    DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1119    return False;
1120  }
1121
1122  /*
1123   * Create the replacement password file.
1124   */
1125  if((fp_write = startsmbfilepwent_internal(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
1126    DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
1127    endsmbfilepwent(fp);
1128    return False;
1129  }
1130
1131  /*
1132   * Scan the file, a line at a time and check if the name matches.
1133   */
1134
1135  while ((pwd = getsmbfilepwent(fp)) != NULL) {
1136    char *new_entry;
1137    size_t new_entry_length;
1138
1139    if (strequal(name, pwd->smb_name)) {
1140      DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
1141      continue;
1142    }
1143
1144    /*
1145     * We need to copy the entry out into the second file.
1146     */
1147
1148    if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
1149      DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
1150Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1151      unlink(pfile2);
1152      endsmbfilepwent(fp);
1153      endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1154      return False;
1155    }
1156
1157    new_entry_length = strlen(new_entry);
1158
1159    if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
1160      DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
1161Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
1162      unlink(pfile2);
1163      endsmbfilepwent(fp);
1164      endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1165      free(new_entry);
1166      return False;
1167    }
1168
1169    free(new_entry);
1170  }
1171
1172  /*
1173   * Ensure pfile2 is flushed before rename.
1174   */
1175
1176  if(fflush(fp_write) != 0) {
1177    DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
1178    endsmbfilepwent(fp);
1179    endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1180    return False;
1181  }
1182
1183  /*
1184   * Do an atomic rename - then release the locks.
1185   */
1186
1187  if(rename(pfile2,pfile) != 0) {
1188    unlink(pfile2);
1189  }
1190  endsmbfilepwent(fp);
1191  endsmbfilepwent_internal(fp_write,&pfile2_lockdepth);
1192  return True;
1193}
1194
1195/*
1196 * Stub functions - implemented in terms of others.
1197 */
1198
1199static BOOL mod_smbfile21pwd_entry(struct sam_passwd* pwd, BOOL override)
1200{
1201 	return mod_smbfilepwd_entry(pdb_sam_to_smb(pwd), override);
1202}
1203
1204static BOOL add_smbfile21pwd_entry(struct sam_passwd *newpwd)
1205{
1206 	return add_smbfilepwd_entry(pdb_sam_to_smb(newpwd));
1207}
1208
1209static struct sam_disp_info *getsmbfiledispnam(char *name)
1210{
1211	return pdb_sam_to_dispinfo(getsam21pwnam(name));
1212}
1213
1214static struct sam_disp_info *getsmbfiledisprid(uint32 rid)
1215{
1216	return pdb_sam_to_dispinfo(getsam21pwrid(rid));
1217}
1218
1219static struct sam_disp_info *getsmbfiledispent(void *vp)
1220{
1221	return pdb_sam_to_dispinfo(getsam21pwent(vp));
1222}
1223
1224static struct passdb_ops file_ops = {
1225  startsmbfilepwent,
1226  endsmbfilepwent,
1227  getsmbfilepwpos,
1228  setsmbfilepwpos,
1229  iterate_getsmbpwnam,          /* In passdb.c */
1230  iterate_getsmbpwuid,          /* In passdb.c */
1231  iterate_getsmbpwrid,          /* In passdb.c */
1232  getsmbfilepwent,
1233  add_smbfilepwd_entry,
1234  mod_smbfilepwd_entry,
1235  del_smbfilepwd_entry,
1236  getsmbfile21pwent,
1237  iterate_getsam21pwnam,
1238  iterate_getsam21pwuid,
1239  iterate_getsam21pwrid,
1240  add_smbfile21pwd_entry,
1241  mod_smbfile21pwd_entry,
1242  getsmbfiledispnam,
1243  getsmbfiledisprid,
1244  getsmbfiledispent
1245};
1246
1247struct passdb_ops *file_initialize_password_db(void)
1248{
1249  return &file_ops;
1250}
1251
1252#else
1253 /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */
1254 void smbpass_dummy_function(void) { } /* stop some compilers complaining */
1255#endif /* USE_SMBPASS_DB */
1256