Coverage Report

Created: 2026-02-14 07:07

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