Coverage Report

Created: 2024-02-11 06:31

/src/proftpd/src/support.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2023 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26
27
/* Various basic support routines for ProFTPD, used by all modules
28
 * and not specific to one or another.
29
 */
30
31
#include "conf.h"
32
33
#ifdef PR_USE_OPENSSL
34
# include <openssl/crypto.h>
35
#endif /* PR_USE_OPENSSL */
36
37
/* Keep a counter of the number of times signals_block()/signals_unblock()
38
 * have been called, to handle nesting of calls.
39
 */
40
static unsigned int sigs_nblocked = 0;
41
42
typedef struct sched_obj {
43
  struct sched_obj *next, *prev;
44
45
  pool *pool;
46
  void (*cb)(void *, void *, void *, void *);
47
  int nloops;
48
  void *arg1, *arg2, *arg3, *arg4;
49
} sched_t;
50
51
static xaset_t *scheds = NULL;
52
53
/* Masks/unmasks all important signals (as opposed to blocking alarms)
54
 */
55
0
static void mask_signals(unsigned char block) {
56
0
  static sigset_t mask_sigset;
57
58
0
  if (block) {
59
0
    sigemptyset(&mask_sigset);
60
61
0
    sigaddset(&mask_sigset, SIGTERM);
62
0
    sigaddset(&mask_sigset, SIGCHLD);
63
0
    sigaddset(&mask_sigset, SIGUSR1);
64
0
    sigaddset(&mask_sigset, SIGINT);
65
0
    sigaddset(&mask_sigset, SIGQUIT);
66
0
    sigaddset(&mask_sigset, SIGALRM);
67
0
#ifdef SIGIO
68
0
    sigaddset(&mask_sigset, SIGIO);
69
0
#endif
70
0
#ifdef SIGBUS
71
0
    sigaddset(&mask_sigset, SIGBUS);
72
0
#endif
73
0
    sigaddset(&mask_sigset, SIGHUP);
74
75
0
    if (sigprocmask(SIG_BLOCK, &mask_sigset, NULL) < 0) {
76
0
      pr_log_pri(PR_LOG_NOTICE,
77
0
        "unable to block signal set: %s", strerror(errno));
78
0
    }
79
80
0
  } else {
81
0
    if (sigprocmask(SIG_UNBLOCK, &mask_sigset, NULL) < 0) {
82
0
      pr_log_pri(PR_LOG_NOTICE,
83
0
        "unable to unblock signal set: %s", strerror(errno));
84
0
    }
85
0
  }
86
0
}
87
88
0
void pr_signals_block(void) {
89
0
  if (sigs_nblocked == 0) {
90
0
    mask_signals(TRUE);
91
0
    pr_trace_msg("signal", 5, "signals blocked");
92
93
0
  } else {
94
0
    pr_trace_msg("signal", 9, "signals already blocked (block count = %u)",
95
0
      sigs_nblocked);
96
0
  }
97
98
0
  sigs_nblocked++;
99
0
}
100
101
0
void pr_signals_unblock(void) {
102
0
  if (sigs_nblocked == 0) {
103
0
    pr_trace_msg("signal", 5, "signals already unblocked");
104
0
    pr_signals_handle();
105
0
    return;
106
0
  }
107
108
0
  if (sigs_nblocked == 1) {
109
0
    mask_signals(FALSE);
110
0
    pr_trace_msg("signal", 5, "signals unblocked");
111
0
    pr_signals_handle();
112
113
0
  } else {
114
0
    pr_trace_msg("signal", 9, "signals already unblocked (block count = %u)",
115
0
      sigs_nblocked);
116
0
  }
117
118
0
  sigs_nblocked--;
119
0
}
120
121
void schedule(void (*cb)(void *, void *, void *, void *), int nloops,
122
0
    void *arg1, void *arg2, void *arg3, void *arg4) {
123
0
  pool *p, *sub_pool;
124
0
  sched_t *s;
125
126
0
  if (cb == NULL ||
127
0
      nloops < 0) {
128
0
    return;
129
0
  }
130
131
0
  if (scheds == NULL) {
132
0
    p = make_sub_pool(permanent_pool);
133
0
    pr_pool_tag(p, "Schedules Pool");
134
0
    scheds = xaset_create(p, NULL);
135
136
0
  } else {
137
0
    p = scheds->pool;
138
0
  }
139
140
0
  sub_pool = make_sub_pool(p);
141
0
  pr_pool_tag(sub_pool, "schedule pool");
142
143
0
  s = pcalloc(sub_pool, sizeof(sched_t));
144
0
  s->pool = sub_pool;
145
0
  s->cb = cb;
146
0
  s->arg1 = arg1;
147
0
  s->arg2 = arg2;
148
0
  s->arg3 = arg3;
149
0
  s->arg4 = arg4;
150
0
  s->nloops = nloops;
151
0
  xaset_insert(scheds, (xasetmember_t *) s);
152
0
}
153
154
0
void run_schedule(void) {
155
0
  sched_t *s, *snext;
156
157
0
  if (scheds == NULL ||
158
0
      scheds->xas_list == NULL) {
159
0
    return;
160
0
  }
161
162
0
  for (s = (sched_t *) scheds->xas_list; s; s = snext) {
163
0
    snext = s->next;
164
165
0
    pr_signals_handle();
166
167
0
    if (s->nloops-- <= 0) {
168
0
      s->cb(s->arg1, s->arg2, s->arg3, s->arg4);
169
0
      xaset_remove(scheds, (xasetmember_t *) s);
170
0
      destroy_pool(s->pool);
171
0
    }
172
0
  }
173
0
}
174
175
/* Get the maximum size of a file name (pathname component).
176
 * If a directory file descriptor, e.g. the d_fd DIR structure element,
177
 * is not available, the second argument should be 0.
178
 *
179
 * Note: a POSIX compliant system typically should NOT define NAME_MAX,
180
 * since the value almost certainly varies across different file system types.
181
 * Refer to POSIX 1003.1a, Section 2.9.5, Table 2-5.
182
 * Alas, current (Jul 2000) Linux systems define NAME_MAX anyway.
183
 * NB: NAME_MAX_GUESS is defined in support.h.
184
 */
185
186
0
static int get_fpathconf_name_max(int fd, long *name_max) {
187
0
#if defined(HAVE_FPATHCONF)
188
0
  *name_max = fpathconf(fd, _PC_NAME_MAX);
189
0
  return 0;
190
#else
191
  errno = ENOSYS;
192
  return -1;
193
#endif /* HAVE_FPATHCONF */
194
0
}
195
196
0
static int get_pathconf_name_max(char *dir, long *name_max) {
197
0
#if defined(HAVE_PATHCONF)
198
0
  *name_max = pathconf(dir, _PC_NAME_MAX);
199
0
  return 0;
200
#else
201
  errno = ENOSYS;
202
  return -1;
203
#endif /* HAVE_PATHCONF */
204
0
}
205
206
0
long get_name_max(char *dir_name, int dir_fd) {
207
0
  int res;
208
0
  long name_max = 0;
209
210
0
  if (dir_name == NULL &&
211
0
      dir_fd < 0) {
212
0
    errno = EINVAL;
213
0
    return -1;
214
0
  }
215
216
  /* Try the fd first. */
217
0
  if (dir_fd >= 0) {
218
0
    res = get_fpathconf_name_max(dir_fd, &name_max);
219
0
    if (res == 0) {
220
0
      if (name_max < 0) {
221
0
        int xerrno = errno;
222
223
0
        pr_log_debug(DEBUG5, "fpathconf() error for fd %d: %s", dir_fd,
224
0
          strerror(xerrno));
225
226
0
        errno = xerrno;
227
0
        return -1;
228
0
      }
229
230
0
      return name_max;
231
0
    }
232
0
  }
233
234
  /* Name, then. */
235
0
  if (dir_name != NULL) {
236
0
    res = get_pathconf_name_max(dir_name, &name_max);
237
0
    if (res == 0) {
238
0
      if (name_max < 0) {
239
0
        int xerrno = errno;
240
241
0
        pr_log_debug(DEBUG5, "pathconf() error for name '%s': %s", dir_name,
242
0
          strerror(xerrno));
243
244
0
        errno = xerrno;
245
0
        return -1;
246
0
      }
247
248
0
      return name_max;
249
0
    }
250
0
  }
251
252
0
  errno = ENOSYS;
253
0
  return -1;
254
0
}
255
256
/* Interpolates a pathname, expanding ~ notation if necessary
257
 */
258
0
char *dir_interpolate(pool *p, const char *path) {
259
0
  char *res = NULL;
260
261
0
  if (p == NULL ||
262
0
      path == NULL) {
263
0
    errno = EINVAL;
264
0
    return NULL;
265
0
  }
266
267
0
  if (*path == '~') {
268
0
    char *ptr, *user;
269
270
0
    user = pstrdup(p, path + 1);
271
0
    ptr = strchr(user, '/');
272
0
    if (ptr != NULL) {
273
0
      *ptr++ = '\0';
274
0
    }
275
276
0
    if (user[0] == '\0') {
277
0
      user = (char *) session.user;
278
0
    }
279
280
0
    if (session.user != NULL &&
281
0
        strcmp(user, session.user) == 0 &&
282
0
        session.user_homedir != NULL) {
283
0
      res = pdircat(p, session.user_homedir, ptr, NULL);
284
285
0
    } else {
286
0
      struct passwd *pw;
287
288
0
      pw = pr_auth_getpwnam(p, user);
289
0
      if (pw == NULL) {
290
0
        errno = ENOENT;
291
0
        return NULL;
292
0
      }
293
294
0
      res = pdircat(p, pw->pw_dir, ptr, NULL);
295
0
    }
296
297
0
  } else {
298
0
    res = pstrdup(p, path);
299
0
  }
300
301
0
  return res;
302
0
}
303
304
/* dir_best_path() creates the "most" fully canonicalized path possible
305
 * (i.e. if path components at the end don't exist, they are ignored).
306
 */
307
0
char *dir_best_path(pool *p, const char *path) {
308
0
  char workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
309
0
  char realpath_buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
310
0
  char *target = NULL, *ntarget;
311
0
  int fini = 0;
312
313
0
  if (p == NULL ||
314
0
      path == NULL) {
315
0
    errno = EINVAL;
316
0
    return NULL;
317
0
  }
318
319
0
  if (*path == '~') {
320
0
    if (pr_fs_interpolate(path, workpath, sizeof(workpath)-1) != 1) {
321
0
      if (pr_fs_dircat(workpath, sizeof(workpath), pr_fs_getcwd(), path) < 0) {
322
0
        return NULL;
323
0
      }
324
0
    }
325
326
0
  } else {
327
0
    if (pr_fs_dircat(workpath, sizeof(workpath), pr_fs_getcwd(), path) < 0) {
328
0
      return NULL;
329
0
    }
330
0
  }
331
332
0
  pr_fs_clean_path(pstrdup(p, workpath), workpath, sizeof(workpath)-1);
333
334
0
  while (!fini && *workpath) {
335
0
    pr_signals_handle();
336
337
0
    if (pr_fs_resolve_path(workpath, realpath_buf,
338
0
        sizeof(realpath_buf)-1, 0) != -1) {
339
0
      break;
340
0
    }
341
342
0
    ntarget = strrchr(workpath, '/');
343
0
    if (ntarget) {
344
0
      if (target) {
345
0
        if (pr_fs_dircat(workpath, sizeof(workpath), workpath, target) < 0) {
346
0
          return NULL;
347
0
        }
348
0
      }
349
350
0
      target = ntarget;
351
0
      *target++ = '\0';
352
353
0
    } else {
354
0
      fini++;
355
0
    }
356
0
  }
357
358
0
  if (!fini && *workpath) {
359
0
    if (target) {
360
0
      if (pr_fs_dircat(workpath, sizeof(workpath), realpath_buf, target) < 0) {
361
0
        return NULL;
362
0
      }
363
364
0
    } else {
365
0
      sstrncpy(workpath, realpath_buf, sizeof(workpath));
366
0
    }
367
368
0
  } else {
369
0
    if (pr_fs_dircat(workpath, sizeof(workpath), "/", target) < 0) {
370
0
      return NULL;
371
0
    }
372
0
  }
373
374
0
  return pstrdup(p, workpath);
375
0
}
376
377
0
char *dir_canonical_path(pool *p, const char *path) {
378
0
  char buf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
379
0
  char work[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
380
381
0
  if (p == NULL ||
382
0
      path == NULL) {
383
0
    errno = EINVAL;
384
0
    return NULL;
385
0
  }
386
387
0
  if (*path == '~') {
388
0
    if (pr_fs_interpolate(path, work, sizeof(work)-1) != 1) {
389
0
      if (pr_fs_dircat(work, sizeof(work), pr_fs_getcwd(), path) < 0) {
390
0
        return NULL;
391
0
      }
392
0
    }
393
394
0
  } else {
395
0
    if (pr_fs_dircat(work, sizeof(work), pr_fs_getcwd(), path) < 0) {
396
0
      return NULL;
397
0
    }
398
0
  }
399
400
0
  pr_fs_clean_path(work, buf, sizeof(buf)-1);
401
0
  return pstrdup(p, buf);
402
0
}
403
404
0
char *dir_canonical_vpath(pool *p, const char *path) {
405
0
  char buf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
406
0
  char work[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
407
408
0
  if (p == NULL ||
409
0
      path == NULL) {
410
0
    errno = EINVAL;
411
0
    return NULL;
412
0
  }
413
414
0
  if (*path == '~') {
415
0
    if (pr_fs_interpolate(path, work, sizeof(work)-1) != 1) {
416
0
      if (pr_fs_dircat(work, sizeof(work), pr_fs_getvwd(), path) < 0) {
417
0
        return NULL;
418
0
      }
419
0
    }
420
421
0
  } else {
422
0
    if (pr_fs_dircat(work, sizeof(work), pr_fs_getvwd(), path) < 0) {
423
0
      return NULL;
424
0
    }
425
0
  }
426
427
0
  pr_fs_clean_path(work, buf, sizeof(buf)-1);
428
0
  return pstrdup(p, buf);
429
0
}
430
431
/* Performs chroot-aware handling of symlinks. */
432
int dir_readlink(pool *p, const char *path, char *buf, size_t bufsz,
433
0
    int flags) {
434
0
  int is_abs_dst, clean_flags, len, res = -1;
435
0
  size_t chroot_pathlen = 0, adj_pathlen = 0;
436
0
  char *dst_path, *adj_path;
437
0
  pool *tmp_pool;
438
439
0
  if (p == NULL ||
440
0
      path == NULL ||
441
0
      buf == NULL) {
442
0
    errno = EINVAL;
443
0
    return -1;
444
0
  }
445
446
0
  if (bufsz == 0) {
447
0
    return 0;
448
0
  }
449
450
0
  len = pr_fsio_readlink(path, buf, bufsz);
451
0
  if (len < 0) {
452
0
    return -1;
453
0
  }
454
455
0
  pr_trace_msg("fsio", 9,
456
0
    "dir_readlink() read link '%.*s' for path '%s'", (int) len, buf, path);
457
458
0
  if (len == 0 ||
459
0
      (size_t) len == bufsz) {
460
    /* If we read nothing in, OR if the given buffer was completely
461
     * filled WITHOUT terminating NUL, there's really nothing we can/should
462
     * be doing.
463
     */
464
0
    return len;
465
0
  }
466
467
0
  is_abs_dst = FALSE;
468
0
  if (*buf == '/') {
469
0
    is_abs_dst = TRUE;
470
0
  }
471
472
0
  if (session.chroot_path != NULL) {
473
0
    chroot_pathlen = strlen(session.chroot_path);
474
0
  }
475
476
0
  if (chroot_pathlen <= 1) {
477
0
    char *ptr;
478
479
0
    if (is_abs_dst == TRUE ||
480
0
        !(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
481
0
      return len;
482
0
    }
483
484
    /* Since we have a relative destination path, we will concat it
485
     * with the source path's directory, then clean up that path.
486
     */
487
0
    ptr = strrchr(path, '/');
488
0
    if (ptr != NULL &&
489
0
        ptr != path) {
490
0
      char *parent_dir;
491
492
0
      tmp_pool = make_sub_pool(p);
493
0
      pr_pool_tag(tmp_pool, "dir_readlink pool");
494
495
0
      parent_dir = pstrndup(tmp_pool, path, (ptr - path));
496
0
      dst_path = pdircat(tmp_pool, parent_dir, buf, NULL);
497
498
0
      adj_pathlen = bufsz + 1;
499
0
      adj_path = pcalloc(tmp_pool, adj_pathlen);
500
501
0
      res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, 0);
502
0
      if (res == 0) {
503
0
        pr_trace_msg("fsio", 19,
504
0
          "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
505
0
        dst_path = adj_path;
506
0
      }
507
508
0
      pr_trace_msg("fsio", 19,
509
0
        "adjusted relative symlink path '%s', yielding '%s'", buf, dst_path);
510
511
0
      memset(buf, '\0', bufsz);
512
0
      sstrncpy(buf, dst_path, bufsz);
513
0
      len = strlen(buf);
514
0
      destroy_pool(tmp_pool);
515
0
    }
516
517
0
    return len;
518
0
  }
519
520
0
  if (is_abs_dst == FALSE) {
521
    /* If we are to ignore relative destination paths, return now. */
522
0
    if (!(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
523
0
      return len;
524
0
    }
525
0
  }
526
527
0
  if (is_abs_dst == TRUE &&
528
0
      (size_t) len < chroot_pathlen) {
529
    /* If the destination path length is shorter than the chroot path,
530
     * AND the destination path is absolute, then by definition it CANNOT
531
     * point within the chroot.
532
     */
533
0
    return len;
534
0
  }
535
536
0
  tmp_pool = make_sub_pool(p);
537
0
  pr_pool_tag(tmp_pool, "dir_readlink pool");
538
539
0
  dst_path = pstrdup(tmp_pool, buf);
540
0
  if (is_abs_dst == FALSE) {
541
0
    char *ptr;
542
543
    /* Since we have a relative destination path, we will concat it
544
     * with the source path's directory, then clean up that path.
545
     */
546
547
0
    ptr = strrchr(path, '/');
548
0
    if (ptr != NULL) {
549
0
      if (ptr != path) {
550
0
        char *parent_dir;
551
552
0
        parent_dir = pstrndup(tmp_pool, path, (ptr - path));
553
0
        dst_path = pdircat(tmp_pool, parent_dir, dst_path, NULL);
554
555
0
      } else {
556
0
        dst_path = pdircat(tmp_pool, "/", dst_path, NULL);
557
0
      }
558
0
    }
559
0
  }
560
561
0
  adj_pathlen = bufsz + 1;
562
0
  adj_path = pcalloc(tmp_pool, adj_pathlen);
563
564
0
  clean_flags = PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH;
565
0
  res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, clean_flags);
566
0
  if (res == 0) {
567
0
    pr_trace_msg("fsio", 19,
568
0
      "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
569
0
    dst_path = adj_path;
570
571
0
    memset(buf, '\0', bufsz);
572
0
    sstrncpy(buf, dst_path, bufsz);
573
0
    len = strlen(dst_path);
574
0
  }
575
576
0
  if (strncmp(dst_path, session.chroot_path, chroot_pathlen) == 0 &&
577
0
      *(dst_path + chroot_pathlen) == '/') {
578
0
    char *ptr;
579
580
0
    ptr = dst_path + chroot_pathlen;
581
582
0
    if (is_abs_dst == FALSE &&
583
0
        res == 0) {
584
      /* If we originally had a relative destination path, AND we cleaned
585
       * that adjusted path, then we should try to re-adjust the path
586
       * back to being a relative path.  Within reason.
587
       */
588
0
      ptr = pstrcat(tmp_pool, ".", ptr, NULL);
589
0
    }
590
591
    /* Since we are making the destination path shorter, the given buffer
592
     * (which was big enough for the original destination path) should
593
     * always be large enough for this adjusted, shorter version.  Right?
594
     */
595
0
    pr_trace_msg("fsio", 19,
596
0
      "adjusted symlink path '%s' for chroot '%s', yielding '%s'",
597
0
      dst_path, session.chroot_path, ptr);
598
599
0
    memset(buf, '\0', bufsz);
600
0
    sstrncpy(buf, ptr, bufsz);
601
0
    len = strlen(buf);
602
0
  }
603
604
0
  destroy_pool(tmp_pool);
605
0
  return len;
606
0
}
607
608
/* dir_realpath() is needed to properly dereference symlinks (getcwd() may
609
 * not work if permissions cause problems somewhere up the tree).
610
 */
611
0
char *dir_realpath(pool *p, const char *path) {
612
0
  char buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
613
614
0
  if (p == NULL ||
615
0
      path == NULL) {
616
0
    errno = EINVAL;
617
0
    return NULL;
618
0
  }
619
620
0
  if (pr_fs_resolve_partial(path, buf, sizeof(buf)-1, 0) < 0) {
621
0
    return NULL;
622
0
  }
623
624
0
  return pstrdup(p, buf);
625
0
}
626
627
/* Takes a directory and returns its absolute version.  ~username references
628
 * are appropriately interpolated.  "Absolute" includes a _full_ reference
629
 * based on the root directory, not upon a chrooted dir.
630
 */
631
0
char *dir_abs_path(pool *p, const char *path, int interpolate) {
632
0
  char *res = NULL;
633
634
0
  if (p == NULL ||
635
0
      path == NULL) {
636
0
    errno = EINVAL;
637
0
    return NULL;
638
0
  }
639
640
0
  if (interpolate) {
641
0
    char buf[PR_TUNABLE_PATH_MAX+1];
642
643
0
    memset(buf, '\0', sizeof(buf));
644
0
    switch (pr_fs_interpolate(path, buf, sizeof(buf)-1)) {
645
0
      case -1:
646
0
        return NULL;
647
648
0
      case 0:
649
        /* Do nothing; path exists */
650
0
        break;
651
652
0
      case 1:
653
        /* Interpolation occurred; make a copy of the interpolated path. */
654
0
        path = pstrdup(p, buf);
655
0
        break;
656
0
    }
657
0
  }
658
659
0
  if (*path != '/') {
660
0
    if (session.chroot_path) {
661
0
      res = pdircat(p, session.chroot_path, pr_fs_getcwd(), path, NULL);
662
663
0
    } else {
664
0
      res = pdircat(p, pr_fs_getcwd(), path, NULL);
665
0
    }
666
667
0
  } else {
668
0
    if (session.chroot_path) {
669
0
      if (strncmp(path, session.chroot_path,
670
0
          strlen(session.chroot_path)) != 0) {
671
0
        res = pdircat(p, session.chroot_path, path, NULL);
672
673
0
      } else {
674
0
        res = pstrdup(p, path);
675
0
      }
676
677
0
    } else {
678
0
      res = pstrdup(p, path);
679
0
    }
680
0
  }
681
682
0
  return res;
683
0
}
684
685
/* Return the mode (including the file type) of the file pointed to by symlink
686
 * PATH, or 0 if it doesn't exist. Catch symlink loops using LAST_INODE and
687
 * RCOUNT.
688
 */
689
static mode_t _symlink(pool *p, const char *path, ino_t last_inode,
690
0
    int rcount) {
691
0
  char buf[PR_TUNABLE_PATH_MAX + 1];
692
0
  struct stat st;
693
0
  int i;
694
695
0
  if (++rcount >= PR_FSIO_MAX_LINK_COUNT) {
696
0
    errno = ELOOP;
697
0
    return 0;
698
0
  }
699
700
0
  memset(buf, '\0', sizeof(buf));
701
702
0
  if (p != NULL) {
703
0
    i = dir_readlink(p, path, buf, sizeof(buf)-1,
704
0
      PR_DIR_READLINK_FL_HANDLE_REL_PATH);
705
0
  } else {
706
0
    i = pr_fsio_readlink(path, buf, sizeof(buf)-1);
707
0
  }
708
709
0
  if (i < 0) {
710
0
    return (mode_t) 0;
711
0
  }
712
0
  buf[i] = '\0';
713
714
0
  pr_fs_clear_cache2(buf);
715
0
  if (pr_fsio_lstat(buf, &st) >= 0) {
716
0
    if (st.st_ino > 0 &&
717
0
        (ino_t) st.st_ino == last_inode) {
718
0
      errno = ELOOP;
719
0
      return 0;
720
0
    }
721
722
0
    if (S_ISLNK(st.st_mode)) {
723
0
      return _symlink(p, buf, (ino_t) st.st_ino, rcount);
724
0
    }
725
726
0
    return st.st_mode;
727
0
  }
728
729
0
  return 0;
730
0
}
731
732
0
mode_t symlink_mode2(pool *p, const char *path) {
733
0
  if (path == NULL) {
734
0
    errno = EINVAL;
735
0
    return 0;
736
0
  }
737
738
0
  return _symlink(p, path, (ino_t) 0, 0);
739
0
}
740
741
0
mode_t symlink_mode(const char *path) {
742
0
  return symlink_mode2(NULL, path);
743
0
}
744
745
0
mode_t file_mode2(pool *p, const char *path) {
746
0
  struct stat st;
747
0
  mode_t mode = 0;
748
749
0
  if (path == NULL) {
750
0
    errno = EINVAL;
751
0
    return mode;
752
0
  }
753
754
0
  pr_fs_clear_cache2(path);
755
0
  if (pr_fsio_lstat(path, &st) >= 0) {
756
0
    if (S_ISLNK(st.st_mode)) {
757
0
      mode = _symlink(p, path, (ino_t) 0, 0);
758
0
      if (mode == 0) {
759
  /* a dangling symlink, but it exists to rename or delete. */
760
0
  mode = st.st_mode;
761
0
      }
762
763
0
    } else {
764
0
      mode = st.st_mode;
765
0
    }
766
0
  }
767
768
0
  return mode;
769
0
}
770
771
0
mode_t file_mode(const char *path) {
772
0
  return file_mode2(NULL, path);
773
0
}
774
775
/* If flags == 1, fail unless PATH is an existing directory.
776
 * If flags == 0, fail unless PATH is an existing non-directory.
777
 * If flags == -1, fail unless PATH exists; the caller doesn't care whether
778
 * PATH is a file or a directory.
779
 */
780
0
static int _exists(pool *p, const char *path, int flags) {
781
0
  mode_t mode;
782
783
0
  mode = file_mode2(p, path);
784
0
  if (mode != 0) {
785
0
    switch (flags) {
786
0
      case 1:
787
0
        if (!S_ISDIR(mode)) {
788
0
          return FALSE;
789
0
        }
790
0
        break;
791
792
0
      case 0:
793
0
        if (S_ISDIR(mode)) {
794
0
          return FALSE;
795
0
        }
796
0
        break;
797
798
0
      default:
799
0
        break;
800
0
    }
801
802
0
    return TRUE;
803
0
  }
804
805
0
  return FALSE;
806
0
}
807
808
0
int file_exists2(pool *p, const char *path) {
809
0
  return _exists(p, path, 0);
810
0
}
811
812
0
int file_exists(const char *path) {
813
0
  return file_exists2(NULL, path);
814
0
}
815
816
0
int dir_exists2(pool *p, const char *path) {
817
0
  return _exists(p, path, 1);
818
0
}
819
820
0
int dir_exists(const char *path) {
821
0
  return dir_exists2(NULL, path);
822
0
}
823
824
0
int exists2(pool *p, const char *path) {
825
0
  return _exists(p, path, -1);
826
0
}
827
828
0
int exists(const char *path) {
829
0
  return exists2(NULL, path);
830
0
}
831
832
/* safe_token tokenizes a string, and increments the pointer to
833
 * the next non-white space character.  It's "safe" because it
834
 * never returns NULL, only an empty string if no token remains
835
 * in the source string.
836
 */
837
0
char *safe_token(char **s) {
838
0
  char *res = "";
839
840
0
  if (s == NULL ||
841
0
      !*s) {
842
0
    return res;
843
0
  }
844
845
0
  while (PR_ISSPACE(**s) && **s) {
846
0
    (*s)++;
847
0
  }
848
849
0
  if (**s) {
850
0
    res = *s;
851
852
0
    while (!PR_ISSPACE(**s) && **s) {
853
0
      (*s)++;
854
0
    }
855
856
0
    if (**s) {
857
0
      *(*s)++ = '\0';
858
0
    }
859
860
0
    while (PR_ISSPACE(**s) && **s) {
861
0
      (*s)++;
862
0
    }
863
0
  }
864
865
0
  return res;
866
0
}
867
868
/* Checks for the existence of PR_SHUTMSG_PATH.  deny and disc are
869
 * filled with the times to deny new connections and disconnect
870
 * existing ones.
871
 */
872
int check_shutmsg(pool *p, const char *path, time_t *shut, time_t *deny,
873
0
    time_t *disc, char *msg, size_t msg_size) {
874
0
  FILE *fp;
875
0
  char *deny_str, *disc_str, *cp, buf[PR_TUNABLE_BUFFER_SIZE+1] = {'\0'};
876
0
  char hr[3] = {'\0'}, mn[3] = {'\0'};
877
0
  time_t now, shuttime = (time_t) 0;
878
0
  struct stat st;
879
0
  struct tm *tm;
880
881
0
  if (path == NULL) {
882
0
    errno = EINVAL;
883
0
    return -1;
884
0
  }
885
886
0
  fp = fopen(path, "r");
887
0
  if (fp == NULL) {
888
0
    return -1;
889
0
  }
890
891
0
  if (fstat(fileno(fp), &st) == 0) {
892
0
    if (S_ISDIR(st.st_mode)) {
893
0
      fclose(fp);
894
0
      errno = EISDIR;
895
0
      return -1;
896
0
    }
897
0
  }
898
899
0
  cp = fgets(buf, sizeof(buf), fp);
900
0
  if (cp != NULL) {
901
0
    buf[sizeof(buf)-1] = '\0'; CHOP(cp);
902
903
    /* We use this to fill in dst, timezone, etc */
904
0
    time(&now);
905
0
    tm = pr_localtime(p, &now);
906
0
    if (tm == NULL) {
907
0
      fclose(fp);
908
0
      return 0;
909
0
    }
910
911
0
    tm->tm_year = atoi(safe_token(&cp)) - 1900;
912
0
    tm->tm_mon = atoi(safe_token(&cp)) - 1;
913
0
    tm->tm_mday = atoi(safe_token(&cp));
914
0
    tm->tm_hour = atoi(safe_token(&cp));
915
0
    tm->tm_min = atoi(safe_token(&cp));
916
0
    tm->tm_sec = atoi(safe_token(&cp));
917
918
0
    deny_str = safe_token(&cp);
919
0
    disc_str = safe_token(&cp);
920
921
0
    shuttime = mktime(tm);
922
0
    if (shuttime == (time_t) -1) {
923
0
      fclose(fp);
924
0
      return 0;
925
0
    }
926
927
0
    if (deny != NULL) {
928
0
      if (strlen(deny_str) == 4) {
929
0
        sstrncpy(hr, deny_str, sizeof(hr));
930
0
        hr[2] = '\0';
931
0
        deny_str += 2;
932
933
0
        sstrncpy(mn, deny_str, sizeof(mn));
934
0
        mn[2] = '\0';
935
936
0
        *deny = shuttime - ((atoi(hr) * 3600) + (atoi(mn) * 60));
937
938
0
      } else {
939
0
        *deny = shuttime;
940
0
      }
941
0
    }
942
943
0
    if (disc != NULL) {
944
0
      if (strlen(disc_str) == 4) {
945
0
        sstrncpy(hr, disc_str, sizeof(hr));
946
0
        hr[2] = '\0';
947
0
        disc_str += 2;
948
949
0
        sstrncpy(mn, disc_str, sizeof(mn));
950
0
        mn[2] = '\0';
951
952
0
        *disc = shuttime - ((atoi(hr) * 3600) + (atoi(mn) * 60));
953
954
0
      } else {
955
0
        *disc = shuttime;
956
0
      }
957
0
    }
958
959
0
    if (fgets(buf, sizeof(buf), fp) && msg) {
960
0
      buf[sizeof(buf)-1] = '\0';
961
0
      CHOP(buf);
962
0
      sstrncpy(msg, buf, msg_size-1);
963
0
    }
964
0
  }
965
966
0
  fclose(fp);
967
968
0
  if (shut != NULL) {
969
0
    *shut = shuttime;
970
0
  }
971
972
0
  return 1;
973
0
}
974
975
#if !defined(PR_USE_OPENSSL) || OPENSSL_VERSION_NUMBER <= 0x000907000L
976
/* "safe" memset() (code borrowed from OpenSSL).  This function should be
977
 * used to clear/scrub sensitive memory areas instead of memset() for the
978
 * reasons mentioned in this BugTraq thread:
979
 *
980
 *  http://online.securityfocus.com/archive/1/298598
981
 */
982
983
static unsigned char memscrub_ctr = 0;
984
#endif
985
986
0
void pr_memscrub(void *ptr, size_t ptrlen) {
987
#if defined(PR_USE_OPENSSL) && OPENSSL_VERSION_NUMBER > 0x000907000L
988
  if (ptr == NULL ||
989
      ptrlen == 0) {
990
    return;
991
  }
992
993
  /* Just use OpenSSL's function for this.  They have optimized it for
994
   * performance in later OpenSSL releases.
995
   */
996
  OPENSSL_cleanse(ptr, ptrlen);
997
998
#else
999
0
  unsigned char *p;
1000
0
  size_t loop;
1001
1002
0
  if (ptr == NULL ||
1003
0
      ptrlen == 0) {
1004
0
    return;
1005
0
  }
1006
1007
0
  p = ptr;
1008
0
  loop = ptrlen;
1009
1010
0
  while (loop--) {
1011
0
    *(p++) = memscrub_ctr++;
1012
0
    memscrub_ctr += (17 + (unsigned char)((intptr_t) p & 0xF));
1013
0
  }
1014
1015
0
  if (memchr(ptr, memscrub_ctr, ptrlen)) {
1016
0
    memscrub_ctr += 63;
1017
0
  }
1018
0
#endif
1019
0
}
1020
1021
0
void pr_getopt_reset(void) {
1022
#if defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \
1023
    defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \
1024
    defined(FREEBSD10) || defined(FREEBSD11) || defined(FREEBSD12) || \
1025
    defined(FREEBSD13) || \
1026
    defined(DARWIN7) || defined(DARWIN8) || defined(DARWIN9) || \
1027
    defined(DARWIN10) || defined(DARWIN11) || defined(DARWIN12) || \
1028
    defined(DARWIN13) || defined(DARWIN14) || defined(DARWIN15) || \
1029
    defined(DARWIN16) || defined(DARWIN17) || defined(DARWIN18)
1030
  optreset = 1;
1031
  opterr = 1;
1032
  optind = 1;
1033
1034
#elif defined(SOLARIS2) || defined(HPUX11)
1035
  opterr = 0;
1036
  optind = 1;
1037
1038
#else
1039
0
  opterr = 0;
1040
0
  optind = 0;
1041
0
#endif /* !FreeBSD, !Mac OSX and !Solaris2 */
1042
1043
0
  if (pr_env_get(permanent_pool, "POSIXLY_CORRECT") == NULL) {
1044
0
    pr_env_set(permanent_pool, "POSIXLY_CORRECT", "1");
1045
0
  }
1046
0
}
1047
1048
0
struct tm *pr_gmtime(pool *p, const time_t *now) {
1049
0
  struct tm *sys_tm, *dup_tm;
1050
1051
0
  if (now == NULL) {
1052
0
    errno = EINVAL;
1053
0
    return NULL;
1054
0
  }
1055
1056
0
#if defined(HAVE_GMTIME_R)
1057
0
  if (p == NULL) {
1058
0
    errno = EINVAL;
1059
0
    return NULL;
1060
0
  }
1061
1062
0
  sys_tm = gmtime_r(now, pcalloc(p, sizeof(struct tm)));
1063
#else
1064
  sys_tm = gmtime(now);
1065
#endif /* HAVE_GMTIME_R */
1066
0
  if (sys_tm == NULL) {
1067
0
    return NULL;
1068
0
  }
1069
1070
  /* If the caller provided a pool, make a copy of the struct tm using that
1071
   * pool.  Otherwise, return the struct tm as is.
1072
   */
1073
0
  if (p != NULL) {
1074
0
    dup_tm = pcalloc(p, sizeof(struct tm));
1075
0
    memcpy(dup_tm, sys_tm, sizeof(struct tm));
1076
1077
0
  } else {
1078
0
    dup_tm = sys_tm;
1079
0
  }
1080
1081
0
  return dup_tm;
1082
0
}
1083
1084
0
struct tm *pr_localtime(pool *p, const time_t *now) {
1085
0
  struct tm *sys_tm, *dup_tm;
1086
1087
0
#ifdef HAVE_TZNAME
1088
0
  char *tzname_dup[2];
1089
1090
  /* The localtime(3) function has a nasty habit of changing the tzname
1091
   * global variable as a side-effect.  This can cause problems, as when
1092
   * the process has become chrooted, and localtime(3) sets/changes
1093
   * tzname wrong.  (For more information on the tzname global variable,
1094
   * see the tzset(3) man page.)
1095
   *
1096
   * The best way to deal with this issue (which is especially prominent
1097
   * on systems running glibc-2.3 or later, which is particularly ill-behaved
1098
   * in a chrooted environment, as it assumes the ability to find system
1099
   * timezone files at paths which are no longer valid within the chroot)
1100
   * is to set the TZ environment variable explicitly, before starting
1101
   * proftpd.  You can also use the SetEnv configuration directive within
1102
   * the proftpd.conf to set the TZ environment variable, e.g.:
1103
   *
1104
   *  SetEnv TZ PST
1105
   *
1106
   * To try to help sites which fail to do this, the tzname global variable
1107
   * will be copied prior to the localtime(3) call, and the copy restored
1108
   * after the call.  (Note that calling the ctime(3) and mktime(3)
1109
   * functions also causes a similar overwriting/setting of the tzname
1110
   * environment variable.)
1111
   *
1112
   * This hack is also used in the lib/pr-syslog.c code, to work around
1113
   * mktime(3) antics.
1114
   */
1115
0
  memcpy(&tzname_dup, tzname, sizeof(tzname_dup));
1116
0
#endif /* HAVE_TZNAME */
1117
1118
0
  if (now == NULL) {
1119
0
    errno = EINVAL;
1120
0
    return NULL;
1121
0
  }
1122
1123
0
#if defined(HAVE_LOCALTIME_R)
1124
0
  if (p == NULL) {
1125
0
    errno = EINVAL;
1126
0
    return NULL;
1127
0
  }
1128
1129
0
  sys_tm = localtime_r(now, pcalloc(p, sizeof(struct tm)));
1130
#else
1131
  sys_tm = localtime(now);
1132
#endif /* HAVE_LOCALTIME_R */
1133
0
  if (sys_tm == NULL) {
1134
0
    return NULL;
1135
0
  }
1136
1137
0
  if (p != NULL) {
1138
    /* If the caller provided a pool, make a copy of the returned
1139
     * struct tm, allocated out of that pool.
1140
     */
1141
0
    dup_tm = pcalloc(p, sizeof(struct tm));
1142
0
    memcpy(dup_tm, sys_tm, sizeof(struct tm));
1143
1144
0
  } else {
1145
1146
    /* Other callers do not require pool-allocated copies, and instead
1147
     * are happy with the struct tm as is.
1148
     */
1149
0
    dup_tm = sys_tm;
1150
0
  }
1151
1152
0
#if defined(HAVE_TZNAME)
1153
  /* Restore the old tzname values prior to returning. */
1154
0
  memcpy(tzname, tzname_dup, sizeof(tzname_dup));
1155
0
#endif /* HAVE_TZNAME */
1156
1157
0
  return dup_tm;
1158
0
}
1159
1160
0
const char *pr_strtime(time_t t) {
1161
0
  return pr_strtime2(t, FALSE);
1162
0
}
1163
1164
0
const char *pr_strtime2(time_t t, int use_gmtime) {
1165
0
  return pr_strtime3(NULL, t, use_gmtime);
1166
0
}
1167
1168
0
const char *pr_strtime3(pool *p, time_t t, int use_gmtime) {
1169
0
  static char buf[64];
1170
0
  static char *mons[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
1171
0
    "Aug", "Sep", "Oct", "Nov", "Dec" };
1172
0
  static char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
1173
0
  struct tm *tr;
1174
1175
0
  memset(buf, '\0', sizeof(buf));
1176
1177
0
  if (use_gmtime) {
1178
0
    tr = pr_gmtime(p, &t);
1179
1180
0
  } else {
1181
0
    tr = pr_localtime(p, &t);
1182
0
  }
1183
1184
0
  if (tr == NULL) {
1185
0
    return NULL;
1186
0
  }
1187
1188
0
  pr_snprintfl(__FILE__, __LINE__, buf, sizeof(buf),
1189
0
    "%s %s %02d %02d:%02d:%02d %d", days[tr->tm_wday], mons[tr->tm_mon],
1190
0
    tr->tm_mday, tr->tm_hour, tr->tm_min, tr->tm_sec, tr->tm_year + 1900);
1191
0
  buf[sizeof(buf)-1] = '\0';
1192
1193
0
  if (p != NULL) {
1194
0
    return pstrdup(p, buf);
1195
0
  }
1196
1197
0
  return buf;
1198
0
}
1199
1200
0
int pr_timeval2millis(struct timeval *tv, uint64_t *millis) {
1201
0
  if (tv == NULL ||
1202
0
      millis == NULL) {
1203
0
    errno = EINVAL;
1204
0
    return -1;
1205
0
  }
1206
1207
  /* Make sure to use 64-bit multiplication to avoid overflow errors,
1208
   * as much as we can.
1209
   */
1210
0
  *millis = (tv->tv_sec * (uint64_t) 1000) + (tv->tv_usec / (uint64_t) 1000);
1211
0
  return 0;
1212
0
}
1213
1214
0
int pr_gettimeofday_millis(uint64_t *millis) {
1215
0
  struct timeval tv;
1216
1217
0
  if (gettimeofday(&tv, NULL) < 0) {
1218
0
    return -1;
1219
0
  }
1220
1221
0
  if (pr_timeval2millis(&tv, millis) < 0) {
1222
0
    return -1;
1223
0
  }
1224
1225
0
  return 0;
1226
0
}
1227
1228
int pr_vsnprintfl(const char *file, int lineno, char *buf, size_t bufsz,
1229
0
    const char *fmt, va_list msg) {
1230
0
  int res, xerrno = 0;
1231
1232
0
  if (buf == NULL ||
1233
0
      fmt == NULL) {
1234
0
    errno = EINVAL;
1235
0
    return -1;
1236
0
  }
1237
1238
0
  if (bufsz == 0) {
1239
0
    return 0;
1240
0
  }
1241
1242
0
  res = vsnprintf(buf, bufsz, fmt, msg);
1243
0
  xerrno = errno;
1244
1245
0
  if (res < 0) {
1246
    /* Unexpected error. */
1247
1248
0
#ifdef EOVERFLOW
1249
0
    if (xerrno == EOVERFLOW) {
1250
0
      xerrno = ENOSPC;
1251
0
    }
1252
0
#endif /* EOVERFLOW */
1253
1254
0
  } else if ((size_t) res >= bufsz) {
1255
    /* Buffer too small. */
1256
0
    xerrno = ENOSPC;
1257
0
    res = -1;
1258
0
  }
1259
1260
  /* We are mostly concerned with tracking down the locations of truncated
1261
   * buffers, hence the stacktrace logging only for these conditions.
1262
   */
1263
0
  if (res < 0 &&
1264
0
      xerrno == ENOSPC) {
1265
0
    if (file != NULL &&
1266
0
        lineno > 0) {
1267
0
      pr_log_pri(PR_LOG_WARNING,
1268
0
        "%s:%d: error writing format string '%s' into %lu-byte buffer: %s",
1269
0
        file, lineno, fmt, (unsigned long) bufsz, strerror(xerrno));
1270
1271
0
    } else {
1272
0
      pr_log_pri(PR_LOG_WARNING,
1273
0
        "error writing format string '%s' into %lu-byte buffer: %s", fmt,
1274
0
        (unsigned long) bufsz, strerror(xerrno));
1275
0
    }
1276
1277
0
    pr_log_stacktrace(-1, NULL);
1278
0
  }
1279
1280
0
  errno = xerrno;
1281
0
  return res;
1282
0
}
1283
1284
0
int pr_vsnprintf(char *buf, size_t bufsz, const char *fmt, va_list msg) {
1285
0
  return pr_vsnprintfl(NULL, -1, buf, bufsz, fmt, msg);
1286
0
}
1287
1288
int pr_snprintfl(const char *file, int lineno, char *buf, size_t bufsz,
1289
0
    const char *fmt, ...) {
1290
0
  va_list msg;
1291
0
  int res;
1292
1293
0
  va_start(msg, fmt);
1294
0
  res = pr_vsnprintfl(file, lineno, buf, bufsz, fmt, msg);
1295
0
  va_end(msg);
1296
1297
0
  return res;
1298
0
}
1299
1300
0
int pr_snprintf(char *buf, size_t bufsz, const char *fmt, ...) {
1301
0
  va_list msg;
1302
0
  int res;
1303
1304
0
  va_start(msg, fmt);
1305
0
  res = pr_vsnprintfl(NULL, -1, buf, bufsz, fmt, msg);
1306
0
  va_end(msg);
1307
1308
0
  return res;
1309
0
}
1310
1311
/* Substitute any appearance of the %u variable in the given string with
1312
 * the value.
1313
 */
1314
0
const char *path_subst_uservar(pool *path_pool, const char **path) {
1315
0
  const char *new_path = NULL, *substr_path = NULL;
1316
0
  char *substr = NULL;
1317
0
  size_t user_len = 0;
1318
1319
  /* Sanity check. */
1320
0
  if (path_pool == NULL ||
1321
0
      path == NULL ||
1322
0
      !*path) {
1323
0
    errno = EINVAL;
1324
0
    return NULL;
1325
0
  }
1326
1327
  /* If no %u string present, do nothing. */
1328
0
  if (strstr(*path, "%u") == NULL) {
1329
0
    return *path;
1330
0
  }
1331
1332
  /* Same if there is no user set yet. */
1333
0
  if (session.user == NULL) {
1334
0
    return *path;
1335
0
  }
1336
1337
0
  user_len = strlen(session.user);
1338
1339
  /* First, deal with occurrences of "%u[index]" strings.  Note that
1340
   * with this syntax, the '[' and ']' characters become invalid in paths,
1341
   * but only if that '[' appears after a "%u" string -- certainly not
1342
   * a common phenomenon (I hope).  This means that in the future, an escape
1343
   * mechanism may be needed in this function.  Caveat emptor.
1344
   */
1345
1346
0
  substr_path = *path;
1347
0
  substr = substr_path ? strstr(substr_path, "%u[") : NULL;
1348
0
  while (substr != NULL) {
1349
0
    long i = 0;
1350
0
    char *substr_end = NULL, *substr_dup = NULL, *endp = NULL;
1351
0
    char ref_char[2] = {'\0', '\0'};
1352
1353
0
    pr_signals_handle();
1354
1355
    /* Now, find the closing ']'. If not found, it is a syntax error;
1356
     * continue on without processing this occurrence.
1357
     */
1358
0
    substr_end = strchr(substr, ']');
1359
0
    if (substr_end == NULL) {
1360
      /* Just end here. */
1361
0
      break;
1362
0
    }
1363
1364
    /* Make a copy of the entire substring. */
1365
0
    substr_dup = pstrdup(path_pool, substr);
1366
1367
    /* The substr_end variable (used as an index) should work here, too
1368
     * (trying to obtain the entire substring).
1369
     */
1370
0
    substr_dup[substr_end - substr + 1] = '\0';
1371
1372
    /* Advance the substring pointer by three characters, so that it is
1373
     * pointing at the character after the '['.
1374
     */
1375
0
    substr += 3;
1376
1377
    /* If the closing ']' is the next character after the opening '[', it
1378
     * is a syntax error.
1379
     */
1380
0
    if (*substr == ']') {
1381
0
      substr_path = *path;
1382
0
      break;
1383
0
    }
1384
1385
    /* Temporarily set the ']' to '\0', to make it easy for the string
1386
     * scanning below.
1387
     */
1388
0
    *substr_end = '\0';
1389
1390
    /* Scan the index string into a number, watching for bad strings. */
1391
0
    i = strtol(substr, &endp, 10);
1392
0
    if (endp && *endp) {
1393
0
      *substr_end = ']';
1394
0
      pr_trace_msg("auth", 3,
1395
0
        "invalid index number syntax found in '%s', ignoring", substr);
1396
0
      return *path;
1397
0
    }
1398
1399
    /* Make sure that index is within bounds. */
1400
0
    if (i < 0 ||
1401
0
        (size_t) i > user_len - 1) {
1402
1403
      /* Put the closing ']' back. */
1404
0
      *substr_end = ']';
1405
1406
0
      if (i < 0) {
1407
0
        pr_trace_msg("auth", 3,
1408
0
          "out-of-bounds index number (%ld) found in '%s', ignoring", i,
1409
0
          substr);
1410
1411
0
      } else {
1412
0
        pr_trace_msg("auth", 3,
1413
0
          "out-of-bounds index number (%ld > %lu) found in '%s', ignoring", i,
1414
0
          (unsigned long) user_len-1, substr);
1415
0
      }
1416
1417
0
      return *path;
1418
0
    }
1419
1420
0
    ref_char[0] = session.user[i];
1421
1422
    /* Put the closing ']' back. */
1423
0
    *substr_end = ']';
1424
1425
    /* Now, to substitute the whole "%u[index]" substring with the
1426
     * referenced character/string.
1427
     */
1428
0
    substr_path = sreplace(path_pool, substr_path, substr_dup, ref_char, NULL);
1429
0
    substr = substr_path ? strstr(substr_path, "%u[") : NULL;
1430
0
  }
1431
1432
  /* Check for any bare "%u", and handle those if present. */
1433
0
  if (substr_path &&
1434
0
      strstr(substr_path, "%u") != NULL) {
1435
0
    new_path = sreplace(path_pool, substr_path, "%u", session.user, NULL);
1436
1437
0
  } else {
1438
0
    new_path = substr_path;
1439
0
  }
1440
1441
0
  return new_path;
1442
0
}
1443