• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 28531645955

01 Jul 2026 04:14PM UTC coverage: 92.468% (-0.6%) from 93.032%
28531645955

push

github

web-flow
Exercise caution when reading the client-provided file size for SCP uploads, as it could possibly overflow our size type.

Thanks to Fabian Wahle of Hap Security for reporting this issue.

48701 of 52668 relevant lines covered (92.47%)

227.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.4
/src/ctrls.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2001-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Controls API routines */
25

26
#include "conf.h"
27
#include "privs.h"
28

29
#if defined(HAVE_UCRED_H)
30
# include <ucred.h>
31
#endif /* !HAVE_UCRED_H */
32

33
#if defined(HAVE_SYS_UCRED_H)
34
# include <sys/ucred.h>
35
#endif /* !HAVE_SYS_UCRED_H */
36

37
#if defined(HAVE_SYS_UIO_H)
38
# include <sys/uio.h>
39
#endif /* !HAVE_SYS_UIO_H */
40

41
#if defined(PR_USE_CTRLS)
42

43
#include "mod_ctrls.h"
44

45
#define CTRLS_MAX_REQ_SIZE        1024
46
#define CTRLS_MAX_RESP_SIZE        (1024 * 1024)
47

48
#define CTRLS_REQ_ACTION_KEY        "action"
49
#define CTRLS_REQ_ARGS_KEY        "args"
50
#define CTRLS_RESP_STATUS_KEY        "status"
51
#define CTRLS_RESP_RESPS_KEY        "responses"
52

53
typedef struct ctrls_act_obj {
54
  struct ctrls_act_obj *prev, *next;
55
  pool *pool;
56
  unsigned int id;
57
  const char *action;
58
  const char *desc;
59
  const module *module;
60
  volatile unsigned int flags;
61
  int (*action_cb)(pr_ctrls_t *, int, char **);
62
} ctrls_action_t;
63

64
static unsigned char ctrls_blocked = FALSE;
65

66
static pool *ctrls_pool = NULL;
67
static ctrls_action_t *ctrls_action_list = NULL;
68

69
static pr_ctrls_t *ctrls_active_list = NULL;
70
static pr_ctrls_t *ctrls_free_list = NULL;
71

72
static int ctrls_use_isfifo = FALSE;
73

74
static const char *trace_channel = "ctrls";
75

76
/* lookup/lookup_next indices */
77
static ctrls_action_t *action_lookup_next = NULL;
78
static const char *action_lookup_action = NULL;
79
static module *action_lookup_module = NULL;
80

81
/* Logging */
82
static int ctrls_logfd = -1;
83

84
/* necessary prototypes */
85
static ctrls_action_t *ctrls_action_new(void);
86
static pr_ctrls_t *ctrls_lookup_action(module *, const char *, unsigned char);
87
static pr_ctrls_t *ctrls_lookup_next_action(module *, unsigned char);
88

89
static pr_ctrls_t *ctrls_prepare(ctrls_action_t *act) {
7✔
90
  pr_ctrls_t *ctrl = NULL;
7✔
91

92
  pr_block_ctrls();
93

94
  /* Get a blank ctrl object */
95
  ctrl = pr_ctrls_alloc();
7✔
96

97
  /* Fill in the fields from the action object. */
98
  ctrl->ctrls_id = act->id;
7✔
99
  ctrl->ctrls_module = act->module;
7✔
100
  ctrl->ctrls_action = act->action;
7✔
101
  ctrl->ctrls_desc = act->desc;
7✔
102
  ctrl->ctrls_cb = act->action_cb;
7✔
103
  ctrl->ctrls_flags = act->flags;
7✔
104

105
  /* Add this to the "in use" list */
106
  ctrl->ctrls_next = ctrls_active_list;
7✔
107
  ctrls_active_list = ctrl;
7✔
108

109
  pr_unblock_ctrls();
110
  return ctrl;
7✔
111
}
112

113
static ctrls_action_t *ctrls_action_new(void) {
12✔
114
  ctrls_action_t *act = NULL;
12✔
115
  pool *sub_pool = NULL;
12✔
116

117
  sub_pool = make_sub_pool(ctrls_pool);
12✔
118
  pr_pool_tag(sub_pool, "ctrls action subpool");
12✔
119

120
  act = pcalloc(sub_pool, sizeof(ctrls_action_t));
12✔
121
  act->pool = sub_pool;
12✔
122

123
  return act;
12✔
124
}
125

126
pr_ctrls_t *pr_ctrls_alloc(void) {
27✔
127
  pr_ctrls_t *ctrl = NULL;
27✔
128

129
  /* Check for a free ctrl first */
130
  if (ctrls_free_list != NULL) {
27✔
131

132
    /* Take one from the top */
133
    ctrl = ctrls_free_list;
5✔
134
    ctrls_free_list = ctrls_free_list->ctrls_next;
5✔
135

136
    if (ctrls_free_list != NULL) {
5✔
137
      ctrls_free_list->ctrls_prev = NULL;
2✔
138
    }
139

140
  } else {
141
    /* Have to allocate a new one. */
142
    ctrl = (pr_ctrls_t *) pcalloc(ctrls_pool, sizeof(pr_ctrls_t));
22✔
143

144
    /* It's important that a new ctrl object have the retval initialized
145
     * to 1; this tells the Controls layer that it is "pending", not yet
146
     * handled.
147
     */
148
    ctrl->ctrls_cb_retval = PR_CTRLS_STATUS_PENDING;
22✔
149
  }
150

151
  return ctrl;
27✔
152
}
153

154
int pr_ctrls_free(pr_ctrls_t *ctrl) {
9✔
155
  if (ctrl == NULL) {
9✔
156
    errno = EINVAL;
1✔
157
    return -1;
1✔
158
  }
159

160
  /* Make sure that ctrls are blocked while we're doing this */
161
  pr_block_ctrls();
162

163
  /* Remove this object from the active list */
164
  if (ctrl->ctrls_prev != NULL) {
8✔
165
    ctrl->ctrls_prev->ctrls_next = ctrl->ctrls_next;
×
166

167
  } else {
168
    ctrls_active_list = ctrl->ctrls_next;
8✔
169
  }
170

171
  if (ctrl->ctrls_next != NULL) {
8✔
172
    ctrl->ctrls_next->ctrls_prev = ctrl->ctrls_prev;
2✔
173
  }
174

175
  /* Clear its fields, and add it to the free list */
176
  ctrl->ctrls_next = NULL;
8✔
177
  ctrl->ctrls_prev = NULL;
8✔
178
  ctrl->ctrls_id = 0;
8✔
179
  ctrl->ctrls_module = NULL;
8✔
180
  ctrl->ctrls_action = NULL;
8✔
181
  ctrl->ctrls_cb = NULL;
8✔
182
  ctrl->ctrls_cb_retval = PR_CTRLS_STATUS_PENDING;
8✔
183
  ctrl->ctrls_flags = 0;
8✔
184

185
  if (ctrl->ctrls_tmp_pool != NULL) {
8✔
186
    destroy_pool(ctrl->ctrls_tmp_pool);
4✔
187
    ctrl->ctrls_tmp_pool = NULL;
4✔
188
  }
189

190
  ctrl->ctrls_cb_args = NULL;
8✔
191
  ctrl->ctrls_cb_resps = NULL;
8✔
192
  ctrl->ctrls_data = NULL;
8✔
193

194
  ctrl->ctrls_next = ctrls_free_list;
8✔
195
  ctrls_free_list = ctrl;
8✔
196

197
  pr_unblock_ctrls();
198
  return 0;
8✔
199
}
200

201
int pr_ctrls_register(const module *mod, const char *action,
15✔
202
    const char *desc, int (*cb)(pr_ctrls_t *, int, char **)) {
203
  ctrls_action_t *act = NULL, *acti = NULL;
15✔
204
  unsigned int act_id = 0;
15✔
205

206
  /* sanity checks */
207
  if (action == NULL ||
30✔
208
      desc == NULL ||
28✔
209
      cb == NULL) {
210
    errno = EINVAL;
3✔
211
    return -1;
3✔
212
  }
213

214
  pr_trace_msg("ctrls", 3,
12✔
215
    "module '%s' registering handler for ctrl action '%s' (at %p)",
216
    mod ? mod->name : "(none)", action, cb);
217

218
  /* Block ctrls while we're doing this */
219
  pr_block_ctrls();
220

221
  /* Get a ctrl action object */
222
  act = ctrls_action_new();
12✔
223

224
  /* Randomly generate a unique random ID for this object */
225
  while (TRUE) {
226
    int have_id = FALSE;
12✔
227

228
    act_id = (unsigned int) pr_random_next(1L, RAND_MAX);
12✔
229

230
    /* Check the list for this ID */
231
    for (acti = ctrls_action_list; acti; acti = acti->next) {
16✔
232
      if (acti->id == act_id) {
4✔
233
        have_id = TRUE;
234
        break;
235
      }
236
    }
237

238
    if (have_id == FALSE) {
12✔
239
      break;
240
    }
241
  }
242

243
  act->next = NULL;
12✔
244
  act->id = act_id;
12✔
245
  act->action = pstrdup(ctrls_pool, action);
12✔
246
  act->desc = desc;
12✔
247
  act->module = mod;
12✔
248
  act->action_cb = cb;
12✔
249

250
  /* Add this to the list of "registered" actions */
251

252
  if (ctrls_action_list != NULL) {
12✔
253
    act->next = ctrls_action_list;
4✔
254
    ctrls_action_list->prev = act;
4✔
255
  }
256

257
  ctrls_action_list = act;
12✔
258

259
  pr_unblock_ctrls();
260
  return act_id;
12✔
261
}
262

263
int pr_ctrls_unregister(module *mod, const char *action) {
15✔
264
  ctrls_action_t *act = NULL, *next_act = NULL;
15✔
265
  unsigned char have_action = FALSE;
15✔
266

267
  /* Make sure that ctrls are blocked while we're doing this */
268
  pr_block_ctrls();
269

270
  for (act = ctrls_action_list; act != NULL; act = next_act) {
42✔
271
    next_act = act->next;
12✔
272

273
    if ((action == NULL || strcmp(act->action, action) == 0) &&
24✔
274
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
15✔
275
      have_action = TRUE;
12✔
276

277
      /* Remove this object from the list of registered actions */
278
      if (act->prev != NULL) {
12✔
279
        act->prev->next = act->next;
×
280

281
      } else {
282
        ctrls_action_list = act->next;
12✔
283
      }
284

285
      if (act->next != NULL) {
12✔
286
        act->next->prev = act->prev;
4✔
287
      }
288

289
      pr_trace_msg("ctrls", 3,
12✔
290
        "module '%s' unregistering handler for ctrl action '%s'",
291
        mod ? mod->name : "(none)", act->action);
292

293
      /* Destroy this action. */
294
      destroy_pool(act->pool);
12✔
295
    }
296
  }
297

298
  pr_unblock_ctrls();
299

300
  if (have_action == FALSE) {
15✔
301
    errno = ENOENT;
7✔
302
    return -1;
7✔
303
  }
304

305
  return 0;
306
}
307

308
int pr_ctrls_add_arg(pr_ctrls_t *ctrl, char *ctrls_arg, size_t ctrls_arglen) {
11✔
309
  register unsigned int i;
310

311
  /* Sanity checks */
312
  if (ctrl == NULL ||
22✔
313
      ctrls_arg == NULL) {
11✔
314
    errno = EINVAL;
2✔
315
    return -1;
2✔
316
  }
317

318
  /* Scan for non-printable characters. */
319
  for (i = 0; i < ctrls_arglen; i++) {
26✔
320
    if (!PR_ISPRINT((int) ctrls_arg[i])) {
27✔
321
      errno = EPERM;
1✔
322
      return -1;
1✔
323
    }
324
  }
325

326
  /* Make sure the pr_ctrls_t has a temporary pool, from which the args will
327
   * be allocated.
328
   */
329
  if (ctrl->ctrls_tmp_pool == NULL) {
8✔
330
    ctrl->ctrls_tmp_pool = make_sub_pool(ctrls_pool);
6✔
331
    pr_pool_tag(ctrl->ctrls_tmp_pool, "ctrls tmp pool");
6✔
332
  }
333

334
  if (ctrl->ctrls_cb_args == NULL) {
8✔
335
    ctrl->ctrls_cb_args = make_array(ctrl->ctrls_tmp_pool, 0, sizeof(char *));
6✔
336
  }
337

338
  /* Add the given argument */
339
  *((char **) push_array(ctrl->ctrls_cb_args)) = pstrndup(ctrl->ctrls_tmp_pool,
8✔
340
    ctrls_arg, ctrls_arglen);
341

342
  return 0;
8✔
343
}
344

345
int pr_ctrls_copy_args(pr_ctrls_t *src_ctrl, pr_ctrls_t *dst_ctrl) {
6✔
346
  if (src_ctrl == NULL ||
12✔
347
      dst_ctrl == NULL ||
10✔
348
      src_ctrl == dst_ctrl) {
349
    errno = EINVAL;
3✔
350
    return -1;
3✔
351
  }
352

353
  /* If source ctrl has no ctrls_cb_args member, there's nothing to be
354
   * done.
355
   */
356
  if (src_ctrl->ctrls_cb_args == NULL) {
3✔
357
    return 0;
358
  }
359

360
  /* Make sure the pr_ctrls_t has a temporary pool, from which the args will
361
   * be allocated.
362
   */
363
  if (dst_ctrl->ctrls_tmp_pool == NULL) {
2✔
364
    dst_ctrl->ctrls_tmp_pool = make_sub_pool(ctrls_pool);
2✔
365
    pr_pool_tag(dst_ctrl->ctrls_tmp_pool, "ctrls tmp pool");
2✔
366
  }
367

368
  /* Overwrite any existing dst_ctrl->ctrls_cb_args.  This is OK, as
369
   * the ctrl will be reset (cleared) once it has been processed.
370
   */
371
  dst_ctrl->ctrls_cb_args = copy_array(dst_ctrl->ctrls_tmp_pool,
2✔
372
    src_ctrl->ctrls_cb_args);
2✔
373

374
  return 0;
2✔
375
}
376

377
int pr_ctrls_copy_resps(pr_ctrls_t *src_ctrl, pr_ctrls_t *dst_ctrl) {
6✔
378
  if (src_ctrl == NULL ||
12✔
379
      dst_ctrl == NULL ||
10✔
380
      src_ctrl == dst_ctrl) {
381
    errno = EINVAL;
3✔
382
    return -1;
3✔
383
  }
384

385
  /* The source ctrl must have a ctrls_cb_resps member, and the destination
386
   * ctrl must not have a ctrls_cb_resps member.
387
   */
388
  if (src_ctrl->ctrls_cb_resps == NULL ||
5✔
389
      dst_ctrl->ctrls_cb_resps != NULL) {
2✔
390
    errno = EPERM;
2✔
391
    return -1;
2✔
392
  }
393

394
  dst_ctrl->ctrls_cb_resps = copy_array(dst_ctrl->ctrls_tmp_pool,
1✔
395
    src_ctrl->ctrls_cb_resps);
396

397
  return 0;
1✔
398
}
399

400
int pr_ctrls_add_response(pr_ctrls_t *ctrl, const char *fmt, ...) {
14✔
401
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
14✔
402
  va_list resp;
403

404
  /* Sanity check */
405
  if (ctrl == NULL ||
28✔
406
      fmt == NULL) {
14✔
407
    errno = EINVAL;
2✔
408
    return -1;
2✔
409
  }
410

411
  /* Make sure the pr_ctrls_t has a temporary pool, from which the responses
412
   * will be allocated
413
   */
414
  if (ctrl->ctrls_tmp_pool == NULL) {
12✔
415
    ctrl->ctrls_tmp_pool = make_sub_pool(ctrls_pool);
6✔
416
    pr_pool_tag(ctrl->ctrls_tmp_pool, "ctrls tmp pool");
6✔
417
  }
418

419
  if (ctrl->ctrls_cb_resps == NULL) {
12✔
420
    ctrl->ctrls_cb_resps = make_array(ctrl->ctrls_tmp_pool, 0,
7✔
421
      sizeof(char *));
422
  }
423

424
  /* Affix the message */
425
  va_start(resp, fmt);
12✔
426
  pr_vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, resp);
12✔
427
  va_end(resp);
12✔
428

429
  buf[sizeof(buf) - 1] = '\0';
12✔
430

431
  /* add the given response */
432
  *((char **) push_array(ctrl->ctrls_cb_resps)) =
24✔
433
    pstrdup(ctrl->ctrls_tmp_pool, buf);
24✔
434

435
  return 0;
12✔
436
}
437

438
int pr_ctrls_flush_response(pr_ctrls_t *ctrl) {
5✔
439
  if (ctrl == NULL) {
5✔
440
    errno = EINVAL;
1✔
441
    return -1;
1✔
442
  }
443

444
  /* Make sure the callback(s) added responses */
445
  if (ctrl->ctrls_cb_resps != NULL) {
4✔
446
    int res;
447

448
    if (ctrl->ctrls_cl == NULL) {
3✔
449
      errno = EPERM;
1✔
450
      return -1;
1✔
451
    }
452

453
    res = pr_ctrls_send_response(ctrl->ctrls_tmp_pool, ctrl->ctrls_cl->cl_fd,
2✔
454
      ctrl->ctrls_cb_retval, ctrl->ctrls_cb_resps->nelts,
455
      (char **) ctrl->ctrls_cb_resps->elts);
2✔
456
    if (res < 0) {
2✔
457
      return -1;
458
    }
459
  }
460

461
  return 0;
462
}
463

464
static int ctrls_send_msg(pool *p, int fd, pr_json_object_t *json) {
7✔
465
  uint32_t msglen;
466
  int res, xerrno;
467
  char *msg;
468

469
  msg = pr_json_object_to_text(p, json, "");
7✔
470
  if (msg == NULL) {
7✔
471
    return -1;
472
  }
473

474
  msglen = strlen(msg);
7✔
475

476
  /* No interruptions. */
477
  pr_signals_block();
7✔
478

479
  pr_trace_msg(trace_channel, 27,
7✔
480
    "sending Controls message (%lu bytes) to fd %d", (unsigned long) msglen,
481
    fd);
482

483
  res = write(fd, &msglen, sizeof(uint32_t));
7✔
484
  xerrno = errno;
7✔
485

486
  if ((size_t) res != sizeof(uint32_t)) {
7✔
487
    pr_signals_unblock();
3✔
488

489
    errno = xerrno;
3✔
490
    return -1;
3✔
491
  }
492

493
  while (TRUE) {
494
    res = write(fd, msg, msglen);
4✔
495
    xerrno = errno;
4✔
496

497
    if ((size_t) res != msglen) {
4✔
498
      if (xerrno == EAGAIN) {
×
499
        continue;
×
500
      }
501

502
      pr_signals_unblock();
×
503

504
      errno = xerrno;
×
505
      return -1;
×
506
    }
507

508
    break;
509
  }
510

511
  pr_signals_unblock();
4✔
512
  return 0;
4✔
513
}
514

515
int pr_ctrls_send_request(pool *p, int fd, const char *action,
6✔
516
    unsigned int argc, char **argv) {
517
  register unsigned int i;
518
  pool *tmp_pool;
519
  int res, xerrno;
520
  pr_json_object_t *json;
521
  pr_json_array_t *args;
522

523
  if (p == NULL ||
12✔
524
      fd < 0 ||
10✔
525
      action == NULL) {
526
    errno = EINVAL;
3✔
527
    return -1;
3✔
528
  }
529

530
  if (argc > 0 &&
6✔
531
      argv == NULL) {
3✔
532
    errno = EINVAL;
1✔
533
    return -1;
1✔
534
  }
535

536
  tmp_pool = make_sub_pool(p);
2✔
537
  pr_pool_tag(tmp_pool, "Controls API send_request pool");
2✔
538

539
  json = pr_json_object_alloc(tmp_pool);
2✔
540

541
  res = pr_json_object_set_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY, action);
2✔
542
  xerrno = errno;
2✔
543

544
  if (res < 0) {
2✔
545
    pr_json_object_free(json);
×
546
    destroy_pool(tmp_pool);
×
547

548
    errno = xerrno;
×
549
    return -1;
×
550
  }
551

552
  args = pr_json_array_alloc(tmp_pool);
2✔
553

554
  for (i = 0; i < argc; i++) {
4✔
555
    res = pr_json_array_append_string(tmp_pool, args, argv[i]);
2✔
556
    xerrno = errno;
2✔
557

558
    if (res < 0) {
2✔
559
      pr_json_array_free(args);
×
560
      pr_json_object_free(json);
×
561
      destroy_pool(tmp_pool);
×
562

563
      errno = xerrno;
×
564
      return -1;
×
565
    }
566
  }
567

568
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, args);
2✔
569
  xerrno = errno;
2✔
570

571
  if (res < 0) {
2✔
572
    pr_json_array_free(args);
×
573
    pr_json_object_free(json);
×
574
    destroy_pool(tmp_pool);
×
575

576
    errno = xerrno;
×
577
    return -1;
×
578
  }
579

580
  res = ctrls_send_msg(tmp_pool, fd, json);
2✔
581
  xerrno = errno;
2✔
582

583
  pr_json_array_free(args);
2✔
584
  pr_json_object_free(json);
2✔
585
  destroy_pool(tmp_pool);
2✔
586

587
  errno = xerrno;
2✔
588
  return res;
2✔
589
}
590

591
int pr_ctrls_recv_request(pr_ctrls_cl_t *cl) {
16✔
592
  register int i = 0;
16✔
593
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
16✔
594
  pool *tmp_pool = NULL;
16✔
595
  int nread, nreqargs = 0, res, xerrno;
16✔
596
  uint32_t msglen;
597
  char *msg = NULL, *reqaction = NULL;
16✔
598
  pr_json_object_t *json = NULL;
16✔
599
  pr_json_array_t *args = NULL;
16✔
600

601
  if (cl == NULL ||
31✔
602
      cl->cl_ctrls == NULL) {
15✔
603
    errno = EINVAL;
2✔
604
    return -1;
2✔
605
  }
606

607
  if (cl->cl_fd < 0) {
14✔
608
    errno = EBADF;
1✔
609
    return -1;
1✔
610
  }
611

612
  /* No interruptions */
613
  pr_signals_block();
13✔
614

615
  /* Read in the size of the message, as JSON text. */
616

617
  nread = read(cl->cl_fd, &msglen, sizeof(uint32_t));
26✔
618
  xerrno = errno;
13✔
619

620
  if (nread < 0) {
13✔
621
    pr_signals_unblock();
1✔
622

623
    pr_trace_msg(trace_channel, 3,
1✔
624
      "error reading %lu bytes of request message size: %s",
625
      sizeof(msglen), strerror(xerrno));
626
    errno = xerrno;
1✔
627
    return -1;
1✔
628
  }
629

630
  /* Watch for short reads. */
631
  if (nread != sizeof(uint32_t)) {
12✔
632
    pr_signals_unblock();
1✔
633

634
    (void) pr_trace_msg(trace_channel, 3,
1✔
635
      "short read (%d of %u bytes) of message size, unable to receive request",
636
      nread, (unsigned int) sizeof(uint32_t));
637
    errno = EPERM;
1✔
638
    return -1;
1✔
639
  }
640

641
  tmp_pool = make_sub_pool(cl->cl_pool);
11✔
642
  pr_pool_tag(tmp_pool, "Controls API recv_request pool");
11✔
643

644
  pr_trace_msg(trace_channel, 27,
11✔
645
    "receiving Controls request message (%lu bytes) from fd %d",
646
    (unsigned long) msglen, cl->cl_fd);
647

648
  /* Impose max request size limit here, for Issue #2036. */
649
  if (msglen > CTRLS_MAX_REQ_SIZE) {
11✔
650
    destroy_pool(tmp_pool);
1✔
651
    pr_signals_unblock();
1✔
652

653
    (void) pr_trace_msg(trace_channel, 3,
1✔
654
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
655
      "request", (unsigned long) msglen, (unsigned long) CTRLS_MAX_REQ_SIZE);
656
    errno = E2BIG;
1✔
657
    return -1;
1✔
658
  }
659

660
  /* Allocate one byte for the terminating NUL. */
661
  msg = pcalloc(tmp_pool, msglen + 1);
10✔
662

663
  nread = read(cl->cl_fd, msg, msglen);
20✔
664
  xerrno = errno;
10✔
665

666
  if (nread < 0) {
10✔
667
    destroy_pool(tmp_pool);
×
668
    pr_signals_unblock();
×
669

670
    pr_trace_msg(trace_channel, 3,
×
671
      "error reading %lu bytes of request message: %s",
672
      (unsigned long) msglen, strerror(xerrno));
673
    errno = xerrno;
×
674
    return -1;
×
675
  }
676

677
  /* Watch for short reads. */
678
  if ((unsigned int) nread != msglen) {
10✔
679
    destroy_pool(tmp_pool);
1✔
680
    pr_signals_unblock();
1✔
681

682
    (void) pr_trace_msg(trace_channel, 3,
1✔
683
      "short read (%d of %u bytes) of message text, unable to receive request",
684
      nread, (unsigned int) msglen);
685
    errno = EPERM;
1✔
686
    return -1;
1✔
687
  }
688

689
  json = pr_json_object_from_text(tmp_pool, msg);
9✔
690
  xerrno = errno;
9✔
691

692
  if (json == NULL) {
9✔
693
    destroy_pool(tmp_pool);
1✔
694
    pr_signals_unblock();
1✔
695

696
    (void) pr_trace_msg(trace_channel, 3,
1✔
697
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
698
      "receive request: %s", (int) msglen, msg, (unsigned long) msglen,
699
      strerror(xerrno));
700
    errno = EINVAL;
1✔
701
    return -1;
1✔
702
  }
703

704
  res = pr_json_object_get_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY,
8✔
705
    &reqaction);
706
  xerrno = errno;
8✔
707

708
  if (res < 0) {
8✔
709
    pr_json_object_free(json);
1✔
710
    destroy_pool(tmp_pool);
1✔
711
    pr_signals_unblock();
1✔
712

713
    (void) pr_trace_msg(trace_channel, 3,
1✔
714
      "unable to read message action (%s), unable to receive request",
715
      strerror(xerrno));
716
    errno = EINVAL;
1✔
717
    return -1;
1✔
718
  }
719

720
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, &args);
7✔
721
  xerrno = errno;
7✔
722

723
  if (res < 0) {
7✔
724
    pr_json_object_free(json);
1✔
725
    destroy_pool(tmp_pool);
1✔
726
    pr_signals_unblock();
1✔
727

728
    (void) pr_trace_msg(trace_channel, 3,
1✔
729
      "unable to read message arguments (%s), unable to receive request",
730
      strerror(xerrno));
731
    errno = EINVAL;
1✔
732
    return -1;
1✔
733
  }
734

735
  nreqargs = pr_json_array_count(args);
6✔
736
  pr_trace_msg(trace_channel, 19, "received request argc: %u", nreqargs);
6✔
737

738
  /* Find a matching action object, and use it to populate a ctrl object,
739
   * preparing the ctrl object for dispatching to the action handlers.
740
   */
741
  ctrl = ctrls_lookup_action(NULL, reqaction, TRUE);
12✔
742
  if (ctrl == NULL) {
6✔
743
    (void) pr_trace_msg(trace_channel, 3,
1✔
744
      "unknown action requested '%s', unable to receive request", reqaction);
745
    pr_json_array_free(args);
1✔
746
    pr_json_object_free(json);
1✔
747
    destroy_pool(tmp_pool);
1✔
748
    pr_signals_unblock();
1✔
749

750
    /* XXX This is where we could also add "did you mean" functionality. */
751
    errno = EINVAL;
1✔
752
    return -1;
1✔
753
  }
754

755
  pr_trace_msg(trace_channel, 19, "known action '%s' requested", reqaction);
5✔
756

757
  for (i = 0; i < nreqargs; i++) {
14✔
758
    size_t reqarglen = 0;
4✔
759
    char *reqarg = NULL;
4✔
760

761
    res = pr_json_array_get_string(tmp_pool, args, i, &reqarg);
4✔
762
    xerrno = errno;
4✔
763

764
    if (res < 0) {
4✔
765
      (void) pr_trace_msg(trace_channel, 3,
×
766
        "unable to read message argument #%u (%s), unable to receive request",
767
        i+1, strerror(xerrno));
768
      pr_json_array_free(args);
×
769
      pr_json_object_free(json);
×
770
      destroy_pool(tmp_pool);
×
771
      pr_signals_unblock();
×
772

773
      errno = EINVAL;
×
774
      return -1;
×
775
    }
776

777
    reqarglen = strlen(reqarg);
4✔
778
    res = pr_ctrls_add_arg(ctrl, reqarg, reqarglen);
4✔
779
    xerrno = errno;
4✔
780

781
    if (res < 0) {
4✔
782
      pr_trace_msg(trace_channel, 3,
×
783
        "error adding message argument #%u (%s): %s", i+1, reqarg,
784
        strerror(xerrno));
785
      pr_json_array_free(args);
×
786
      pr_json_object_free(json);
×
787
      destroy_pool(tmp_pool);
×
788
      pr_signals_unblock();
×
789

790
      errno = xerrno;
×
791
      return -1;
×
792
    }
793
  }
794

795
  /* Add this ctrls object to the client object. */
796
  *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = ctrl;
5✔
797

798
  /* Set the flag that this control is ready to go */
799
  ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
5✔
800
  ctrl->ctrls_cl = cl;
5✔
801

802
  /* Copy the populated ctrl object args to ctrl objects for all other
803
   * matching action objects.
804
   */
805
  next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
5✔
806

807
  while (next_ctrl != NULL) {
11✔
808
    (void) pr_ctrls_copy_args(ctrl, next_ctrl);
1✔
809

810
    /* Add this ctrl object to the client object. */
811
    *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = next_ctrl;
1✔
812

813
    /* Set the flag that this control is ready to go. */
814
    next_ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
1✔
815
    next_ctrl->ctrls_cl = cl;
1✔
816

817
    next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
1✔
818
  }
819

820
  pr_json_array_free(args);
5✔
821
  pr_json_object_free(json);
5✔
822
  destroy_pool(tmp_pool);
5✔
823
  pr_signals_unblock();
5✔
824

825
  return 0;
5✔
826
}
827

828
int pr_ctrls_send_response(pool *p, int fd, int status, unsigned int argc,
8✔
829
    char **argv) {
830
  register unsigned int i;
831
  pool *tmp_pool;
832
  int res, xerrno;
833
  pr_json_object_t *json;
834
  pr_json_array_t *resps;
835

836
  if (p == NULL ||
16✔
837
      fd < 0) {
8✔
838
    errno = EINVAL;
2✔
839
    return -1;
2✔
840
  }
841

842
  if (argc > 0 &&
12✔
843
      argv == NULL) {
6✔
844
    errno = EINVAL;
1✔
845
    return -1;
1✔
846
  }
847

848
  tmp_pool = make_sub_pool(p);
5✔
849
  pr_pool_tag(tmp_pool, "Controls API send_response pool");
5✔
850

851
  json = pr_json_object_alloc(tmp_pool);
5✔
852

853
  res = pr_json_object_set_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY,
5✔
854
    (double) status);
855
  xerrno = errno;
5✔
856

857
  if (res < 0) {
5✔
858
    pr_json_object_free(json);
×
859
    destroy_pool(tmp_pool);
×
860

861
    errno = xerrno;
×
862
    return -1;
×
863
  }
864

865
  resps = pr_json_array_alloc(tmp_pool);
5✔
866

867
  for (i = 0; i < argc; i++) {
9✔
868
    res = pr_json_array_append_string(tmp_pool, resps, argv[i]);
4✔
869
    xerrno = errno;
4✔
870

871
    if (res < 0) {
4✔
872
      pr_json_array_free(resps);
×
873
      pr_json_object_free(json);
×
874
      destroy_pool(tmp_pool);
×
875

876
      errno = xerrno;
×
877
      return -1;
×
878
    }
879
  }
880

881
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, resps);
5✔
882
  xerrno = errno;
5✔
883

884
  if (res < 0) {
5✔
885
    pr_json_array_free(resps);
×
886
    pr_json_object_free(json);
×
887
    destroy_pool(tmp_pool);
×
888

889
    errno = xerrno;
×
890
    return -1;
×
891
  }
892

893
  res = ctrls_send_msg(tmp_pool, fd, json);
5✔
894
  xerrno = errno;
5✔
895

896
  pr_json_array_free(resps);
5✔
897
  pr_json_object_free(json);
5✔
898
  destroy_pool(tmp_pool);
5✔
899

900
  errno = xerrno;
5✔
901
  return res;
5✔
902
}
903

904
int pr_ctrls_recv_response(pool *p, int fd, int *status, char ***respargv) {
11✔
905
  register int i = 0;
11✔
906
  pool *tmp_pool;
907
  int nread, res, respargc = 0, xerrno;
11✔
908
  uint32_t msglen = 0;
11✔
909
  char *msg = NULL;
11✔
910
  pr_json_object_t *json = NULL;
11✔
911
  pr_json_array_t *resps = NULL;
11✔
912
  double dv;
913
  array_header *resparr = NULL;
11✔
914

915
  /* Sanity checks */
916
  if (p == NULL ||
22✔
917
      fd < 0 ||
20✔
918
      status == NULL) {
919
    errno = EINVAL;
3✔
920
    return -1;
3✔
921
  }
922

923
  /* No interruptions. */
924
  pr_signals_block();
8✔
925

926
  /* Read in the size of the message, as JSON text. */
927

928
  nread = read(fd, &msglen, sizeof(uint32_t));
8✔
929
  xerrno = errno;
8✔
930

931
  if (nread < 0) {
8✔
932
    pr_signals_unblock();
×
933

934
    pr_trace_msg(trace_channel, 3,
×
935
      "error reading %lu bytes of response message size: %s",
936
      sizeof(msglen), strerror(xerrno));
937

938
    errno = xerrno;
×
939
    return -1;
×
940
  }
941

942
  /* Watch for short reads. */
943
  if (nread != sizeof(uint32_t)) {
8✔
944
    pr_signals_unblock();
1✔
945

946
    (void) pr_trace_msg(trace_channel, 3,
1✔
947
      "short read (%d of %u bytes) of response message, unable to receive "
948
      "response", nread, (unsigned int) sizeof(uint32_t));
949
    errno = EPERM;
1✔
950
    return -1;
1✔
951
  }
952

953
  tmp_pool = make_sub_pool(p);
7✔
954
  pr_pool_tag(tmp_pool, "Controls API recv_response pool");
7✔
955

956
  pr_trace_msg(trace_channel, 27,
7✔
957
    "receiving Controls response message (%lu bytes) from fd %d",
958
    (unsigned long) msglen, fd);
959

960
  /* Impose max response size limit here, for Issue #2036. */
961
  if (msglen > CTRLS_MAX_RESP_SIZE) {
7✔
962
    destroy_pool(tmp_pool);
1✔
963
    pr_signals_unblock();
1✔
964

965
    (void) pr_trace_msg(trace_channel, 3,
1✔
966
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
967
      "response", (unsigned long) msglen, (unsigned long) CTRLS_MAX_RESP_SIZE);
968
    errno = E2BIG;
1✔
969
    return -1;
1✔
970
  }
971

972
  /* Allocate one byte for the terminating NUL. */
973
  msg = pcalloc(tmp_pool, msglen + 1);
6✔
974
  nread = read(fd, msg, msglen);
12✔
975
  xerrno = errno;
6✔
976

977
  if (nread < 0) {
6✔
978
    destroy_pool(tmp_pool);
×
979
    pr_signals_unblock();
×
980

981
    pr_trace_msg(trace_channel, 3,
×
982
      "error reading %lu bytes of response message: %s",
983
      (unsigned long) msglen, strerror(xerrno));
984
    errno = xerrno;
×
985
    return -1;
×
986
  }
987

988
  /* Watch for short reads. */
989
  if ((unsigned int) nread != msglen) {
6✔
990
    destroy_pool(tmp_pool);
1✔
991
    pr_signals_unblock();
1✔
992

993
    (void) pr_trace_msg(trace_channel, 3,
1✔
994
      "short read (%d of %u bytes) of message text, unable to receive response",
995
      nread, (unsigned int) msglen);
996
    errno = EPERM;
1✔
997
    return -1;
1✔
998
  }
999

1000
  json = pr_json_object_from_text(tmp_pool, msg);
5✔
1001
  xerrno = errno;
5✔
1002

1003
  if (json == NULL) {
5✔
1004
    destroy_pool(tmp_pool);
1✔
1005
    pr_signals_unblock();
1✔
1006

1007
    (void) pr_trace_msg(trace_channel, 3,
1✔
1008
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
1009
      "receive response: %s", (int) msglen, msg, (unsigned long) msglen,
1010
      strerror(xerrno));
1011
    errno = EINVAL;
1✔
1012
    return -1;
1✔
1013
  }
1014

1015
  res = pr_json_object_get_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY, &dv);
4✔
1016
  xerrno = errno;
4✔
1017

1018
  if (res < 0) {
4✔
1019
    pr_json_object_free(json);
1✔
1020
    destroy_pool(tmp_pool);
1✔
1021
    pr_signals_unblock();
1✔
1022

1023
    (void) pr_trace_msg(trace_channel, 3,
1✔
1024
      "unable to read response status (%s), unable to receive response",
1025
      strerror(xerrno));
1026
    errno = EINVAL;
1✔
1027
    return -1;
1✔
1028
  }
1029

1030
  *status = (int) dv;
3✔
1031
  pr_trace_msg(trace_channel, 19, "received response status: %d", *status);
3✔
1032

1033
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, &resps);
3✔
1034
  xerrno = errno;
3✔
1035

1036
  if (res < 0) {
3✔
1037
    (void) pr_trace_msg(trace_channel, 3,
1✔
1038
      "unable to read message responses (%s), unable to receive response",
1039
      strerror(xerrno));
1040
    pr_json_object_free(json);
1✔
1041
    destroy_pool(tmp_pool);
1✔
1042
    pr_signals_unblock();
1✔
1043

1044
    errno = EINVAL;
1✔
1045
    return -1;
1✔
1046
  }
1047

1048
  respargc = pr_json_array_count(resps);
2✔
1049
  pr_trace_msg(trace_channel, 19, "received response argc: %u", respargc);
2✔
1050

1051
  resparr = make_array(p, 0, sizeof(char *));
2✔
1052

1053
  /* Read each response, and add it to the array */
1054
  for (i = 0; i < respargc; i++) {
3✔
1055
    char *resp = NULL;
2✔
1056

1057
    /* TODO: Handle other response types, such as arrays or objects, for
1058
     * more complex responses.  Think of an action that dumps the memory
1059
     * pools, for example.
1060
     */
1061
    res = pr_json_array_get_string(tmp_pool, resps, i, &resp);
2✔
1062
    xerrno = errno;
2✔
1063

1064
    if (res < 0) {
2✔
1065
      (void) pr_trace_msg(trace_channel, 3,
1✔
1066
        "unable to read message response #%u (%s), unable to receive response",
1067
        i+1, strerror(xerrno));
1068
      pr_json_array_free(resps);
1✔
1069
      pr_json_object_free(json);
1✔
1070
      destroy_pool(tmp_pool);
1✔
1071
      pr_signals_unblock();
1✔
1072

1073
      errno = EINVAL;
1✔
1074
      return -1;
1✔
1075
    }
1076

1077
    *((char **) push_array(resparr)) = pstrdup(p, resp);
1✔
1078
  }
1079

1080
  if (respargv != NULL) {
1✔
1081
    *respargv = ((char **) resparr->elts);
×
1082
  }
1083

1084
  pr_json_array_free(resps);
1✔
1085
  pr_json_object_free(json);
1✔
1086
  destroy_pool(tmp_pool);
1✔
1087
  pr_signals_unblock();
1✔
1088

1089
  return respargc;
1✔
1090
}
1091

1092
static pr_ctrls_t *ctrls_lookup_action(module *mod, const char *action,
×
1093
    unsigned char skip_disabled) {
1094

1095
  /* (Re)set the current indices */
1096
  action_lookup_next = ctrls_action_list;
7✔
1097
  action_lookup_action = action;
7✔
1098
  action_lookup_module = mod;
7✔
1099

1100
  /* Wrapper around ctrls_lookup_next_action() */
1101
  return ctrls_lookup_next_action(mod, skip_disabled);
7✔
1102
}
1103

1104
static pr_ctrls_t *ctrls_lookup_next_action(module *mod,
13✔
1105
    unsigned char skip_disabled) {
1106
  register ctrls_action_t *act = NULL;
13✔
1107

1108
  /* Sanity check */
1109
  if (action_lookup_action == NULL) {
13✔
1110
    errno = EINVAL;
×
1111
    return NULL;
×
1112
  }
1113

1114
  if (mod != action_lookup_module) {
13✔
1115
    return ctrls_lookup_action(mod, action_lookup_action, skip_disabled);
×
1116
  }
1117

1118
  for (act = action_lookup_next; act; act = act->next) {
13✔
1119
    if (skip_disabled && (act->flags & PR_CTRLS_ACT_DISABLED)) {
7✔
1120
      continue;
×
1121
    }
1122

1123
    if (strcmp(act->action, action_lookup_action) == 0 &&
14✔
1124
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
13✔
1125
      action_lookup_next = act->next;
7✔
1126

1127
      /* Use this action object to prepare a ctrl object. */
1128
      return ctrls_prepare(act);
7✔
1129
    }
1130
  }
1131

1132
  return NULL;
1133
}
1134

1135
int pr_get_registered_actions(pr_ctrls_t *ctrl, int flags) {
7✔
1136
  register ctrls_action_t *act = NULL;
7✔
1137
  int count = 0;
7✔
1138

1139
  if (ctrl == NULL) {
7✔
1140
    errno = EINVAL;
1✔
1141
    return -1;
1✔
1142
  }
1143

1144
  /* Are ctrls blocked? */
1145
  if (ctrls_blocked == TRUE) {
6✔
1146
    errno = EPERM;
1✔
1147
    return -1;
1✔
1148
  }
1149

1150
  for (act = ctrls_action_list; act; act = act->next) {
13✔
1151
    switch (flags) {
8✔
1152
      case CTRLS_GET_ACTION_ALL:
2✔
1153
        if (act->module != NULL) {
2✔
1154
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1155
            act->module->name);
1156

1157
        } else {
1158
           pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1✔
1159
        }
1160

1161
        count++;
2✔
1162
        break;
2✔
1163

1164
      case CTRLS_GET_ACTION_ENABLED:
2✔
1165
        if (act->flags & PR_CTRLS_ACT_DISABLED) {
2✔
1166
          continue;
×
1167
        }
1168

1169
        if (act->module != NULL) {
2✔
1170
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1171
            act->module->name);
1172

1173
        } else {
1174
          pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1✔
1175
        }
1176

1177
        count++;
2✔
1178
        break;
2✔
1179

1180
      case CTRLS_GET_DESC:
2✔
1181
        pr_ctrls_add_response(ctrl, "%s: %s", act->action,
2✔
1182
          act->desc);
1183
        count++;
2✔
1184
        break;
2✔
1185
    }
1186
  }
1187

1188
  return count;
1189
}
1190

1191
int pr_set_registered_actions(module *mod, const char *action,
7✔
1192
    unsigned char skip_disabled, unsigned int flags) {
1193
  register ctrls_action_t *act = NULL;
7✔
1194
  unsigned char have_action = FALSE;
7✔
1195

1196
  /* Is flags a valid combination of settable flags? */
1197
  if (flags > 0 &&
14✔
1198
      flags != PR_CTRLS_ACT_SOLITARY &&
7✔
1199
      flags != PR_CTRLS_ACT_DISABLED &&
2✔
1200
      flags != (PR_CTRLS_ACT_SOLITARY|PR_CTRLS_ACT_DISABLED)) {
1✔
1201
    errno = EINVAL;
1✔
1202
    return -1;
1✔
1203
  }
1204

1205
  /* Are ctrls blocked? */
1206
  if (ctrls_blocked == TRUE) {
6✔
1207
    errno = EPERM;
1✔
1208
    return -1;
1✔
1209
  }
1210

1211
  for (act = ctrls_action_list; act; act = act->next) {
10✔
1212
    if (skip_disabled == TRUE &&
5✔
1213
        (act->flags & PR_CTRLS_ACT_DISABLED)) {
×
1214
      continue;
×
1215
    }
1216

1217
    if ((action == NULL ||
9✔
1218
         strcmp(action, "all") == 0 ||
7✔
1219
         strcmp(act->action, action) == 0) &&
8✔
1220
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
5✔
1221
      have_action = TRUE;
5✔
1222
      act->flags = flags;
5✔
1223
    }
1224
  }
1225

1226
  if (have_action == FALSE) {
5✔
1227
    errno = ENOENT;
1✔
1228
    return -1;
1✔
1229
  }
1230

1231
  return 0;
1232
}
1233

1234
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1235
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1236
static int ctrls_connect_local_creds(int fd) {
1237
  char buf[1] = {'\0'};
1238
  int res;
1239

1240
  /* The backend doesn't care what we send here, but it wants
1241
   * exactly one character to force recvmsg() to block and wait
1242
   * for us.
1243
   */
1244

1245
  res = write(fd, buf, 1);
1246
  while (res < 0) {
1247
    if (errno == EINTR) {
1248
      pr_signals_handle();
1249

1250
      res = write(fd, buf, 1);
1251
      continue;
1252
    }
1253

1254
    pr_trace_msg(trace_channel, 5,
1255
      "error writing credentials byte for LOCAL_CREDS to fd %d: %s", fd,
1256
      strerror(errno));
1257
    return -1;
1258
  }
1259

1260
  return res;
1261
}
1262
#endif /* !SCM_CREDS */
1263

1264
int pr_ctrls_connect(const char *socket_file) {
3✔
1265
  int fd = -1, len = 0;
3✔
1266
  struct sockaddr_un cl_sock, ctrl_sock;
1267

1268
  if (socket_file == NULL) {
3✔
1269
    errno = EINVAL;
1✔
1270
    return -1;
1✔
1271
  }
1272

1273
  /* No interruptions */
1274
  pr_signals_block();
2✔
1275

1276
  /* Create a Unix domain socket */
1277
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
2✔
1278
  if (fd < 0) {
2✔
1279
    int xerrno = errno;
×
1280

1281
    pr_signals_unblock();
×
1282

1283
    errno = xerrno;
×
1284
    return -1;
×
1285
  }
1286

1287
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
2✔
1288
    int xerrno = errno;
×
1289

1290
    (void) close(fd);
×
1291
    pr_signals_unblock();
×
1292

1293
    errno = xerrno;
×
1294
    return -1;
×
1295
  }
1296

1297
  /* Fill in the socket address */
1298
  memset(&cl_sock, 0, sizeof(cl_sock));
2✔
1299

1300
  /* This first part is clever.  First, this process creates a socket in
1301
   * the file system.  It _then_ connect()s to the server.  Upon accept()ing
1302
   * the connection, the server examines the created socket to see that it
1303
   * is indeed a socket, with the proper mode and time.  Clever, but not
1304
   * ideal.
1305
   */
1306

1307
  cl_sock.sun_family = AF_UNIX;
2✔
1308
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
2✔
1309
    "/tmp/ftp.cl", (unsigned int) getpid());
2✔
1310
  len = sizeof(cl_sock);
2✔
1311

1312
  /* Make sure the file doesn't already exist */
1313
  (void) unlink(cl_sock.sun_path);
2✔
1314

1315
  /* Make it a socket */
1316
  if (bind(fd, (struct sockaddr *) &cl_sock, len) < 0) {
2✔
1317
    int xerrno = errno;
×
1318

1319
    pr_trace_msg(trace_channel, 19, "error binding local socket to '%s': %s",
×
1320
      cl_sock.sun_path, strerror(xerrno));
1321
    (void) unlink(cl_sock.sun_path);
×
1322
    (void) close(fd);
×
1323
    pr_signals_unblock();
×
1324

1325
    errno = xerrno;
×
1326
    return -1;
×
1327
  }
1328

1329
  /* Set the proper mode */
1330
  if (chmod(cl_sock.sun_path, PR_CTRLS_CL_MODE) < 0) {
2✔
1331
    int xerrno = errno;
×
1332

1333
    pr_trace_msg(trace_channel, 19, "error setting local socket mode: %s",
×
1334
      strerror(xerrno));
1335
    (void) unlink(cl_sock.sun_path);
×
1336
    (void) close(fd);
×
1337
    pr_signals_unblock();
×
1338

1339
    errno = xerrno;
×
1340
    return -1;
×
1341
  }
1342

1343
  /* Now connect to the real server */
1344
  memset(&ctrl_sock, 0, sizeof(ctrl_sock));
2✔
1345

1346
  ctrl_sock.sun_family = AF_UNIX;
2✔
1347
  sstrncpy(ctrl_sock.sun_path, socket_file, sizeof(ctrl_sock.sun_path));
2✔
1348
  len = sizeof(ctrl_sock);
2✔
1349

1350
  if (connect(fd, (struct sockaddr *) &ctrl_sock, len) < 0) {
2✔
1351
    int xerrno = errno;
1✔
1352

1353
    pr_trace_msg(trace_channel, 19, "error connecting to local socket '%s': %s",
1✔
1354
      ctrl_sock.sun_path, strerror(xerrno));
1355
    (void) unlink(cl_sock.sun_path);
1✔
1356
    (void) close(fd);
1✔
1357
    pr_signals_unblock();
1✔
1358

1359
    errno = xerrno;
1✔
1360
    return -1;
1✔
1361
  }
1362

1363
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1364
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1365
  if (ctrls_connect_local_creds(fd) < 0) {
1366
    int xerrno = errno;
1367

1368
    pr_trace_msg(trace_channel, 19, "error sending creds to local socket: %s",
1369
      strerror(xerrno));
1370
    (void) unlink(cl_sock.sun_path);
1371
    (void) close(fd);
1372
    pr_signals_unblock();
1373

1374
    errno = xerrno;
1375
    return -1;
1376
  }
1377
#endif /* LOCAL_CREDS */
1378

1379
  pr_signals_unblock();
1✔
1380
  return fd;
1✔
1381
}
1382

1383
int pr_ctrls_issock_unix(mode_t sock_mode) {
3✔
1384

1385
  if (ctrls_use_isfifo == TRUE) {
3✔
1386
#if defined(S_ISFIFO)
1387
    if (S_ISFIFO(sock_mode)) {
×
1388
      return 0;
1389
    }
1390
#endif /* S_ISFIFO */
1391
  } else {
1392
#if defined(S_ISSOCK)
1393
    if (S_ISSOCK(sock_mode)) {
3✔
1394
      return 0;
1395
    }
1396
#endif /* S_ISSOCK */
1397
  }
1398

1399
  errno = ENOSYS;
2✔
1400
  return -1;
2✔
1401
}
1402

1403
#if defined(SO_PEERCRED)
1404
static int ctrls_get_creds_peercred(int fd, uid_t *uid, gid_t *gid,
×
1405
    pid_t *pid) {
1406
# if defined(HAVE_STRUCT_SOCKPEERCRED)
1407
  struct sockpeercred cred;
1408
# else
1409
  struct ucred cred;
1410
# endif /* HAVE_STRUCT_SOCKPEERCRED */
1411
  socklen_t cred_len;
1412

1413
  cred_len = sizeof(cred);
×
1414
  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) {
×
1415
    int xerrno = errno;
×
1416

1417
    pr_trace_msg(trace_channel, 2,
×
1418
      "error obtaining peer credentials using SO_PEERCRED: %s",
1419
      strerror(xerrno));
1420

1421
    errno = EPERM;
×
1422
    return -1;
×
1423
  }
1424

1425
  if (uid != NULL) {
×
1426
    *uid = cred.uid;
×
1427
  }
1428

1429
  if (gid != NULL) {
×
1430
    *gid = cred.gid;
×
1431
  }
1432

1433
  if (pid != NULL) {
×
1434
    *pid = cred.pid;
×
1435
  }
1436

1437
  return 0;
1438
}
1439
#endif /* SO_PEERCRED */
1440

1441
#if !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1442
static int ctrls_get_creds_peereid(int fd, uid_t *uid, gid_t *gid) {
1443
  if (getpeereid(fd, uid, gid) < 0) {
1444
    int xerrno = errno;
1445

1446
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1447
      "getpeereid(2) on fd %d: %s", fd, strerror(xerrno));
1448

1449
    errno = xerrno;
1450
    return -1;
1451
  }
1452

1453
  return 0;
1454
}
1455
#endif /* !HAVE_GETPEEREID */
1456

1457
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1458
    defined(HAVE_GETPEERUCRED)
1459
static int ctrls_get_creds_peerucred(int fd, uid_t *uid, gid_t *gid) {
1460
  ucred_t *cred = NULL;
1461

1462
  if (getpeerucred(fd, &cred) < 0) {
1463
    int xerrno = errno;
1464

1465
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1466
      "getpeerucred(3) on fd %d: %s", fd, strerror(xerrno));
1467

1468
    errno = xerrno;
1469
    return -1;
1470
  }
1471

1472
  if (uid != NULL) {
1473
    *uid = ucred_getruid(cred);
1474
  }
1475

1476
  if (gid != NULL) {
1477
    *gid = ucred_getrgid(cred);
1478
  }
1479

1480
  ucred_free(cred);
1481
  return 0;
1482
}
1483
#endif /* !HAVE_GETPEERUCRED */
1484

1485
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1486
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1487
static int ctrls_get_creds_local(int fd, uid_t *uid, gid_t *gid,
1488
    pid_t *pid) {
1489
  int res;
1490
  char buf[1];
1491
  struct iovec iov;
1492
  struct msghdr msg;
1493

1494
# if defined(SOCKCREDSIZE)
1495
#  define MINCREDSIZE                (sizeof(struct cmsghdr) + SOCKCREDSIZE(0))
1496
# else
1497
#  if defined(HAVE_STRUCT_CMSGCRED)
1498
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
1499
#  elif defined(HAVE_STRUCT_SOCKCRED)
1500
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct sockcred))
1501
#  endif
1502
# endif /* !SOCKCREDSIZE */
1503

1504
  char control[MINCREDSIZE];
1505

1506
  iov.iov_base = buf;
1507
  iov.iov_len = 1;
1508

1509
  memset(&msg, 0, sizeof(msg));
1510
  msg.msg_iov = &iov;
1511
  msg.msg_iovlen = 1;
1512
  msg.msg_control = control;
1513
  msg.msg_controllen = sizeof(control);
1514
  msg.msg_flags = 0;
1515

1516
  res = recvmsg(fd, &msg, 0);
1517
  while (res < 0) {
1518
    int xerrno = errno;
1519

1520
    if (xerrno == EINTR) {
1521
      pr_signals_handle();
1522

1523
      res = recvmsg(fd, &msg, 0);
1524
      continue;
1525
    }
1526

1527
    pr_trace_msg(trace_channel, 6,
1528
      "error calling recvmsg() on fd %d: %s", fd, strerror(xerrno));
1529

1530
    errno = xerrno;
1531
    return -1;
1532
  }
1533

1534
  if (msg.msg_controllen > 0) {
1535
#if defined(HAVE_STRUCT_CMSGCRED)
1536
    struct cmsgcred cred;
1537
#elif defined(HAVE_STRUCT_SOCKCRED)
1538
    struct sockcred cred;
1539
#endif /* !CMSGCRED and !SOCKCRED */
1540

1541
    struct cmsghdr *hdr = (struct cmsghdr *) control;
1542

1543
    if (hdr->cmsg_level != SOL_SOCKET) {
1544
      pr_trace_msg(trace_channel, 5,
1545
        "message received via recvmsg() on fd %d was not a SOL_SOCKET message",
1546
        fd);
1547

1548
      errno = EINVAL;
1549
      return -1;
1550
    }
1551

1552
    if (hdr->cmsg_len < MINCREDSIZE) {
1553
      pr_trace_msg(trace_channel, 5,
1554
        "message received via recvmsg() on fd %d was not of proper "
1555
        "length (%u bytes)", fd, MINCREDSIZE);
1556

1557
      errno = EINVAL;
1558
      return -1;
1559
    }
1560

1561
    if (hdr->cmsg_type != SCM_CREDS) {
1562
      pr_trace_msg(trace_channel, 5,
1563
        "message received via recvmsg() on fd %d was not of type SCM_CREDS",
1564
        fd);
1565

1566
      errno = EINVAL;
1567
      return -1;
1568
    }
1569

1570
#if defined(HAVE_STRUCT_CMSGCRED)
1571
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct cmsgcred));
1572

1573
    if (uid != NULL) {
1574
      *uid = cred.cmcred_uid;
1575
    }
1576

1577
    if (gid != NULL) {
1578
      *gid = cred.cmcred_gid;
1579
    }
1580

1581
    if (pid != NULL) {
1582
      *pid = cred.cmcred_pid;
1583
    }
1584

1585
#elif defined(HAVE_STRUCT_SOCKCRED)
1586
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct sockcred));
1587

1588
    if (uid != NULL) {
1589
      *uid = cred.sc_uid;
1590
    }
1591

1592
    if (gid != NULL) {
1593
      *gid = cred.sc_gid;
1594
    }
1595
#endif
1596

1597
    return 0;
1598
  }
1599

1600
  return -1;
1601
}
1602
#endif /* !SCM_CREDS */
1603

1604
static int ctrls_get_creds_basic(struct sockaddr_un *sock, int cl_fd,
×
1605
    unsigned int max_age, uid_t *uid, gid_t *gid, pid_t *pid) {
1606
  pid_t cl_pid = 0;
×
1607
  char *tmp = NULL;
×
1608
  time_t stale_time;
1609
  struct stat st;
1610

1611
  /* Check the path -- hmmm... */
1612
  PRIVS_ROOT
×
1613
  while (stat(sock->sun_path, &st) < 0) {
×
1614
    int xerrno = errno;
×
1615

1616
    if (xerrno == EINTR) {
×
1617
      pr_signals_handle();
×
1618
      continue;
×
1619
    }
1620

1621
    PRIVS_RELINQUISH
×
1622
    pr_trace_msg(trace_channel, 2, "error: unable to stat %s: %s",
×
1623
      sock->sun_path, strerror(xerrno));
1624
    (void) close(cl_fd);
×
1625

1626
    errno = xerrno;
×
1627
    return -1;
×
1628
  }
1629
  PRIVS_RELINQUISH
×
1630

1631
  /* Is it a socket? */
1632
  if (pr_ctrls_issock_unix(st.st_mode) < 0) {
×
1633
    (void) close(cl_fd);
×
1634
    errno = ENOTSOCK;
×
1635
    return -1;
×
1636
  }
1637

1638
  /* Are the perms _not_ rwx------? */
1639
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
×
1640
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
1641
    pr_trace_msg(trace_channel, 3,
×
1642
      "error: unable to accept connection: incorrect mode");
1643
    (void) close(cl_fd);
×
1644
    errno = EPERM;
×
1645
    return -1;
×
1646
  }
1647

1648
  /* Is it new enough? */
1649
  stale_time = time(NULL) - max_age;
×
1650

1651
  if (st.st_atime < stale_time ||
×
1652
      st.st_ctime < stale_time ||
×
1653
      st.st_mtime < stale_time) {
×
1654
    pool *tmp_pool;
1655
    char *msg = "error: stale connection";
×
1656

1657
    pr_trace_msg(trace_channel, 3,
×
1658
      "unable to accept connection: stale connection");
1659

1660
    /* Log the times being compared, to aid in debugging this situation. */
1661
    if (st.st_atime < stale_time) {
×
1662
      time_t age = stale_time - st.st_atime;
×
1663

1664
      pr_trace_msg(trace_channel, 3,
×
1665
        "last access time of '%s' is %lu secs old (must be less than %u secs)",
1666
        sock->sun_path, (unsigned long) age, max_age);
1667
    }
1668

1669
    if (st.st_ctime < stale_time) {
×
1670
      time_t age = stale_time - st.st_ctime;
×
1671

1672
      pr_trace_msg(trace_channel, 3,
×
1673
        "last change time of '%s' is %lu secs old (must be less than %u secs)",
1674
        sock->sun_path, (unsigned long) age, max_age);
1675
    }
1676

1677
    if (st.st_mtime < stale_time) {
×
1678
      time_t age = stale_time - st.st_mtime;
×
1679

1680
      pr_trace_msg(trace_channel, 3,
×
1681
        "last modified time of '%s' is %lu secs old (must be less than %u "
1682
        "secs)", sock->sun_path, (unsigned long) age, max_age);
1683
    }
1684

1685
    tmp_pool = make_sub_pool(permanent_pool);
×
1686

1687
    if (pr_ctrls_send_response(tmp_pool, cl_fd, -1, 1, &msg) < 0) {
×
1688
      pr_trace_msg(trace_channel, 2, "error sending message: %s",
×
1689
        strerror(errno));
×
1690
    }
1691

1692
    destroy_pool(tmp_pool);
×
1693
    (void) close(cl_fd);
×
1694

1695
    errno = ETIMEDOUT;
×
1696
    return -1;
1697
  }
1698

1699
  /* Parse the PID out of the path */
1700
  tmp = sock->sun_path;
×
1701
  tmp += strlen("/tmp/ftp.cl");
×
1702
  cl_pid = atol(tmp);
×
1703

1704
  /* Return the IDs of the caller */
1705
  if (uid != NULL) {
×
1706
    *uid = st.st_uid;
×
1707
  }
1708

1709
  if (gid != NULL) {
×
1710
    *gid = st.st_gid;
×
1711
  }
1712

1713
  if (pid != NULL) {
×
1714
    *pid = cl_pid;
×
1715
  }
1716

1717
  return 0;
1718
}
1719

1720
int pr_ctrls_accept(int fd, uid_t *uid, gid_t *gid, pid_t *pid,
2✔
1721
    unsigned int max_age) {
1722
  socklen_t len = 0;
1723
  struct sockaddr_un sock;
1724
  int cl_fd = -1, res = -1, xerrno;
2✔
1725

1726
  len = sizeof(sock);
2✔
1727

1728
  cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
2✔
1729
  xerrno = errno;
2✔
1730

1731
  while (cl_fd < 0) {
4✔
1732
    if (xerrno == EINTR) {
2✔
1733
      pr_signals_handle();
×
1734

1735
      cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
×
1736
      xerrno = errno;
×
1737
      continue;
×
1738
    }
1739

1740
    pr_trace_msg(trace_channel, 3,
2✔
1741
      "error: unable to accept on local socket: %s", strerror(xerrno));
1742

1743
    errno = xerrno;
2✔
1744
    return -1;
2✔
1745
  }
1746

1747
  /* NULL terminate the name */
1748
  sock.sun_path[sizeof(sock.sun_path)-1] = '\0';
×
1749

1750
#if defined(SO_PEERCRED)
1751
  pr_trace_msg(trace_channel, 5,
×
1752
    "checking client credentials using SO_PEERCRED");
1753
  res = ctrls_get_creds_peercred(cl_fd, uid, gid, pid);
×
1754

1755
#elif !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1756
  pr_trace_msg(trace_channel, 5,
1757
    "checking client credentials using getpeereid(2)");
1758
  res = ctrls_get_creds_peereid(cl_fd, uid, gid);
1759

1760
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1761
      defined(HAVE_GETPEERUCRED)
1762
  pr_trace_msg(trace_channel, 5,
1763
    "checking client credentials using getpeerucred(3)");
1764
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1765

1766
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1767
      !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1768
  pr_trace_msg(trace_channel, 5,
1769
    "checking client credentials using SCM_CREDS");
1770
  res = ctrls_get_creds_local(cl_fd, uid, gid, pid);
1771
#endif
1772

1773
  /* Fallback to the Stevens method of determining connection credentials,
1774
   * if the kernel-enforced methods did not pan out.
1775
   */
1776
  if (res < 0) {
×
1777
    pr_trace_msg(trace_channel, 5,
×
1778
      "checking client credentials using Stevens' method");
1779
    res = ctrls_get_creds_basic(&sock, cl_fd, max_age, uid, gid, pid);
×
1780
    if (res < 0) {
×
1781
      return res;
1782
    }
1783
  }
1784

1785
  /* Done with the path now */
1786
  PRIVS_ROOT
×
1787
  (void) unlink(sock.sun_path);
×
1788
  PRIVS_RELINQUISH
×
1789

1790
  return cl_fd;
×
1791
}
1792

1793
void pr_block_ctrls(void) {
3✔
1794
  ctrls_blocked = TRUE;
46✔
1795
}
3✔
1796

1797
void pr_unblock_ctrls(void) {
3✔
1798
  ctrls_blocked = FALSE;
46✔
1799
}
3✔
1800

1801
int pr_ctrls_check_actions(void) {
4✔
1802
  register ctrls_action_t *act = NULL;
4✔
1803

1804
  for (act = ctrls_action_list; act; act = act->next) {
7✔
1805
    if (act->flags & PR_CTRLS_ACT_SOLITARY) {
4✔
1806
      /* This is a territorial action -- only one instance allowed */
1807
      if (ctrls_lookup_action(NULL, act->action, FALSE)) {
2✔
1808
        pr_log_pri(PR_LOG_NOTICE,
1✔
1809
          "duplicate controls for '%s' action not allowed",
1810
          act->action);
1811
        errno = EEXIST;
1✔
1812
        return -1;
1✔
1813
      }
1814
    }
1815
  }
1816

1817
  return 0;
1818
}
1819

1820
int pr_run_ctrls(module *mod, const char *action) {
12✔
1821
  register pr_ctrls_t *ctrl = NULL;
12✔
1822
  time_t now;
1823

1824
  /* Are ctrls blocked? */
1825
  if (ctrls_blocked == TRUE) {
12✔
1826
    errno = EPERM;
1✔
1827
    return -1;
1✔
1828
  }
1829

1830
  now = time(NULL);
11✔
1831

1832
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
18✔
1833
    int res;
1834

1835
    if (mod != NULL &&
14✔
1836
        ctrl->ctrls_module != NULL &&
14✔
1837
        ctrl->ctrls_module != mod) {
1838
      pr_trace_msg(trace_channel, 19,
1✔
1839
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1840
        mod, ctrl->ctrls_module);
1841
      continue;
1✔
1842
    }
1843

1844
    /* Be watchful of the various client-side flags.  Note: if
1845
     * ctrl->ctrls_cl is ever NULL, it means there's a bug in the code.
1846
     */
1847
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
6✔
1848
      pr_trace_msg(trace_channel, 19,
1✔
1849
        "skipping ctrl due to missing client HAVEREQ flag");
1850
      continue;
1✔
1851
    }
1852

1853
    /* Has this control been disabled? */
1854
    if (ctrl->ctrls_flags & PR_CTRLS_ACT_DISABLED) {
5✔
1855
      pr_trace_msg(trace_channel, 19,
1✔
1856
        "skipping ctrl due to ACT_DISABLED flag");
1857
      continue;
1✔
1858
    }
1859

1860
    /* Is it time to trigger this ctrl? */
1861
    if (!(ctrl->ctrls_flags & PR_CTRLS_FL_REQUESTED)) {
4✔
1862
      pr_trace_msg(trace_channel, 19,
1✔
1863
        "skipping ctrl due to missing CTRLS_REQUESTED flag");
1864
      continue;
1✔
1865
    }
1866

1867
    if (ctrl->ctrls_when > now) {
3✔
1868
      pr_trace_msg(trace_channel, 19,
1✔
1869
        "skipping ctrl because it is still pending: now = %lu, ctrl when = %lu",
1870
        (unsigned long) now, (unsigned long) ctrl->ctrls_when);
1871
      ctrl->ctrls_flags |= PR_CTRLS_FL_PENDING;
1✔
1872
      pr_ctrls_add_response(ctrl, "request pending");
1✔
1873
      continue;
1✔
1874
    }
1875

1876
    if (action == NULL ||
4✔
1877
        strcmp(ctrl->ctrls_action, action) == 0) {
2✔
1878
      pr_trace_msg(trace_channel, 7, "calling '%s' control handler",
1✔
1879
        ctrl->ctrls_action);
1880

1881
    } else {
1882
      continue;
1✔
1883
    }
1884

1885
    pr_unblock_ctrls();
1886
    res = ctrl->ctrls_cb(ctrl,
4✔
1887
      (ctrl->ctrls_cb_args ? ctrl->ctrls_cb_args->nelts : 0),
1✔
1888
      (ctrl->ctrls_cb_args ? (char **) ctrl->ctrls_cb_args->elts : NULL));
1✔
1889
    pr_block_ctrls();
1890

1891
    pr_trace_msg(trace_channel, 19,
1✔
1892
      "ran '%s' ctrl, callback value = %d", ctrl->ctrls_action, res);
1893

1894
    if (res >= PR_CTRLS_STATUS_PENDING) {
1✔
1895
      pr_trace_msg(trace_channel, 1, "'%s' ctrl returned inappropriate "
×
1896
        "value %d, treating as GENERIC_ERROR (%d)", ctrl->ctrls_action, res,
1897
        PR_CTRLS_STATUS_GENERIC_ERROR);
1898
      res = PR_CTRLS_STATUS_GENERIC_ERROR;
×
1899
    }
1900

1901
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_REQUESTED;
1✔
1902
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_PENDING;
1✔
1903
    ctrl->ctrls_flags |= PR_CTRLS_FL_HANDLED;
1✔
1904

1905
    ctrl->ctrls_cb_retval = res;
1✔
1906
  }
1907

1908
  return 0;
1909
}
1910

1911
int pr_ctrls_reset(void) {
2✔
1912
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
2✔
1913

1914
  /* NOTE: need a clean_ctrls() or somesuch that will, after sending any
1915
   * responses, iterate through the list and "free" any ctrls whose
1916
   * ctrls_cb_retval is zero.  This feature is used to handle things like
1917
   * shutdown requests in the future -- the request is only considered
1918
   * "processed" when the callback returns zero.  Any non-zero requests are
1919
   * not cleared, and are considered "pending".  However, this brings up the
1920
   * complication of an additional request for that action being issued by the
1921
   * client before the request is processed.  Simplest solution: remove the
1922
   * old request args, and replace them with the new ones.
1923
   *
1924
   * This requires that the return value of the ctrl callback be explicitly
1925
   * documented.
1926
   *
1927
   * How about: ctrls_cb_retval = 1  pending
1928
   *                              0  processed, OK    (reset)
1929
   *                             -1  processed, error (reset)
1930
   */
1931

1932
  for (ctrl = ctrls_active_list; ctrl; ctrl = next_ctrl) {
4✔
1933
    next_ctrl = ctrl->ctrls_next;
×
1934

1935
    if (ctrl->ctrls_cb_retval < PR_CTRLS_STATUS_PENDING) {
×
1936
      pr_ctrls_free(ctrl);
×
1937
    }
1938
  }
1939

1940
  return 0;
2✔
1941
}
1942

1943
/* From include/mod_ctrls.h */
1944

1945
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
1946
 * otherwise. Note that the default is to deny everyone, unless an ACL has
1947
 * been configured.
1948
 */
1949
int pr_ctrls_check_group_acl(gid_t cl_gid, const ctrls_group_acl_t *group_acl) {
10✔
1950
  int res = FALSE;
10✔
1951

1952
  if (group_acl == NULL) {
10✔
1953
    errno = EINVAL;
1✔
1954
    return -1;
1✔
1955
  }
1956

1957
  /* Note: the special condition of ngids of 1 and gids of NULL signals
1958
   * that all groups are to be treated according to the allow member.
1959
   */
1960
  if (group_acl->gids != NULL) {
9✔
1961
    register unsigned int i = 0;
1962

1963
    for (i = 0; i < group_acl->ngids; i++) {
2✔
1964
      if ((group_acl->gids)[i] == cl_gid) {
2✔
1965
        res = TRUE;
1✔
1966
      }
1967
    }
1968

1969
  } else if (group_acl->ngids == 1) {
7✔
1970
    res = TRUE;
4✔
1971
  }
1972

1973
  if (!group_acl->allow) {
9✔
1974
    res = !res;
2✔
1975
  }
1976

1977
  return res;
1978
}
1979

1980
/* Returns TRUE if the given cl_uid is allowed by the user ACL, FALSE
1981
 * otherwise. Note that the default is to deny everyone, unless an ACL has
1982
 * been configured.
1983
 */
1984
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
10✔
1985
  int res = FALSE;
10✔
1986

1987
  if (user_acl == NULL) {
10✔
1988
    errno = EINVAL;
1✔
1989
    return -1;
1✔
1990
  }
1991

1992
  /* Note: the special condition of nuids of 1 and uids of NULL signals
1993
   * that all users are to be treated according to the allow member.
1994
   */
1995
  if (user_acl->uids != NULL) {
9✔
1996
    register unsigned int i = 0;
1997

1998
    for (i = 0; i < user_acl->nuids; i++) {
2✔
1999
      if ((user_acl->uids)[i] == cl_uid) {
2✔
2000
        res = TRUE;
1✔
2001
      }
2002
    }
2003

2004
  } else if (user_acl->nuids == 1) {
7✔
2005
    res = TRUE;
3✔
2006
  }
2007

2008
  if (!user_acl->allow) {
9✔
2009
    res = !res;
2✔
2010
  }
2011

2012
  return res;
2013
}
2014

2015
/* Returns TRUE for allowed, FALSE for denied. */
2016
int pr_ctrls_check_acl(const pr_ctrls_t *ctrl,
9✔
2017
    const ctrls_acttab_t *acttab, const char *action) {
2018
  register unsigned int i = 0;
9✔
2019

2020
  if (ctrl == NULL ||
17✔
2021
      ctrl->ctrls_cl == NULL ||
8✔
2022
      acttab == NULL ||
14✔
2023
      action == NULL) {
7✔
2024
    errno = EINVAL;
4✔
2025
    return -1;
4✔
2026
  }
2027

2028
  for (i = 0; acttab[i].act_action; i++) {
3✔
2029
    if (strcmp(acttab[i].act_action, action) == 0) {
5✔
2030
      int user_check = FALSE, group_check = FALSE;
4✔
2031

2032
      if (acttab[i].act_acl != NULL) {
4✔
2033
        user_check = pr_ctrls_check_user_acl(ctrl->ctrls_cl->cl_uid,
3✔
2034
          &(acttab[i].act_acl->acl_users));
3✔
2035
        group_check = pr_ctrls_check_group_acl(ctrl->ctrls_cl->cl_gid,
3✔
2036
          &(acttab[i].act_acl->acl_groups));
3✔
2037
      }
2038

2039
      if (user_check != TRUE &&
8✔
2040
          group_check != TRUE) {
4✔
2041
        return FALSE;
2042
      }
2043
    }
2044
  }
2045

2046
  return TRUE;
2047
}
2048

2049
int pr_ctrls_init_acl(ctrls_acl_t *acl) {
7✔
2050
  if (acl == NULL) {
7✔
2051
    errno = EINVAL;
1✔
2052
    return -1;
1✔
2053
  }
2054

2055
  memset(acl, 0, sizeof(ctrls_acl_t));
6✔
2056
  acl->acl_users.allow = acl->acl_groups.allow = TRUE;
6✔
2057

2058
  return 0;
6✔
2059
}
2060

2061
static char *ctrls_argsep(char **arg) {
46✔
2062
  char *ret = NULL, *dst = NULL;
46✔
2063
  char quote_mode = 0;
46✔
2064

2065
  if (arg == NULL ||
92✔
2066
      !*arg ||
92✔
2067
      !**arg) {
46✔
2068
    errno = EINVAL;
12✔
2069
    return NULL;
12✔
2070
  }
2071

2072
  while (**arg &&
34✔
2073
         PR_ISSPACE(**arg)) {
34✔
2074
    (*arg)++;
×
2075
  }
2076

2077
  if (!**arg) {
34✔
2078
    return NULL;
2079
  }
2080

2081
  ret = dst = *arg;
34✔
2082

2083
  if (**arg == '\"') {
34✔
2084
    quote_mode++;
×
2085
    (*arg)++;
×
2086
  }
2087

2088
  while (**arg && **arg != ',' &&
240✔
2089
      (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
103✔
2090

2091
    if (**arg == '\\' && quote_mode) {
103✔
2092
      /* escaped char */
2093
      if (*((*arg) + 1)) {
×
2094
        *dst = *(++(*arg));
×
2095
      }
2096
    }
2097

2098
    *dst++ = **arg;
103✔
2099
    ++(*arg);
103✔
2100
  }
2101

2102
  if (**arg) {
34✔
2103
    (*arg)++;
22✔
2104
  }
2105

2106
  *dst = '\0';
34✔
2107
  return ret;
34✔
2108
}
2109

2110
char **pr_ctrls_parse_acl(pool *acl_pool, const char *acl_text) {
14✔
2111
  char *name = NULL, *acl_text_dup = NULL, **acl_list = NULL;
14✔
2112
  array_header *acl_arr = NULL;
14✔
2113
  pool *tmp_pool = NULL;
14✔
2114

2115
  if (acl_pool == NULL ||
28✔
2116
      acl_text == NULL) {
14✔
2117
    errno = EINVAL;
2✔
2118
    return NULL;
2✔
2119
  }
2120

2121
  tmp_pool = make_sub_pool(acl_pool);
12✔
2122
  acl_text_dup = pstrdup(tmp_pool, acl_text);
12✔
2123

2124
  /* Allocate an array */
2125
  acl_arr = make_array(acl_pool, 0, sizeof(char **));
12✔
2126

2127
  /* Add each name to the array */
2128
  while ((name = ctrls_argsep(&acl_text_dup)) != NULL) {
58✔
2129
    char *text;
2130

2131
    text = pstrdup(acl_pool, name);
34✔
2132

2133
    /* Push the name into the ACL array */
2134
    *((char **) push_array(acl_arr)) = text;
34✔
2135
  }
2136

2137
  /* Terminate the temp array with a NULL, as is proper. */
2138
  *((char **) push_array(acl_arr)) = NULL;
12✔
2139

2140
  acl_list = (char **) acl_arr->elts;
12✔
2141
  destroy_pool(tmp_pool);
12✔
2142

2143
  /* return the array of names */
2144
  return acl_list;
12✔
2145
}
2146

2147
int pr_ctrls_set_group_acl(pool *group_acl_pool, ctrls_group_acl_t *group_acl,
8✔
2148
    const char *allow, char *grouplist) {
2149
  char *group = NULL, **groups = NULL;
8✔
2150
  array_header *gid_list = NULL;
8✔
2151
  gid_t gid = 0;
8✔
2152
  pool *tmp_pool = NULL;
8✔
2153

2154
  if (group_acl_pool == NULL ||
16✔
2155
      group_acl == NULL ||
8✔
2156
      allow == NULL ||
12✔
2157
      grouplist == NULL) {
6✔
2158
    errno = EINVAL;
4✔
2159
    return -1;
4✔
2160
  }
2161

2162
  tmp_pool = make_sub_pool(group_acl_pool);
4✔
2163

2164
  if (strcasecmp(allow, "allow") == 0) {
4✔
2165
    group_acl->allow = TRUE;
3✔
2166

2167
  } else {
2168
    group_acl->allow = FALSE;
1✔
2169
  }
2170

2171
  /* Parse the given expression into an array, then retrieve the GID
2172
   * for each given name.
2173
   */
2174
  groups = pr_ctrls_parse_acl(group_acl_pool, grouplist);
4✔
2175

2176
  /* Allocate an array of gid_t's */
2177
  gid_list = make_array(group_acl_pool, 0, sizeof(gid_t));
4✔
2178

2179
  for (group = *groups; group != NULL; group = *++groups) {
15✔
2180

2181
    /* Handle a group name of "*" differently. */
2182
    if (strcmp(group, "*") == 0) {
12✔
2183
      group_acl->ngids = 1;
1✔
2184
      group_acl->gids = NULL;
1✔
2185
      destroy_pool(tmp_pool);
1✔
2186
      return 0;
1✔
2187
    }
2188

2189
    gid = pr_auth_name2gid(tmp_pool, group);
11✔
2190
    if (gid == (gid_t) -1) {
11✔
2191
      continue;
11✔
2192
    }
2193

2194
    *((gid_t *) push_array(gid_list)) = gid;
×
2195
  }
2196

2197
  group_acl->ngids = gid_list->nelts;
3✔
2198
  group_acl->gids = (gid_t *) gid_list->elts;
3✔
2199

2200
  destroy_pool(tmp_pool);
3✔
2201
  return 0;
3✔
2202
}
2203

2204
int pr_ctrls_set_user_acl(pool *user_acl_pool, ctrls_user_acl_t *user_acl,
10✔
2205
    const char *allow, char *userlist) {
2206
  char *user = NULL, **users = NULL;
10✔
2207
  array_header *uid_list = NULL;
10✔
2208
  uid_t uid = 0;
10✔
2209
  pool *tmp_pool = NULL;
10✔
2210

2211
  /* Sanity checks */
2212
  if (user_acl_pool == NULL ||
20✔
2213
      user_acl == NULL ||
10✔
2214
      allow == NULL ||
16✔
2215
      userlist == NULL) {
8✔
2216
    errno = EINVAL;
4✔
2217
    return -1;
4✔
2218
  }
2219

2220
  tmp_pool = make_sub_pool(user_acl_pool);
6✔
2221

2222
  if (strcasecmp(allow, "allow") == 0) {
6✔
2223
    user_acl->allow = TRUE;
5✔
2224

2225
  } else {
2226
    user_acl->allow = FALSE;
1✔
2227
  }
2228

2229
  /* Parse the given expression into an array, then retrieve the UID
2230
   * for each given name.
2231
   */
2232
  users = pr_ctrls_parse_acl(user_acl_pool, userlist);
6✔
2233

2234
  /* Allocate an array of uid_t's */
2235
  uid_list = make_array(user_acl_pool, 0, sizeof(uid_t));
6✔
2236

2237
  for (user = *users; user != NULL; user = *++users) {
23✔
2238

2239
    /* Handle a user name of "*" differently. */
2240
    if (strcmp(user, "*") == 0) {
18✔
2241
      user_acl->nuids = 1;
1✔
2242
      user_acl->uids = NULL;
1✔
2243
      destroy_pool(tmp_pool);
1✔
2244
      return 0;
1✔
2245
    }
2246

2247
    uid = pr_auth_name2uid(tmp_pool, user);
17✔
2248
    if (uid == (uid_t) -1) {
17✔
2249
      continue;
17✔
2250
    }
2251

2252
    *((uid_t *) push_array(uid_list)) = uid;
×
2253
  }
2254

2255
  user_acl->nuids = uid_list->nelts;
5✔
2256
  user_acl->uids = (uid_t *) uid_list->elts;
5✔
2257

2258
  destroy_pool(tmp_pool);
5✔
2259
  return 0;
5✔
2260
}
2261

2262
int pr_ctrls_set_module_acls2(ctrls_acttab_t *acttab, pool *acl_pool,
23✔
2263
    char **actions, const char *allow, const char *type, char *list,
2264
    const char **bad_action) {
2265
  register unsigned int i = 0;
23✔
2266
  int all_actions = FALSE;
23✔
2267

2268
  if (acttab == NULL ||
46✔
2269
      acl_pool == NULL ||
23✔
2270
      actions == NULL ||
38✔
2271
      type == NULL ||
32✔
2272
      bad_action == NULL) {
2273
    errno = EINVAL;
12✔
2274
    return -1;
12✔
2275
  }
2276

2277
  if (strcasecmp(type, "user") != 0 &&
16✔
2278
      strcasecmp(type, "group") != 0) {
5✔
2279
    errno = EINVAL;
3✔
2280
    return -1;
3✔
2281
  }
2282

2283
  /* First, sanity check the given list of actions against the actions
2284
   * in the given table.
2285
   */
2286
  for (i = 0; actions[i]; i++) {
6✔
2287
    register unsigned int j = 0;
8✔
2288
    int valid_action = FALSE;
8✔
2289

2290
    if (strcasecmp(actions[i], "all") == 0) {
8✔
2291
      continue;
2✔
2292
    }
2293

2294
    for (j = 0; acttab[j].act_action; j++) {
2✔
2295
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
6✔
2296
        valid_action = TRUE;
2297
        break;
2298
      }
2299
    }
2300

2301
    if (valid_action == FALSE) {
6✔
2302
      *bad_action = actions[i];
2✔
2303
      errno = EPERM;
2✔
2304
      return -1;
2✔
2305
    }
2306
  }
2307

2308
  for (i = 0; actions[i]; i++) {
6✔
2309
    register unsigned int j = 0;
6✔
2310

2311
    if (all_actions == FALSE &&
12✔
2312
        strcasecmp(actions[i], "all") == 0) {
6✔
2313
      all_actions = TRUE;
2✔
2314
    }
2315

2316
    for (j = 0; acttab[j].act_action; j++) {
12✔
2317
      int res = 0;
6✔
2318

2319
      if (all_actions == TRUE ||
10✔
2320
          strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2321

2322
        /* Use the type parameter to determine whether the list is of users or
2323
         * of groups.
2324
         */
2325
        if (strcasecmp(type, "user") == 0) {
6✔
2326
          res = pr_ctrls_set_user_acl(acl_pool,
4✔
2327
            &(acttab[j].act_acl->acl_users), allow, list);
4✔
2328

2329
        } else if (strcasecmp(type, "group") == 0) {
2✔
2330
          res = pr_ctrls_set_group_acl(acl_pool,
2✔
2331
            &(acttab[j].act_acl->acl_groups), allow, list);
2✔
2332
        }
2333

2334
        if (res < 0) {
6✔
2335
          *bad_action = actions[i];
×
2336
          return -1;
×
2337
        }
2338
      }
2339
    }
2340
  }
2341

2342
  return 0;
2343
}
2344

2345
char *pr_ctrls_set_module_acls(ctrls_acttab_t *acttab, pool *acl_pool,
11✔
2346
    char **actions, const char *allow, const char *type, char *list) {
2347
  int res;
2348
  char *bad_action = NULL;
11✔
2349

2350
  res = pr_ctrls_set_module_acls2(acttab, acl_pool, actions, allow, type, list,
11✔
2351
    (const char **) &bad_action);
2352
  if (res < 0) {
11✔
2353
    return bad_action;
8✔
2354
  }
2355

2356
  return 0;
2357
}
2358

2359
int pr_ctrls_unregister_module_actions2(ctrls_acttab_t *acttab,
11✔
2360
    char **actions, module *mod, const char **bad_action) {
2361
  register unsigned int i = 0;
11✔
2362

2363
  if (acttab == NULL ||
22✔
2364
      actions == NULL ||
11✔
2365
      mod == NULL ||
14✔
2366
      bad_action == NULL) {
7✔
2367
    errno = EINVAL;
7✔
2368
    return -1;
7✔
2369
  }
2370

2371
  /* First, sanity check the given actions against the actions supported by
2372
   * this module.
2373
   */
2374
  for (i = 0; actions[i]; i++) {
2✔
2375
    register unsigned int j = 0;
2376
    int valid_action = FALSE;
2377

2378
    for (j = 0; acttab[j].act_action; j++) {
2✔
2379
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2380
        valid_action = TRUE;
2381
        break;
2382
      }
2383
    }
2384

2385
    if (valid_action == FALSE) {
4✔
2386
      *bad_action = actions[i];
2✔
2387
      errno = EPERM;
2✔
2388
      return -1;
2✔
2389
    }
2390
  }
2391

2392
  /* Next, iterate through both lists again, looking for actions of the
2393
   * module _not_ in the given list.
2394
   */
2395
  for (i = 0; acttab[i].act_action; i++) {
2✔
2396
    register unsigned int j = 0;
2397
    int have_action = FALSE;
2398

2399
    for (j = 0; actions[j]; j++) {
×
2400
      if (strcmp(acttab[i].act_action, actions[j]) == 0) {
2✔
2401
        have_action = TRUE;
2402
        break;
2403
      }
2404
    }
2405

2406
    if (have_action == TRUE) {
2✔
2407
      pr_trace_msg(trace_channel, 4, "mod_%s.c: removing '%s' control",
2✔
2408
        mod->name, acttab[i].act_action);
2409
      pr_ctrls_unregister(mod, acttab[i].act_action);
2✔
2410
      destroy_pool(acttab[i].act_acl->acl_pool);
2✔
2411
    }
2412
  }
2413

2414
  return 0;
2415
}
2416

2417
char *pr_ctrls_unregister_module_actions(ctrls_acttab_t *acttab,
5✔
2418
    char **actions, module *mod) {
2419
  int res;
2420
  char *bad_action = NULL;
5✔
2421

2422
  res = pr_ctrls_unregister_module_actions2(acttab, actions, mod,
5✔
2423
    (const char **) &bad_action);
2424
  if (res < 0) {
5✔
2425
    return bad_action;
4✔
2426
  }
2427

2428
  return 0;
2429
}
2430

2431
int pr_ctrls_set_logfd(int fd) {
4✔
2432

2433
  /* Close any existing log fd. */
2434
  if (ctrls_logfd >= 0) {
4✔
2435
    (void) close(ctrls_logfd);
1✔
2436
  }
2437

2438
  ctrls_logfd = fd;
4✔
2439
  return 0;
4✔
2440
}
2441

2442
int pr_ctrls_log(const char *module_version, const char *fmt, ...) {
4✔
2443
  va_list msg;
2444
  int res;
2445

2446
  if (ctrls_logfd < 0) {
4✔
2447
    return 0;
2448
  }
2449

2450
  if (fmt == NULL) {
3✔
2451
    return 0;
2452
  }
2453

2454
  va_start(msg, fmt);
1✔
2455
  res = pr_log_vwritefile(ctrls_logfd, module_version, fmt, msg);
1✔
2456
  va_end(msg);
1✔
2457

2458
  return res;
1✔
2459
}
2460

2461
static void ctrls_cleanup_cb(void *user_data) {
36✔
2462
  ctrls_pool = NULL;
36✔
2463
  ctrls_action_list = NULL;
36✔
2464
  ctrls_active_list = NULL;
36✔
2465
  ctrls_free_list = NULL;
36✔
2466

2467
  action_lookup_next = NULL;
36✔
2468
  action_lookup_action = NULL;
36✔
2469
  action_lookup_module = NULL;
36✔
2470
}
36✔
2471

2472
/* Initialize the Controls API. */
2473
int init_ctrls2(const char *socket_path) {
36✔
2474
  struct stat st;
2475
  int fd, xerrno;
2476
  struct sockaddr_un sockun;
2477
  size_t socklen;
2478

2479
  if (ctrls_pool != NULL) {
36✔
2480
    destroy_pool(ctrls_pool);
×
2481
  }
2482

2483
  ctrls_pool = make_sub_pool(permanent_pool);
36✔
2484
  pr_pool_tag(ctrls_pool, "Controls Pool");
36✔
2485
  register_cleanup2(ctrls_pool, NULL, ctrls_cleanup_cb);
36✔
2486

2487
  /* Make sure all of the lists are zero'd out. */
2488
  ctrls_action_list = NULL;
36✔
2489
  ctrls_active_list = NULL;
36✔
2490
  ctrls_free_list = NULL;
36✔
2491

2492
   /* And that the lookup indices are (re)set as well... */
2493
  action_lookup_next = NULL;
36✔
2494
  action_lookup_action = NULL;
36✔
2495
  action_lookup_module = NULL;
36✔
2496

2497
  /* Run-time check to find out whether this platform identifies a
2498
   * Unix domain socket file descriptor via the S_ISFIFO macro, or
2499
   * the S_ISSOCK macro.
2500
   */
2501

2502
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
36✔
2503
  xerrno = errno;
36✔
2504

2505
  if (fd < 0) {
36✔
2506
    pr_log_debug(DEBUG10, "unable to create Unix domain socket: %s",
×
2507
      strerror(xerrno));
2508

2509
    errno = xerrno;
×
2510
    return -1;
×
2511
  }
2512

2513
  memset(&sockun, 0, sizeof(sockun));
36✔
2514
  sockun.sun_family = AF_UNIX;
36✔
2515
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
36✔
2516
  socklen = sizeof(struct sockaddr_un);
36✔
2517

2518
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
36✔
2519
    xerrno = errno;
×
2520

2521
    pr_log_debug(DEBUG10, "unable to bind to Unix domain socket at '%s': %s",
×
2522
      socket_path, strerror(xerrno));
2523
    (void) close(fd);
×
2524
    (void) unlink(socket_path);
×
2525

2526
    errno = xerrno;
×
2527
    return -1;
×
2528
  }
2529

2530
  if (fstat(fd, &st) < 0) {
36✔
2531
    xerrno = errno;
×
2532

2533
    pr_log_debug(DEBUG10, "unable to stat Unix domain socket at '%s': %s",
×
2534
      socket_path, strerror(xerrno));
2535
    (void) close(fd);
×
2536
    (void) unlink(socket_path);
×
2537

2538
    errno = xerrno;
×
2539
    return -1;
×
2540
  }
2541

2542
#if defined(S_ISFIFO)
2543
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISFIFO");
36✔
2544
  if (S_ISFIFO(st.st_mode)) {
36✔
2545
    ctrls_use_isfifo = TRUE;
×
2546
  }
2547
#else
2548
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISFIFO: "
2549
    "macro undefined");
2550
#endif /* S_ISFIFO */
2551

2552
#if defined(S_ISSOCK)
2553
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISSOCK");
36✔
2554
  if (S_ISSOCK(st.st_mode)) {
36✔
2555
    ctrls_use_isfifo = FALSE;
36✔
2556
  }
2557
#else
2558
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISSOCK: "
2559
    "macro undefined");
2560
#endif /* S_ISSOCK */
2561

2562
  pr_trace_msg(trace_channel, 9,
36✔
2563
    "using %s macro for Unix domain socket detection",
2564
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
36✔
2565

2566
  (void) close(fd);
36✔
2567
  (void) unlink(socket_path);
36✔
2568
  return 0;
36✔
2569
}
2570

2571
void init_ctrls(void) {
×
2572
  const char *socket_path = PR_RUN_DIR "/test.sock";
×
2573

2574
  (void) init_ctrls2(socket_path);
×
2575
}
×
2576
#endif /* PR_USE_CTRLS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc