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

proftpd / proftpd / 29116929668

10 Jul 2026 07:06PM UTC coverage: 92.419% (-0.6%) from 93.032%
29116929668

push

github

web-flow
AIX: Avoid `ip_len` macro collision with external headers such as libsodium

48763 of 52763 relevant lines covered (92.42%)

236.98 hits per line

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

78.74
/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_SOCKET_PREFIX        "/tmp/ftp.cl"
46

47
#define CTRLS_MAX_REQ_SIZE        1024
48
#define CTRLS_MAX_RESP_SIZE        (1024 * 1024)
49

50
#define CTRLS_REQ_ACTION_KEY        "action"
51
#define CTRLS_REQ_ARGS_KEY        "args"
52
#define CTRLS_RESP_STATUS_KEY        "status"
53
#define CTRLS_RESP_RESPS_KEY        "responses"
54

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

66
static unsigned char ctrls_blocked = FALSE;
67

68
static pool *ctrls_pool = NULL;
69
static ctrls_action_t *ctrls_action_list = NULL;
70

71
static pr_ctrls_t *ctrls_active_list = NULL;
72
static pr_ctrls_t *ctrls_free_list = NULL;
73

74
static int ctrls_use_isfifo = FALSE;
75

76
static const char *trace_channel = "ctrls";
77

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

83
/* Logging */
84
static int ctrls_logfd = -1;
85

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

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

94
  pr_block_ctrls();
95

96
  /* Get a blank ctrl object */
97
  ctrl = pr_ctrls_alloc();
7✔
98

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

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

111
  pr_unblock_ctrls();
112
  return ctrl;
7✔
113
}
114

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

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

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

125
  return act;
12✔
126
}
127

128
pr_ctrls_t *pr_ctrls_alloc(void) {
27✔
129
  pr_ctrls_t *ctrl = NULL;
27✔
130

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

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

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

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

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

153
  return ctrl;
27✔
154
}
155

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

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

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

169
  } else {
170
    ctrls_active_list = ctrl->ctrls_next;
8✔
171
  }
172

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

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

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

192
  ctrl->ctrls_cb_args = NULL;
8✔
193
  ctrl->ctrls_cb_resps = NULL;
8✔
194
  ctrl->ctrls_data = NULL;
8✔
195

196
  ctrl->ctrls_next = ctrls_free_list;
8✔
197
  ctrls_free_list = ctrl;
8✔
198

199
  pr_unblock_ctrls();
200
  return 0;
8✔
201
}
202

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

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

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

220
  /* Block ctrls while we're doing this */
221
  pr_block_ctrls();
222

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

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

230
    act_id = (unsigned int) pr_random_next(1L, RAND_MAX);
12✔
231

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

240
    if (have_id == FALSE) {
12✔
241
      break;
242
    }
243
  }
244

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

252
  /* Add this to the list of "registered" actions */
253

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

259
  ctrls_action_list = act;
12✔
260

261
  pr_unblock_ctrls();
262
  return act_id;
12✔
263
}
264

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

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

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

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

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

283
      } else {
284
        ctrls_action_list = act->next;
12✔
285
      }
286

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

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

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

300
  pr_unblock_ctrls();
301

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

307
  return 0;
308
}
309

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

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

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

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

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

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

344
  return 0;
8✔
345
}
346

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

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

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

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

376
  return 0;
2✔
377
}
378

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

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

396
  dst_ctrl->ctrls_cb_resps = copy_array(dst_ctrl->ctrls_tmp_pool,
1✔
397
    src_ctrl->ctrls_cb_resps);
398

399
  return 0;
1✔
400
}
401

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

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

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

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

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

431
  buf[sizeof(buf) - 1] = '\0';
12✔
432

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

437
  return 0;
12✔
438
}
439

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

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

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

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

463
  return 0;
464
}
465

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

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

476
  msglen = strlen(msg);
7✔
477

478
  /* No interruptions. */
479
  pr_signals_block();
7✔
480

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

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

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

491
    errno = xerrno;
3✔
492
    return -1;
3✔
493
  }
494

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

499
    if ((size_t) res != msglen) {
4✔
500
      if (xerrno == EAGAIN) {
×
501
        pr_signals_handle();
×
502
        continue;
×
503
      }
504

505
      pr_signals_unblock();
×
506

507
      errno = xerrno;
×
508
      return -1;
×
509
    }
510

511
    break;
512
  }
513

514
  pr_signals_unblock();
4✔
515
  return 0;
4✔
516
}
517

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

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

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

539
  tmp_pool = make_sub_pool(p);
2✔
540
  pr_pool_tag(tmp_pool, "Controls API send_request pool");
2✔
541

542
  json = pr_json_object_alloc(tmp_pool);
2✔
543

544
  res = pr_json_object_set_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY, action);
2✔
545
  xerrno = errno;
2✔
546

547
  if (res < 0) {
2✔
548
    pr_json_object_free(json);
×
549
    destroy_pool(tmp_pool);
×
550

551
    errno = xerrno;
×
552
    return -1;
×
553
  }
554

555
  args = pr_json_array_alloc(tmp_pool);
2✔
556

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

561
    if (res < 0) {
2✔
562
      pr_json_array_free(args);
×
563
      pr_json_object_free(json);
×
564
      destroy_pool(tmp_pool);
×
565

566
      errno = xerrno;
×
567
      return -1;
×
568
    }
569
  }
570

571
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, args);
2✔
572
  xerrno = errno;
2✔
573

574
  if (res < 0) {
2✔
575
    pr_json_array_free(args);
×
576
    pr_json_object_free(json);
×
577
    destroy_pool(tmp_pool);
×
578

579
    errno = xerrno;
×
580
    return -1;
×
581
  }
582

583
  res = ctrls_send_msg(tmp_pool, fd, json);
2✔
584
  xerrno = errno;
2✔
585

586
  pr_json_array_free(args);
2✔
587
  pr_json_object_free(json);
2✔
588
  destroy_pool(tmp_pool);
2✔
589

590
  errno = xerrno;
2✔
591
  return res;
2✔
592
}
593

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

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

610
  if (cl->cl_fd < 0) {
14✔
611
    errno = EBADF;
1✔
612
    return -1;
1✔
613
  }
614

615
  /* No interruptions */
616
  pr_signals_block();
13✔
617

618
  /* Read in the size of the message, as JSON text. */
619

620
  nread = read(cl->cl_fd, &msglen, sizeof(uint32_t));
26✔
621
  xerrno = errno;
13✔
622

623
  if (nread < 0) {
13✔
624
    pr_signals_unblock();
1✔
625

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

633
  /* Watch for short reads. */
634
  if (nread != sizeof(uint32_t)) {
12✔
635
    pr_signals_unblock();
1✔
636

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

644
  tmp_pool = make_sub_pool(cl->cl_pool);
11✔
645
  pr_pool_tag(tmp_pool, "Controls API recv_request pool");
11✔
646

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

651
  /* Impose min/max request size limit here, for Issue #2036. */
652
  if (msglen == 0) {
11✔
653
    destroy_pool(tmp_pool);
×
654
    pr_signals_unblock();
×
655

656
    (void) pr_trace_msg(trace_channel, 3,
×
657
      "message size (%lu bytes) less than min (1 byte), unable to receive "
658
      "response", (unsigned long) msglen);
659
    errno = EINVAL;
×
660
    return -1;
×
661
  }
662

663
  if (msglen > CTRLS_MAX_REQ_SIZE) {
11✔
664
    destroy_pool(tmp_pool);
1✔
665
    pr_signals_unblock();
1✔
666

667
    (void) pr_trace_msg(trace_channel, 3,
1✔
668
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
669
      "request", (unsigned long) msglen, (unsigned long) CTRLS_MAX_REQ_SIZE);
670
    errno = E2BIG;
1✔
671
    return -1;
1✔
672
  }
673

674
  /* Allocate one byte for the terminating NUL. */
675
  msg = pcalloc(tmp_pool, msglen + 1);
10✔
676

677
  nread = read(cl->cl_fd, msg, msglen);
20✔
678
  xerrno = errno;
10✔
679

680
  if (nread < 0) {
10✔
681
    destroy_pool(tmp_pool);
×
682
    pr_signals_unblock();
×
683

684
    pr_trace_msg(trace_channel, 3,
×
685
      "error reading %lu bytes of request message: %s",
686
      (unsigned long) msglen, strerror(xerrno));
687
    errno = xerrno;
×
688
    return -1;
×
689
  }
690

691
  /* Watch for short reads. */
692
  if ((unsigned int) nread != msglen) {
10✔
693
    destroy_pool(tmp_pool);
1✔
694
    pr_signals_unblock();
1✔
695

696
    (void) pr_trace_msg(trace_channel, 3,
1✔
697
      "short read (%d of %u bytes) of message text, unable to receive request",
698
      nread, (unsigned int) msglen);
699
    errno = EPERM;
1✔
700
    return -1;
1✔
701
  }
702

703
  json = pr_json_object_from_text(tmp_pool, msg);
9✔
704
  xerrno = errno;
9✔
705

706
  if (json == NULL) {
9✔
707
    destroy_pool(tmp_pool);
1✔
708
    pr_signals_unblock();
1✔
709

710
    (void) pr_trace_msg(trace_channel, 3,
1✔
711
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
712
      "receive request: %s", (int) msglen, msg, (unsigned long) msglen,
713
      strerror(xerrno));
714
    errno = EINVAL;
1✔
715
    return -1;
1✔
716
  }
717

718
  res = pr_json_object_get_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY,
8✔
719
    &reqaction);
720
  xerrno = errno;
8✔
721

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

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

734
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, &args);
7✔
735
  xerrno = errno;
7✔
736

737
  if (res < 0) {
7✔
738
    pr_json_object_free(json);
1✔
739
    destroy_pool(tmp_pool);
1✔
740
    pr_signals_unblock();
1✔
741

742
    (void) pr_trace_msg(trace_channel, 3,
1✔
743
      "unable to read message arguments (%s), unable to receive request",
744
      strerror(xerrno));
745
    errno = EINVAL;
1✔
746
    return -1;
1✔
747
  }
748

749
  nreqargs = pr_json_array_count(args);
6✔
750
  pr_trace_msg(trace_channel, 19, "received request argc: %u", nreqargs);
6✔
751

752
  /* Find a matching action object, and use it to populate a ctrl object,
753
   * preparing the ctrl object for dispatching to the action handlers.
754
   */
755
  ctrl = ctrls_lookup_action(NULL, reqaction, TRUE);
12✔
756
  if (ctrl == NULL) {
6✔
757
    (void) pr_trace_msg(trace_channel, 3,
1✔
758
      "unknown action requested '%s', unable to receive request", reqaction);
759
    pr_json_array_free(args);
1✔
760
    pr_json_object_free(json);
1✔
761
    destroy_pool(tmp_pool);
1✔
762
    pr_signals_unblock();
1✔
763

764
    /* XXX This is where we could also add "did you mean" functionality. */
765
    errno = EINVAL;
1✔
766
    return -1;
1✔
767
  }
768

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

771
  for (i = 0; i < nreqargs; i++) {
14✔
772
    size_t reqarglen = 0;
4✔
773
    char *reqarg = NULL;
4✔
774

775
    res = pr_json_array_get_string(tmp_pool, args, i, &reqarg);
4✔
776
    xerrno = errno;
4✔
777

778
    if (res < 0) {
4✔
779
      (void) pr_trace_msg(trace_channel, 3,
×
780
        "unable to read message argument #%u (%s), unable to receive request",
781
        i+1, strerror(xerrno));
782
      pr_json_array_free(args);
×
783
      pr_json_object_free(json);
×
784
      destroy_pool(tmp_pool);
×
785
      pr_signals_unblock();
×
786

787
      errno = EINVAL;
×
788
      return -1;
×
789
    }
790

791
    reqarglen = strlen(reqarg);
4✔
792
    res = pr_ctrls_add_arg(ctrl, reqarg, reqarglen);
4✔
793
    xerrno = errno;
4✔
794

795
    if (res < 0) {
4✔
796
      pr_trace_msg(trace_channel, 3,
×
797
        "error adding message argument #%u (%s): %s", i+1, reqarg,
798
        strerror(xerrno));
799
      pr_json_array_free(args);
×
800
      pr_json_object_free(json);
×
801
      destroy_pool(tmp_pool);
×
802
      pr_signals_unblock();
×
803

804
      errno = xerrno;
×
805
      return -1;
×
806
    }
807
  }
808

809
  /* Add this ctrls object to the client object. */
810
  *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = ctrl;
5✔
811

812
  /* Set the flag that this control is ready to go */
813
  ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
5✔
814
  ctrl->ctrls_cl = cl;
5✔
815

816
  /* Copy the populated ctrl object args to ctrl objects for all other
817
   * matching action objects.
818
   */
819
  next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
5✔
820

821
  while (next_ctrl != NULL) {
11✔
822
    (void) pr_ctrls_copy_args(ctrl, next_ctrl);
1✔
823

824
    /* Add this ctrl object to the client object. */
825
    *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = next_ctrl;
1✔
826

827
    /* Set the flag that this control is ready to go. */
828
    next_ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
1✔
829
    next_ctrl->ctrls_cl = cl;
1✔
830

831
    next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
1✔
832
  }
833

834
  pr_json_array_free(args);
5✔
835
  pr_json_object_free(json);
5✔
836
  destroy_pool(tmp_pool);
5✔
837
  pr_signals_unblock();
5✔
838

839
  return 0;
5✔
840
}
841

842
int pr_ctrls_send_response(pool *p, int fd, int status, unsigned int argc,
8✔
843
    char **argv) {
844
  register unsigned int i;
845
  pool *tmp_pool;
846
  int res, xerrno;
847
  pr_json_object_t *json;
848
  pr_json_array_t *resps;
849

850
  if (p == NULL ||
16✔
851
      fd < 0) {
8✔
852
    errno = EINVAL;
2✔
853
    return -1;
2✔
854
  }
855

856
  if (argc > 0 &&
12✔
857
      argv == NULL) {
6✔
858
    errno = EINVAL;
1✔
859
    return -1;
1✔
860
  }
861

862
  tmp_pool = make_sub_pool(p);
5✔
863
  pr_pool_tag(tmp_pool, "Controls API send_response pool");
5✔
864

865
  json = pr_json_object_alloc(tmp_pool);
5✔
866

867
  res = pr_json_object_set_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY,
5✔
868
    (double) status);
869
  xerrno = errno;
5✔
870

871
  if (res < 0) {
5✔
872
    pr_json_object_free(json);
×
873
    destroy_pool(tmp_pool);
×
874

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

879
  resps = pr_json_array_alloc(tmp_pool);
5✔
880

881
  for (i = 0; i < argc; i++) {
9✔
882
    res = pr_json_array_append_string(tmp_pool, resps, argv[i]);
4✔
883
    xerrno = errno;
4✔
884

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

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

895
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, resps);
5✔
896
  xerrno = errno;
5✔
897

898
  if (res < 0) {
5✔
899
    pr_json_array_free(resps);
×
900
    pr_json_object_free(json);
×
901
    destroy_pool(tmp_pool);
×
902

903
    errno = xerrno;
×
904
    return -1;
×
905
  }
906

907
  res = ctrls_send_msg(tmp_pool, fd, json);
5✔
908
  xerrno = errno;
5✔
909

910
  pr_json_array_free(resps);
5✔
911
  pr_json_object_free(json);
5✔
912
  destroy_pool(tmp_pool);
5✔
913

914
  errno = xerrno;
5✔
915
  return res;
5✔
916
}
917

918
int pr_ctrls_recv_response(pool *p, int fd, int *status, char ***respargv) {
11✔
919
  register int i = 0;
11✔
920
  pool *tmp_pool;
921
  int nread, res, respargc = 0, xerrno;
11✔
922
  uint32_t msglen = 0;
11✔
923
  char *msg = NULL;
11✔
924
  pr_json_object_t *json = NULL;
11✔
925
  pr_json_array_t *resps = NULL;
11✔
926
  double dv;
927
  array_header *resparr = NULL;
11✔
928

929
  /* Sanity checks */
930
  if (p == NULL ||
22✔
931
      fd < 0 ||
20✔
932
      status == NULL) {
933
    errno = EINVAL;
3✔
934
    return -1;
3✔
935
  }
936

937
  /* No interruptions. */
938
  pr_signals_block();
8✔
939

940
  /* Read in the size of the message, as JSON text. */
941

942
  nread = read(fd, &msglen, sizeof(uint32_t));
8✔
943
  xerrno = errno;
8✔
944

945
  if (nread < 0) {
8✔
946
    pr_signals_unblock();
×
947

948
    pr_trace_msg(trace_channel, 3,
×
949
      "error reading %lu bytes of response message size: %s",
950
      sizeof(msglen), strerror(xerrno));
951

952
    errno = xerrno;
×
953
    return -1;
×
954
  }
955

956
  /* Watch for short reads. */
957
  if (nread != sizeof(uint32_t)) {
8✔
958
    pr_signals_unblock();
1✔
959

960
    (void) pr_trace_msg(trace_channel, 3,
1✔
961
      "short read (%d of %u bytes) of response message, unable to receive "
962
      "response", nread, (unsigned int) sizeof(uint32_t));
963
    errno = EPERM;
1✔
964
    return -1;
1✔
965
  }
966

967
  tmp_pool = make_sub_pool(p);
7✔
968
  pr_pool_tag(tmp_pool, "Controls API recv_response pool");
7✔
969

970
  pr_trace_msg(trace_channel, 27,
7✔
971
    "receiving Controls response message (%lu bytes) from fd %d",
972
    (unsigned long) msglen, fd);
973

974
  /* Impose min/max response size limit here, for Issue #2036. */
975
  if (msglen == 0) {
7✔
976
    destroy_pool(tmp_pool);
×
977
    pr_signals_unblock();
×
978

979
    (void) pr_trace_msg(trace_channel, 3,
×
980
      "message size (%lu bytes) less than min (1 byte), unable to receive "
981
      "response", (unsigned long) msglen);
982
    errno = EINVAL;
×
983
    return -1;
×
984
  }
985

986
  if (msglen > CTRLS_MAX_RESP_SIZE) {
7✔
987
    destroy_pool(tmp_pool);
1✔
988
    pr_signals_unblock();
1✔
989

990
    (void) pr_trace_msg(trace_channel, 3,
1✔
991
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
992
      "response", (unsigned long) msglen, (unsigned long) CTRLS_MAX_RESP_SIZE);
993
    errno = E2BIG;
1✔
994
    return -1;
1✔
995
  }
996

997
  /* Allocate one byte for the terminating NUL. */
998
  msg = pcalloc(tmp_pool, msglen + 1);
6✔
999
  nread = read(fd, msg, msglen);
12✔
1000
  xerrno = errno;
6✔
1001

1002
  if (nread < 0) {
6✔
1003
    destroy_pool(tmp_pool);
×
1004
    pr_signals_unblock();
×
1005

1006
    pr_trace_msg(trace_channel, 3,
×
1007
      "error reading %lu bytes of response message: %s",
1008
      (unsigned long) msglen, strerror(xerrno));
1009
    errno = xerrno;
×
1010
    return -1;
×
1011
  }
1012

1013
  /* Watch for short reads. */
1014
  if ((unsigned int) nread != msglen) {
6✔
1015
    destroy_pool(tmp_pool);
1✔
1016
    pr_signals_unblock();
1✔
1017

1018
    (void) pr_trace_msg(trace_channel, 3,
1✔
1019
      "short read (%d of %u bytes) of message text, unable to receive response",
1020
      nread, (unsigned int) msglen);
1021
    errno = EPERM;
1✔
1022
    return -1;
1✔
1023
  }
1024

1025
  json = pr_json_object_from_text(tmp_pool, msg);
5✔
1026
  xerrno = errno;
5✔
1027

1028
  if (json == NULL) {
5✔
1029
    destroy_pool(tmp_pool);
1✔
1030
    pr_signals_unblock();
1✔
1031

1032
    (void) pr_trace_msg(trace_channel, 3,
1✔
1033
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
1034
      "receive response: %s", (int) msglen, msg, (unsigned long) msglen,
1035
      strerror(xerrno));
1036
    errno = EINVAL;
1✔
1037
    return -1;
1✔
1038
  }
1039

1040
  res = pr_json_object_get_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY, &dv);
4✔
1041
  xerrno = errno;
4✔
1042

1043
  if (res < 0) {
4✔
1044
    pr_json_object_free(json);
1✔
1045
    destroy_pool(tmp_pool);
1✔
1046
    pr_signals_unblock();
1✔
1047

1048
    (void) pr_trace_msg(trace_channel, 3,
1✔
1049
      "unable to read response status (%s), unable to receive response",
1050
      strerror(xerrno));
1051
    errno = EINVAL;
1✔
1052
    return -1;
1✔
1053
  }
1054

1055
  *status = (int) dv;
3✔
1056
  pr_trace_msg(trace_channel, 19, "received response status: %d", *status);
3✔
1057

1058
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, &resps);
3✔
1059
  xerrno = errno;
3✔
1060

1061
  if (res < 0) {
3✔
1062
    (void) pr_trace_msg(trace_channel, 3,
1✔
1063
      "unable to read message responses (%s), unable to receive response",
1064
      strerror(xerrno));
1065
    pr_json_object_free(json);
1✔
1066
    destroy_pool(tmp_pool);
1✔
1067
    pr_signals_unblock();
1✔
1068

1069
    errno = EINVAL;
1✔
1070
    return -1;
1✔
1071
  }
1072

1073
  respargc = pr_json_array_count(resps);
2✔
1074
  pr_trace_msg(trace_channel, 19, "received response argc: %u", respargc);
2✔
1075

1076
  resparr = make_array(p, 0, sizeof(char *));
2✔
1077

1078
  /* Read each response, and add it to the array */
1079
  for (i = 0; i < respargc; i++) {
3✔
1080
    char *resp = NULL;
2✔
1081

1082
    /* TODO: Handle other response types, such as arrays or objects, for
1083
     * more complex responses.  Think of an action that dumps the memory
1084
     * pools, for example.
1085
     */
1086
    res = pr_json_array_get_string(tmp_pool, resps, i, &resp);
2✔
1087
    xerrno = errno;
2✔
1088

1089
    if (res < 0) {
2✔
1090
      (void) pr_trace_msg(trace_channel, 3,
1✔
1091
        "unable to read message response #%u (%s), unable to receive response",
1092
        i+1, strerror(xerrno));
1093
      pr_json_array_free(resps);
1✔
1094
      pr_json_object_free(json);
1✔
1095
      destroy_pool(tmp_pool);
1✔
1096
      pr_signals_unblock();
1✔
1097

1098
      errno = EINVAL;
1✔
1099
      return -1;
1✔
1100
    }
1101

1102
    *((char **) push_array(resparr)) = pstrdup(p, resp);
1✔
1103
  }
1104

1105
  if (respargv != NULL) {
1✔
1106
    *respargv = ((char **) resparr->elts);
×
1107
  }
1108

1109
  pr_json_array_free(resps);
1✔
1110
  pr_json_object_free(json);
1✔
1111
  destroy_pool(tmp_pool);
1✔
1112
  pr_signals_unblock();
1✔
1113

1114
  return respargc;
1✔
1115
}
1116

1117
static pr_ctrls_t *ctrls_lookup_action(module *mod, const char *action,
×
1118
    unsigned char skip_disabled) {
1119

1120
  /* (Re)set the current indices */
1121
  action_lookup_next = ctrls_action_list;
7✔
1122
  action_lookup_action = action;
7✔
1123
  action_lookup_module = mod;
7✔
1124

1125
  /* Wrapper around ctrls_lookup_next_action() */
1126
  return ctrls_lookup_next_action(mod, skip_disabled);
7✔
1127
}
1128

1129
static pr_ctrls_t *ctrls_lookup_next_action(module *mod,
13✔
1130
    unsigned char skip_disabled) {
1131
  register ctrls_action_t *act = NULL;
13✔
1132

1133
  /* Sanity check */
1134
  if (action_lookup_action == NULL) {
13✔
1135
    errno = EINVAL;
×
1136
    return NULL;
×
1137
  }
1138

1139
  if (mod != action_lookup_module) {
13✔
1140
    return ctrls_lookup_action(mod, action_lookup_action, skip_disabled);
×
1141
  }
1142

1143
  for (act = action_lookup_next; act; act = act->next) {
13✔
1144
    if (skip_disabled && (act->flags & PR_CTRLS_ACT_DISABLED)) {
7✔
1145
      continue;
×
1146
    }
1147

1148
    if (strcmp(act->action, action_lookup_action) == 0 &&
14✔
1149
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
13✔
1150
      action_lookup_next = act->next;
7✔
1151

1152
      /* Use this action object to prepare a ctrl object. */
1153
      return ctrls_prepare(act);
7✔
1154
    }
1155
  }
1156

1157
  return NULL;
1158
}
1159

1160
int pr_get_registered_actions(pr_ctrls_t *ctrl, int flags) {
7✔
1161
  register ctrls_action_t *act = NULL;
7✔
1162
  int count = 0;
7✔
1163

1164
  if (ctrl == NULL) {
7✔
1165
    errno = EINVAL;
1✔
1166
    return -1;
1✔
1167
  }
1168

1169
  /* Are ctrls blocked? */
1170
  if (ctrls_blocked == TRUE) {
6✔
1171
    errno = EPERM;
1✔
1172
    return -1;
1✔
1173
  }
1174

1175
  for (act = ctrls_action_list; act; act = act->next) {
13✔
1176
    switch (flags) {
8✔
1177
      case CTRLS_GET_ACTION_ALL:
2✔
1178
        if (act->module != NULL) {
2✔
1179
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1180
            act->module->name);
1181

1182
        } else {
1183
           pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1✔
1184
        }
1185

1186
        count++;
2✔
1187
        break;
2✔
1188

1189
      case CTRLS_GET_ACTION_ENABLED:
2✔
1190
        if (act->flags & PR_CTRLS_ACT_DISABLED) {
2✔
1191
          continue;
×
1192
        }
1193

1194
        if (act->module != NULL) {
2✔
1195
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1196
            act->module->name);
1197

1198
        } else {
1199
          pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1✔
1200
        }
1201

1202
        count++;
2✔
1203
        break;
2✔
1204

1205
      case CTRLS_GET_DESC:
2✔
1206
        pr_ctrls_add_response(ctrl, "%s: %s", act->action,
2✔
1207
          act->desc);
1208
        count++;
2✔
1209
        break;
2✔
1210
    }
1211
  }
1212

1213
  return count;
1214
}
1215

1216
int pr_set_registered_actions(module *mod, const char *action,
7✔
1217
    unsigned char skip_disabled, unsigned int flags) {
1218
  register ctrls_action_t *act = NULL;
7✔
1219
  unsigned char have_action = FALSE;
7✔
1220

1221
  /* Is flags a valid combination of settable flags? */
1222
  if (flags > 0 &&
14✔
1223
      flags != PR_CTRLS_ACT_SOLITARY &&
7✔
1224
      flags != PR_CTRLS_ACT_DISABLED &&
2✔
1225
      flags != (PR_CTRLS_ACT_SOLITARY|PR_CTRLS_ACT_DISABLED)) {
1✔
1226
    errno = EINVAL;
1✔
1227
    return -1;
1✔
1228
  }
1229

1230
  /* Are ctrls blocked? */
1231
  if (ctrls_blocked == TRUE) {
6✔
1232
    errno = EPERM;
1✔
1233
    return -1;
1✔
1234
  }
1235

1236
  for (act = ctrls_action_list; act; act = act->next) {
10✔
1237
    if (skip_disabled == TRUE &&
5✔
1238
        (act->flags & PR_CTRLS_ACT_DISABLED)) {
×
1239
      continue;
×
1240
    }
1241

1242
    if ((action == NULL ||
9✔
1243
         strcmp(action, "all") == 0 ||
7✔
1244
         strcmp(act->action, action) == 0) &&
8✔
1245
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
5✔
1246
      have_action = TRUE;
5✔
1247
      act->flags = flags;
5✔
1248
    }
1249
  }
1250

1251
  if (have_action == FALSE) {
5✔
1252
    errno = ENOENT;
1✔
1253
    return -1;
1✔
1254
  }
1255

1256
  return 0;
1257
}
1258

1259
#if !defined(SO_PEERCRED) && \
1260
    !defined(HAVE_GETPEEREID) && \
1261
    !defined(HAVE_GETPEERUCRED) && \
1262
    defined(LOCAL_CREDS)
1263
static int ctrls_connect_local_creds(int fd) {
1264
  char buf[1] = {'\0'};
1265
  int res;
1266

1267
  /* The backend doesn't care what we send here, but it wants
1268
   * exactly one character to force recvmsg() to block and wait
1269
   * for us.
1270
   */
1271

1272
  res = write(fd, buf, 1);
1273
  while (res < 0) {
1274
    if (errno == EINTR) {
1275
      pr_signals_handle();
1276

1277
      res = write(fd, buf, 1);
1278
      continue;
1279
    }
1280

1281
    pr_trace_msg(trace_channel, 5,
1282
      "error writing credentials byte for LOCAL_CREDS to fd %d: %s", fd,
1283
      strerror(errno));
1284
    return -1;
1285
  }
1286

1287
  return res;
1288
}
1289
#endif /* !SCM_CREDS */
1290

1291
int pr_ctrls_connect(const char *socket_file) {
6✔
1292
  int fd = -1, len = 0;
6✔
1293
  struct sockaddr_un cl_sock, ctrl_sock;
1294
  struct stat st;
1295

1296
  if (socket_file == NULL) {
6✔
1297
    errno = EINVAL;
1✔
1298
    return -1;
1✔
1299
  }
1300

1301
  /* No interruptions */
1302
  pr_signals_block();
5✔
1303

1304
  /* Create a Unix domain socket */
1305
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
5✔
1306
  if (fd < 0) {
5✔
1307
    int xerrno = errno;
×
1308

1309
    pr_signals_unblock();
×
1310

1311
    errno = xerrno;
×
1312
    return -1;
×
1313
  }
1314

1315
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
5✔
1316
    int xerrno = errno;
×
1317

1318
    (void) close(fd);
×
1319
    pr_signals_unblock();
×
1320

1321
    errno = xerrno;
×
1322
    return -1;
×
1323
  }
1324

1325
  /* Fill in the socket address */
1326
  memset(&cl_sock, 0, sizeof(cl_sock));
5✔
1327

1328
  /* This first part is clever.  First, this process creates a socket in
1329
   * the file system.  It _then_ connect()s to the server.  Upon accept()ing
1330
   * the connection, the server examines the created socket to see that it
1331
   * is indeed a socket, with the proper mode and time.  Clever, but not
1332
   * ideal.
1333
   */
1334

1335
  cl_sock.sun_family = AF_UNIX;
5✔
1336
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
5✔
1337
    CTRLS_SOCKET_PREFIX, (unsigned int) getpid());
5✔
1338
  len = sizeof(cl_sock);
5✔
1339

1340
  /* Make sure the file doesn't already exist.  If it does exist AND is
1341
   * a file or socket, we can remove it.
1342
   */
1343
  if (lstat(cl_sock.sun_path, &st) == 0) {
5✔
1344
    if (S_ISDIR(st.st_mode) ||
1✔
1345
        S_ISLNK(st.st_mode)) {
1346
      pr_trace_msg(trace_channel, 6,
1✔
1347
        "client path '%s' exists and is not a socket", cl_sock.sun_path);
1348

1349
      (void) close(fd);
1✔
1350
      pr_signals_unblock();
1✔
1351

1352
      errno = EEXIST;
1✔
1353
      return -1;
1✔
1354
    }
1355
  }
1356

1357
  (void) unlink(cl_sock.sun_path);
4✔
1358

1359
  /* Make it a socket */
1360
  if (bind(fd, (struct sockaddr *) &cl_sock, len) < 0) {
4✔
1361
    int xerrno = errno;
×
1362

1363
    pr_trace_msg(trace_channel, 19, "error binding local socket to '%s': %s",
×
1364
      cl_sock.sun_path, strerror(xerrno));
1365
    (void) unlink(cl_sock.sun_path);
×
1366
    (void) close(fd);
×
1367
    pr_signals_unblock();
×
1368

1369
    errno = xerrno;
×
1370
    return -1;
×
1371
  }
1372

1373
  /* Set the proper mode */
1374
  if (chmod(cl_sock.sun_path, PR_CTRLS_CL_MODE) < 0) {
4✔
1375
    int xerrno = errno;
×
1376

1377
    pr_trace_msg(trace_channel, 19, "error setting local socket mode: %s",
×
1378
      strerror(xerrno));
1379
    (void) unlink(cl_sock.sun_path);
×
1380
    (void) close(fd);
×
1381
    pr_signals_unblock();
×
1382

1383
    errno = xerrno;
×
1384
    return -1;
×
1385
  }
1386

1387
  /* Now connect to the real server */
1388
  memset(&ctrl_sock, 0, sizeof(ctrl_sock));
4✔
1389

1390
  ctrl_sock.sun_family = AF_UNIX;
4✔
1391
  sstrncpy(ctrl_sock.sun_path, socket_file, sizeof(ctrl_sock.sun_path));
4✔
1392
  len = sizeof(ctrl_sock);
4✔
1393

1394
  if (connect(fd, (struct sockaddr *) &ctrl_sock, len) < 0) {
4✔
1395
    int xerrno = errno;
3✔
1396

1397
    pr_trace_msg(trace_channel, 19, "error connecting to local socket '%s': %s",
3✔
1398
      ctrl_sock.sun_path, strerror(xerrno));
1399
    (void) unlink(cl_sock.sun_path);
3✔
1400
    (void) close(fd);
3✔
1401
    pr_signals_unblock();
3✔
1402

1403
    errno = xerrno;
3✔
1404
    return -1;
3✔
1405
  }
1406

1407
#if !defined(SO_PEERCRED) && \
1408
    !defined(HAVE_GETPEEREID) && \
1409
    !defined(HAVE_GETPEERUCRED) && \
1410
    defined(LOCAL_CREDS)
1411
  if (ctrls_connect_local_creds(fd) < 0) {
1412
    int xerrno = errno;
1413

1414
    pr_trace_msg(trace_channel, 19, "error sending creds to local socket: %s",
1415
      strerror(xerrno));
1416
    (void) unlink(cl_sock.sun_path);
1417
    (void) close(fd);
1418
    pr_signals_unblock();
1419

1420
    errno = xerrno;
1421
    return -1;
1422
  }
1423
#endif /* LOCAL_CREDS */
1424

1425
  pr_signals_unblock();
1✔
1426
  return fd;
1✔
1427
}
1428

1429
int pr_ctrls_issock_unix(mode_t sock_mode) {
3✔
1430

1431
  if (ctrls_use_isfifo == TRUE) {
3✔
1432
#if defined(S_ISFIFO)
1433
    if (S_ISFIFO(sock_mode)) {
×
1434
      return 0;
1435
    }
1436
#endif /* S_ISFIFO */
1437
  } else {
1438
#if defined(S_ISSOCK)
1439
    if (S_ISSOCK(sock_mode)) {
3✔
1440
      return 0;
1441
    }
1442
#endif /* S_ISSOCK */
1443
  }
1444

1445
  errno = ENOSYS;
2✔
1446
  return -1;
2✔
1447
}
1448

1449
#if defined(SO_PEERCRED)
1450
static int ctrls_get_creds_peercred(int fd, uid_t *uid, gid_t *gid,
×
1451
    pid_t *pid) {
1452
# if defined(HAVE_STRUCT_SOCKPEERCRED)
1453
  struct sockpeercred cred;
1454
# else
1455
  struct ucred cred;
1456
# endif /* HAVE_STRUCT_SOCKPEERCRED */
1457
  socklen_t cred_len;
1458

1459
  cred_len = sizeof(cred);
×
1460
  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) {
×
1461
    int xerrno = errno;
×
1462

1463
    pr_trace_msg(trace_channel, 2,
×
1464
      "error obtaining peer credentials using SO_PEERCRED: %s",
1465
      strerror(xerrno));
1466

1467
    errno = EPERM;
×
1468
    return -1;
×
1469
  }
1470

1471
  if (uid != NULL) {
×
1472
    *uid = cred.uid;
×
1473
  }
1474

1475
  if (gid != NULL) {
×
1476
    *gid = cred.gid;
×
1477
  }
1478

1479
  if (pid != NULL) {
×
1480
    *pid = cred.pid;
×
1481
  }
1482

1483
  return 0;
1484
}
1485
#endif /* SO_PEERCRED */
1486

1487
#if !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1488
static int ctrls_get_creds_peereid(int fd, uid_t *uid, gid_t *gid) {
1489
  if (getpeereid(fd, uid, gid) < 0) {
1490
    int xerrno = errno;
1491

1492
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1493
      "getpeereid(2) on fd %d: %s", fd, strerror(xerrno));
1494

1495
    errno = xerrno;
1496
    return -1;
1497
  }
1498

1499
  return 0;
1500
}
1501
#endif /* !HAVE_GETPEEREID */
1502

1503
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1504
    defined(HAVE_GETPEERUCRED)
1505
static int ctrls_get_creds_peerucred(int fd, uid_t *uid, gid_t *gid) {
1506
  ucred_t *cred = NULL;
1507

1508
  if (getpeerucred(fd, &cred) < 0) {
1509
    int xerrno = errno;
1510

1511
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1512
      "getpeerucred(3) on fd %d: %s", fd, strerror(xerrno));
1513

1514
    errno = xerrno;
1515
    return -1;
1516
  }
1517

1518
  if (uid != NULL) {
1519
    *uid = ucred_getruid(cred);
1520
  }
1521

1522
  if (gid != NULL) {
1523
    *gid = ucred_getrgid(cred);
1524
  }
1525

1526
  ucred_free(cred);
1527
  return 0;
1528
}
1529
#endif /* !HAVE_GETPEERUCRED */
1530

1531
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1532
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1533
static int ctrls_get_creds_local(int fd, uid_t *uid, gid_t *gid,
1534
    pid_t *pid) {
1535
  int res;
1536
  char buf[1];
1537
  struct iovec iov;
1538
  struct msghdr msg;
1539

1540
# if defined(SOCKCREDSIZE)
1541
#  define MINCREDSIZE                (sizeof(struct cmsghdr) + SOCKCREDSIZE(0))
1542
# else
1543
#  if defined(HAVE_STRUCT_CMSGCRED)
1544
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
1545
#  elif defined(HAVE_STRUCT_SOCKCRED)
1546
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct sockcred))
1547
#  endif
1548
# endif /* !SOCKCREDSIZE */
1549

1550
  char control[MINCREDSIZE];
1551

1552
  iov.iov_base = buf;
1553
  iov.iov_len = 1;
1554

1555
  memset(&msg, 0, sizeof(msg));
1556
  msg.msg_iov = &iov;
1557
  msg.msg_iovlen = 1;
1558
  msg.msg_control = control;
1559
  msg.msg_controllen = sizeof(control);
1560
  msg.msg_flags = 0;
1561

1562
  res = recvmsg(fd, &msg, 0);
1563
  while (res < 0) {
1564
    int xerrno = errno;
1565

1566
    if (xerrno == EINTR) {
1567
      pr_signals_handle();
1568

1569
      res = recvmsg(fd, &msg, 0);
1570
      continue;
1571
    }
1572

1573
    pr_trace_msg(trace_channel, 6,
1574
      "error calling recvmsg() on fd %d: %s", fd, strerror(xerrno));
1575

1576
    errno = xerrno;
1577
    return -1;
1578
  }
1579

1580
  if (msg.msg_controllen > 0) {
1581
#if defined(HAVE_STRUCT_CMSGCRED)
1582
    struct cmsgcred cred;
1583
#elif defined(HAVE_STRUCT_SOCKCRED)
1584
    struct sockcred cred;
1585
#endif /* !CMSGCRED and !SOCKCRED */
1586

1587
    struct cmsghdr *hdr = (struct cmsghdr *) control;
1588

1589
    if (hdr->cmsg_level != SOL_SOCKET) {
1590
      pr_trace_msg(trace_channel, 5,
1591
        "message received via recvmsg() on fd %d was not a SOL_SOCKET message",
1592
        fd);
1593

1594
      errno = EINVAL;
1595
      return -1;
1596
    }
1597

1598
    if (hdr->cmsg_len < MINCREDSIZE) {
1599
      pr_trace_msg(trace_channel, 5,
1600
        "message received via recvmsg() on fd %d was not of proper "
1601
        "length (%u bytes)", fd, MINCREDSIZE);
1602

1603
      errno = EINVAL;
1604
      return -1;
1605
    }
1606

1607
    if (hdr->cmsg_type != SCM_CREDS) {
1608
      pr_trace_msg(trace_channel, 5,
1609
        "message received via recvmsg() on fd %d was not of type SCM_CREDS",
1610
        fd);
1611

1612
      errno = EINVAL;
1613
      return -1;
1614
    }
1615

1616
#if defined(HAVE_STRUCT_CMSGCRED)
1617
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct cmsgcred));
1618

1619
    if (uid != NULL) {
1620
      *uid = cred.cmcred_uid;
1621
    }
1622

1623
    if (gid != NULL) {
1624
      *gid = cred.cmcred_gid;
1625
    }
1626

1627
    if (pid != NULL) {
1628
      *pid = cred.cmcred_pid;
1629
    }
1630

1631
#elif defined(HAVE_STRUCT_SOCKCRED)
1632
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct sockcred));
1633

1634
    if (uid != NULL) {
1635
      *uid = cred.sc_uid;
1636
    }
1637

1638
    if (gid != NULL) {
1639
      *gid = cred.sc_gid;
1640
    }
1641
#endif
1642

1643
    return 0;
1644
  }
1645

1646
  return -1;
1647
}
1648
#endif /* !SCM_CREDS */
1649

1650
static int ctrls_get_creds_basic(struct sockaddr_un *sock, int cl_fd,
×
1651
    unsigned int max_age, uid_t *uid, gid_t *gid, pid_t *pid) {
1652
  pid_t cl_pid = 0;
×
1653
  char *tmp = NULL, *endp = NULL;
×
1654
  time_t stale_time;
1655
  struct stat st;
1656
  unsigned long file_pid = 0;
×
1657

1658
  /* Check the path -- hmmm... */
1659
  PRIVS_ROOT
×
1660
  while (lstat(sock->sun_path, &st) < 0) {
×
1661
    int xerrno = errno;
×
1662

1663
    if (xerrno == EINTR) {
×
1664
      pr_signals_handle();
×
1665
      continue;
×
1666
    }
1667

1668
    PRIVS_RELINQUISH
×
1669
    pr_trace_msg(trace_channel, 2, "error: unable to stat %s: %s",
×
1670
      sock->sun_path, strerror(xerrno));
1671

1672
    errno = xerrno;
×
1673
    return -1;
×
1674
  }
1675
  PRIVS_RELINQUISH
×
1676

1677
  /* Is it a socket? */
1678
  if (pr_ctrls_issock_unix(st.st_mode) < 0) {
×
1679
    errno = ENOTSOCK;
×
1680
    return -1;
×
1681
  }
1682

1683
  /* Are the perms _not_ rwx------? */
1684
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
×
1685
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
1686
    pr_trace_msg(trace_channel, 3,
×
1687
      "error: unable to accept connection: incorrect mode");
1688
    errno = EPERM;
×
1689
    return -1;
×
1690
  }
1691

1692
  /* Is it new enough? */
1693
  stale_time = time(NULL) - max_age;
×
1694

1695
  if (st.st_atime < stale_time ||
×
1696
      st.st_ctime < stale_time ||
×
1697
      st.st_mtime < stale_time) {
×
1698
    pool *tmp_pool;
1699
    char *msg = "error: stale connection";
×
1700

1701
    pr_trace_msg(trace_channel, 3,
×
1702
      "unable to accept connection: stale connection");
1703

1704
    /* Log the times being compared, to aid in debugging this situation. */
1705
    if (st.st_atime < stale_time) {
×
1706
      time_t age = stale_time - st.st_atime;
×
1707

1708
      pr_trace_msg(trace_channel, 3,
×
1709
        "last access time of '%s' is %lu secs old (must be less than %u secs)",
1710
        sock->sun_path, (unsigned long) age, max_age);
1711
    }
1712

1713
    if (st.st_ctime < stale_time) {
×
1714
      time_t age = stale_time - st.st_ctime;
×
1715

1716
      pr_trace_msg(trace_channel, 3,
×
1717
        "last change time of '%s' is %lu secs old (must be less than %u secs)",
1718
        sock->sun_path, (unsigned long) age, max_age);
1719
    }
1720

1721
    if (st.st_mtime < stale_time) {
×
1722
      time_t age = stale_time - st.st_mtime;
×
1723

1724
      pr_trace_msg(trace_channel, 3,
×
1725
        "last modified time of '%s' is %lu secs old (must be less than %u "
1726
        "secs)", sock->sun_path, (unsigned long) age, max_age);
1727
    }
1728

1729
    tmp_pool = make_sub_pool(permanent_pool);
×
1730

1731
    if (pr_ctrls_send_response(tmp_pool, cl_fd, -1, 1, &msg) < 0) {
×
1732
      pr_trace_msg(trace_channel, 2, "error sending message: %s",
×
1733
        strerror(errno));
×
1734
    }
1735

1736
    destroy_pool(tmp_pool);
×
1737

1738
    errno = ETIMEDOUT;
×
1739
    return -1;
1740
  }
1741

1742
  /* Parse the PID out of the path */
1743
  tmp = sock->sun_path;
×
1744
  tmp += strlen(CTRLS_SOCKET_PREFIX);
×
1745

1746
  file_pid = strtoul(tmp, &endp, 10);
×
1747
  if (endp != NULL &&
×
1748
      *endp != '\0') {
×
1749
    pr_trace_msg(trace_channel, 3,
×
1750
      "error: unable to accept connection: invalid PID");
1751
    errno = EINVAL;
×
1752
    return -1;
×
1753
  }
1754

1755
  cl_pid = (pid_t) file_pid;
×
1756

1757
  /* Return the IDs of the caller */
1758
  *uid = st.st_uid;
×
1759
  *gid = st.st_gid;
×
1760
  *pid = cl_pid;
×
1761

1762
  return 0;
×
1763
}
1764

1765
int pr_ctrls_accept(int fd, uid_t *uid, gid_t *gid, pid_t *pid,
3✔
1766
    unsigned int max_age) {
1767
  socklen_t len = 0;
1768
  struct sockaddr_un sock;
1769
  int cl_fd = -1, res = -1, xerrno;
3✔
1770
  uid_t basic_uid;
1771
  gid_t basic_gid;
1772
  pid_t basic_pid;
1773

1774
  len = sizeof(sock);
3✔
1775

1776
  cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
3✔
1777
  xerrno = errno;
3✔
1778

1779
  while (cl_fd < 0) {
6✔
1780
    if (xerrno == EINTR) {
3✔
1781
      pr_signals_handle();
×
1782

1783
      cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
×
1784
      xerrno = errno;
×
1785
      continue;
×
1786
    }
1787

1788
    pr_trace_msg(trace_channel, 3,
3✔
1789
      "error: unable to accept on local socket: %s", strerror(xerrno));
1790

1791
    errno = xerrno;
3✔
1792
    return -1;
3✔
1793
  }
1794

1795
  /* NULL terminate the name */
1796
  sock.sun_path[sizeof(sock.sun_path)-1] = '\0';
×
1797

1798
  if (strncmp(sock.sun_path, CTRLS_SOCKET_PREFIX,
×
1799
      strlen(CTRLS_SOCKET_PREFIX)) != 0) {
1800
    pr_trace_msg(trace_channel, 6, "refusing unexpected client path '%s'",
×
1801
      sock.sun_path);
1802
    (void) close(cl_fd);
×
1803
    errno = EINVAL;
×
1804
    return -1;
×
1805
  }
1806

1807
  /* Use the Stevens method of determining connection credentials, as doing
1808
   * so performs other basic sanity checks of the provided path.
1809
   *
1810
   * If kernel-enforced means of determining credentials are available, we
1811
   * will use them as well.
1812
   */
1813
  pr_trace_msg(trace_channel, 5,
×
1814
    "checking client credentials using Stevens' method");
1815
  res = ctrls_get_creds_basic(&sock, cl_fd, max_age, &basic_uid, &basic_gid,
×
1816
    &basic_pid);
1817
  if (res < 0) {
×
1818
    xerrno = errno;
×
1819

1820
    (void) close(cl_fd);
×
1821
    errno = xerrno;
×
1822
    return res;
×
1823
  }
1824

1825
#if defined(SO_PEERCRED)
1826
  pr_trace_msg(trace_channel, 5,
×
1827
    "checking client credentials using SO_PEERCRED");
1828
  res = ctrls_get_creds_peercred(cl_fd, uid, gid, pid);
×
1829

1830
#elif !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1831
  pr_trace_msg(trace_channel, 5,
1832
    "checking client credentials using getpeereid(2)");
1833
  res = ctrls_get_creds_peereid(cl_fd, uid, gid);
1834

1835
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1836
      defined(HAVE_GETPEERUCRED)
1837
  pr_trace_msg(trace_channel, 5,
1838
    "checking client credentials using getpeerucred(3)");
1839
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1840

1841
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1842
      !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1843
  pr_trace_msg(trace_channel, 5,
1844
    "checking client credentials using SCM_CREDS");
1845
  res = ctrls_get_creds_local(cl_fd, uid, gid, pid);
1846
#endif
1847

1848
  if (res < 0) {
×
1849
    pr_trace_msg(trace_channel, 5,
×
1850
      "unable to obtain kernel-enforced client credentials: %s",
1851
       strerror(errno));
1852

1853
    /* Fall back to using the Stevens' obtained credentials. */
1854
    if (uid != NULL) {
×
1855
      *uid = basic_uid;
×
1856
    }
1857

1858
    if (gid != NULL) {
×
1859
      *gid = basic_gid;
×
1860
    }
1861

1862
    if (pid != NULL) {
×
1863
      *pid = basic_pid;
×
1864
    }
1865
  }
1866

1867
  /* Done with the path now */
1868
  PRIVS_ROOT
×
1869
  (void) unlink(sock.sun_path);
×
1870
  PRIVS_RELINQUISH
×
1871

1872
  return cl_fd;
×
1873
}
1874

1875
void pr_block_ctrls(void) {
3✔
1876
  ctrls_blocked = TRUE;
46✔
1877
}
3✔
1878

1879
void pr_unblock_ctrls(void) {
3✔
1880
  ctrls_blocked = FALSE;
46✔
1881
}
3✔
1882

1883
int pr_ctrls_check_actions(void) {
4✔
1884
  register ctrls_action_t *act = NULL;
4✔
1885

1886
  for (act = ctrls_action_list; act; act = act->next) {
7✔
1887
    if (act->flags & PR_CTRLS_ACT_SOLITARY) {
4✔
1888
      /* This is a territorial action -- only one instance allowed */
1889
      if (ctrls_lookup_action(NULL, act->action, FALSE)) {
2✔
1890
        pr_log_pri(PR_LOG_NOTICE,
1✔
1891
          "duplicate controls for '%s' action not allowed",
1892
          act->action);
1893
        errno = EEXIST;
1✔
1894
        return -1;
1✔
1895
      }
1896
    }
1897
  }
1898

1899
  return 0;
1900
}
1901

1902
int pr_run_ctrls(module *mod, const char *action) {
12✔
1903
  register pr_ctrls_t *ctrl = NULL;
12✔
1904
  time_t now;
1905

1906
  /* Are ctrls blocked? */
1907
  if (ctrls_blocked == TRUE) {
12✔
1908
    errno = EPERM;
1✔
1909
    return -1;
1✔
1910
  }
1911

1912
  now = time(NULL);
11✔
1913

1914
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
18✔
1915
    int res;
1916

1917
    if (mod != NULL &&
14✔
1918
        ctrl->ctrls_module != NULL &&
14✔
1919
        ctrl->ctrls_module != mod) {
1920
      pr_trace_msg(trace_channel, 19,
1✔
1921
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1922
        mod, ctrl->ctrls_module);
1923
      continue;
1✔
1924
    }
1925

1926
    /* Be watchful of the various client-side flags.  Note: if
1927
     * ctrl->ctrls_cl is ever NULL, it means there's a bug in the code.
1928
     */
1929
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
6✔
1930
      pr_trace_msg(trace_channel, 19,
1✔
1931
        "skipping ctrl due to missing client HAVEREQ flag");
1932
      continue;
1✔
1933
    }
1934

1935
    /* Has this control been disabled? */
1936
    if (ctrl->ctrls_flags & PR_CTRLS_ACT_DISABLED) {
5✔
1937
      pr_trace_msg(trace_channel, 19,
1✔
1938
        "skipping ctrl due to ACT_DISABLED flag");
1939
      continue;
1✔
1940
    }
1941

1942
    /* Is it time to trigger this ctrl? */
1943
    if (!(ctrl->ctrls_flags & PR_CTRLS_FL_REQUESTED)) {
4✔
1944
      pr_trace_msg(trace_channel, 19,
1✔
1945
        "skipping ctrl due to missing CTRLS_REQUESTED flag");
1946
      continue;
1✔
1947
    }
1948

1949
    if (ctrl->ctrls_when > now) {
3✔
1950
      pr_trace_msg(trace_channel, 19,
1✔
1951
        "skipping ctrl because it is still pending: now = %lu, ctrl when = %lu",
1952
        (unsigned long) now, (unsigned long) ctrl->ctrls_when);
1953
      ctrl->ctrls_flags |= PR_CTRLS_FL_PENDING;
1✔
1954
      pr_ctrls_add_response(ctrl, "request pending");
1✔
1955
      continue;
1✔
1956
    }
1957

1958
    if (action == NULL ||
4✔
1959
        strcmp(ctrl->ctrls_action, action) == 0) {
2✔
1960
      pr_trace_msg(trace_channel, 7, "calling '%s' control handler",
1✔
1961
        ctrl->ctrls_action);
1962

1963
    } else {
1964
      continue;
1✔
1965
    }
1966

1967
    pr_unblock_ctrls();
1968
    res = ctrl->ctrls_cb(ctrl,
4✔
1969
      (ctrl->ctrls_cb_args ? ctrl->ctrls_cb_args->nelts : 0),
1✔
1970
      (ctrl->ctrls_cb_args ? (char **) ctrl->ctrls_cb_args->elts : NULL));
1✔
1971
    pr_block_ctrls();
1972

1973
    pr_trace_msg(trace_channel, 19,
1✔
1974
      "ran '%s' ctrl, callback value = %d", ctrl->ctrls_action, res);
1975

1976
    if (res >= PR_CTRLS_STATUS_PENDING) {
1✔
1977
      pr_trace_msg(trace_channel, 1, "'%s' ctrl returned inappropriate "
×
1978
        "value %d, treating as GENERIC_ERROR (%d)", ctrl->ctrls_action, res,
1979
        PR_CTRLS_STATUS_GENERIC_ERROR);
1980
      res = PR_CTRLS_STATUS_GENERIC_ERROR;
×
1981
    }
1982

1983
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_REQUESTED;
1✔
1984
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_PENDING;
1✔
1985
    ctrl->ctrls_flags |= PR_CTRLS_FL_HANDLED;
1✔
1986

1987
    ctrl->ctrls_cb_retval = res;
1✔
1988
  }
1989

1990
  return 0;
1991
}
1992

1993
int pr_ctrls_reset(void) {
2✔
1994
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
2✔
1995

1996
  /* NOTE: need a clean_ctrls() or somesuch that will, after sending any
1997
   * responses, iterate through the list and "free" any ctrls whose
1998
   * ctrls_cb_retval is zero.  This feature is used to handle things like
1999
   * shutdown requests in the future -- the request is only considered
2000
   * "processed" when the callback returns zero.  Any non-zero requests are
2001
   * not cleared, and are considered "pending".  However, this brings up the
2002
   * complication of an additional request for that action being issued by the
2003
   * client before the request is processed.  Simplest solution: remove the
2004
   * old request args, and replace them with the new ones.
2005
   *
2006
   * This requires that the return value of the ctrl callback be explicitly
2007
   * documented.
2008
   *
2009
   * How about: ctrls_cb_retval = 1  pending
2010
   *                              0  processed, OK    (reset)
2011
   *                             -1  processed, error (reset)
2012
   */
2013

2014
  for (ctrl = ctrls_active_list; ctrl; ctrl = next_ctrl) {
4✔
2015
    next_ctrl = ctrl->ctrls_next;
×
2016

2017
    if (ctrl->ctrls_cb_retval < PR_CTRLS_STATUS_PENDING) {
×
2018
      pr_ctrls_free(ctrl);
×
2019
    }
2020
  }
2021

2022
  return 0;
2✔
2023
}
2024

2025
/* From include/mod_ctrls.h */
2026

2027
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
2028
 * otherwise. Note that the default is to deny everyone, unless an ACL has
2029
 * been configured.
2030
 */
2031
int pr_ctrls_check_group_acl(gid_t cl_gid, const ctrls_group_acl_t *group_acl) {
10✔
2032
  int res = FALSE;
10✔
2033

2034
  if (group_acl == NULL) {
10✔
2035
    errno = EINVAL;
1✔
2036
    return -1;
1✔
2037
  }
2038

2039
  /* Note: the special condition of ngids of 1 and gids of NULL signals
2040
   * that all groups are to be treated according to the allow member.
2041
   */
2042
  if (group_acl->gids != NULL) {
9✔
2043
    register unsigned int i = 0;
2044

2045
    for (i = 0; i < group_acl->ngids; i++) {
2✔
2046
      if ((group_acl->gids)[i] == cl_gid) {
2✔
2047
        res = TRUE;
1✔
2048
      }
2049
    }
2050

2051
  } else if (group_acl->ngids == 1) {
7✔
2052
    res = TRUE;
4✔
2053
  }
2054

2055
  if (!group_acl->allow) {
9✔
2056
    res = !res;
2✔
2057
  }
2058

2059
  return res;
2060
}
2061

2062
/* Returns TRUE if the given cl_uid is allowed by the user ACL, FALSE
2063
 * otherwise. Note that the default is to deny everyone, unless an ACL has
2064
 * been configured.
2065
 */
2066
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
10✔
2067
  int res = FALSE;
10✔
2068

2069
  if (user_acl == NULL) {
10✔
2070
    errno = EINVAL;
1✔
2071
    return -1;
1✔
2072
  }
2073

2074
  /* Note: the special condition of nuids of 1 and uids of NULL signals
2075
   * that all users are to be treated according to the allow member.
2076
   */
2077
  if (user_acl->uids != NULL) {
9✔
2078
    register unsigned int i = 0;
2079

2080
    for (i = 0; i < user_acl->nuids; i++) {
2✔
2081
      if ((user_acl->uids)[i] == cl_uid) {
2✔
2082
        res = TRUE;
1✔
2083
      }
2084
    }
2085

2086
  } else if (user_acl->nuids == 1) {
7✔
2087
    res = TRUE;
3✔
2088
  }
2089

2090
  if (!user_acl->allow) {
9✔
2091
    res = !res;
2✔
2092
  }
2093

2094
  return res;
2095
}
2096

2097
/* Returns TRUE for allowed, FALSE for denied. */
2098
int pr_ctrls_check_acl(const pr_ctrls_t *ctrl,
9✔
2099
    const ctrls_acttab_t *acttab, const char *action) {
2100
  register unsigned int i = 0;
9✔
2101
  int unknown_action = TRUE;
9✔
2102

2103
  if (ctrl == NULL ||
17✔
2104
      ctrl->ctrls_cl == NULL ||
8✔
2105
      acttab == NULL ||
14✔
2106
      action == NULL) {
7✔
2107
    errno = EINVAL;
4✔
2108
    return -1;
4✔
2109
  }
2110

2111
  for (i = 0; acttab[i].act_action; i++) {
3✔
2112
    if (strcmp(acttab[i].act_action, action) == 0) {
5✔
2113
      int user_check = FALSE, group_check = FALSE;
4✔
2114

2115
      unknown_action = FALSE;
4✔
2116

2117
      if (acttab[i].act_acl != NULL) {
4✔
2118
        user_check = pr_ctrls_check_user_acl(ctrl->ctrls_cl->cl_uid,
3✔
2119
          &(acttab[i].act_acl->acl_users));
3✔
2120
        pr_trace_msg(trace_channel, 19,
6✔
2121
          "checking user ACL for action '%s' with UID %lu returned %s", action,
2122
          (unsigned long) ctrl->ctrls_cl->cl_uid,
3✔
2123
          user_check ? "true" : "false");
2124

2125
        group_check = pr_ctrls_check_group_acl(ctrl->ctrls_cl->cl_gid,
3✔
2126
          &(acttab[i].act_acl->acl_groups));
3✔
2127
        pr_trace_msg(trace_channel, 19,
6✔
2128
          "checking group ACL for action '%s' with GID %lu returned %s", action,
2129
          (unsigned long) ctrl->ctrls_cl->cl_gid,
3✔
2130
          group_check ? "true" : "false");
2131
      }
2132

2133
      if (user_check != TRUE &&
8✔
2134
          group_check != TRUE) {
4✔
2135
        /* Known action is explicitly denied for user and group by this ACL. */
2136
        return FALSE;
2137
      }
2138
    }
2139
  }
2140

2141
  if (unknown_action == TRUE) {
3✔
2142
    pr_trace_msg(trace_channel, 19,
1✔
2143
      "checked ACL for unknown/unmatched action '%s', returning false", action);
2144

2145
    /* Fail-close by returning false here for unmatched actions. */
2146
    return FALSE;
1✔
2147
  }
2148

2149
  return TRUE;
2150
}
2151

2152
int pr_ctrls_init_acl(ctrls_acl_t *acl) {
7✔
2153
  if (acl == NULL) {
7✔
2154
    errno = EINVAL;
1✔
2155
    return -1;
1✔
2156
  }
2157

2158
  memset(acl, 0, sizeof(ctrls_acl_t));
6✔
2159
  acl->acl_users.allow = acl->acl_groups.allow = TRUE;
6✔
2160

2161
  return 0;
6✔
2162
}
2163

2164
static char *ctrls_argsep(char **arg) {
46✔
2165
  char *ret = NULL, *dst = NULL;
46✔
2166
  char quote_mode = 0;
46✔
2167

2168
  if (arg == NULL ||
92✔
2169
      !*arg ||
92✔
2170
      !**arg) {
46✔
2171
    errno = EINVAL;
12✔
2172
    return NULL;
12✔
2173
  }
2174

2175
  while (**arg &&
34✔
2176
         PR_ISSPACE(**arg)) {
34✔
2177
    (*arg)++;
×
2178
  }
2179

2180
  if (!**arg) {
34✔
2181
    return NULL;
2182
  }
2183

2184
  ret = dst = *arg;
34✔
2185

2186
  if (**arg == '\"') {
34✔
2187
    quote_mode++;
×
2188
    (*arg)++;
×
2189
  }
2190

2191
  while (**arg && **arg != ',' &&
240✔
2192
      (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
103✔
2193

2194
    if (**arg == '\\' && quote_mode) {
103✔
2195
      /* escaped char */
2196
      if (*((*arg) + 1)) {
×
2197
        *dst = *(++(*arg));
×
2198
      }
2199
    }
2200

2201
    *dst++ = **arg;
103✔
2202
    ++(*arg);
103✔
2203
  }
2204

2205
  if (**arg) {
34✔
2206
    (*arg)++;
22✔
2207
  }
2208

2209
  *dst = '\0';
34✔
2210
  return ret;
34✔
2211
}
2212

2213
char **pr_ctrls_parse_acl(pool *acl_pool, const char *acl_text) {
14✔
2214
  char *name = NULL, *acl_text_dup = NULL, **acl_list = NULL;
14✔
2215
  array_header *acl_arr = NULL;
14✔
2216
  pool *tmp_pool = NULL;
14✔
2217

2218
  if (acl_pool == NULL ||
28✔
2219
      acl_text == NULL) {
14✔
2220
    errno = EINVAL;
2✔
2221
    return NULL;
2✔
2222
  }
2223

2224
  tmp_pool = make_sub_pool(acl_pool);
12✔
2225
  acl_text_dup = pstrdup(tmp_pool, acl_text);
12✔
2226

2227
  /* Allocate an array */
2228
  acl_arr = make_array(acl_pool, 0, sizeof(char **));
12✔
2229

2230
  /* Add each name to the array */
2231
  while ((name = ctrls_argsep(&acl_text_dup)) != NULL) {
58✔
2232
    char *text;
2233

2234
    text = pstrdup(acl_pool, name);
34✔
2235

2236
    /* Push the name into the ACL array */
2237
    *((char **) push_array(acl_arr)) = text;
34✔
2238
  }
2239

2240
  /* Terminate the temp array with a NULL, as is proper. */
2241
  *((char **) push_array(acl_arr)) = NULL;
12✔
2242

2243
  acl_list = (char **) acl_arr->elts;
12✔
2244
  destroy_pool(tmp_pool);
12✔
2245

2246
  /* return the array of names */
2247
  return acl_list;
12✔
2248
}
2249

2250
int pr_ctrls_set_group_acl(pool *group_acl_pool, ctrls_group_acl_t *group_acl,
8✔
2251
    const char *allow, char *grouplist) {
2252
  char *group = NULL, **groups = NULL;
8✔
2253
  array_header *gid_list = NULL;
8✔
2254
  gid_t gid = 0;
8✔
2255
  pool *tmp_pool = NULL;
8✔
2256

2257
  if (group_acl_pool == NULL ||
16✔
2258
      group_acl == NULL ||
8✔
2259
      allow == NULL ||
12✔
2260
      grouplist == NULL) {
6✔
2261
    errno = EINVAL;
4✔
2262
    return -1;
4✔
2263
  }
2264

2265
  tmp_pool = make_sub_pool(group_acl_pool);
4✔
2266

2267
  if (strcasecmp(allow, "allow") == 0) {
4✔
2268
    group_acl->allow = TRUE;
3✔
2269

2270
  } else {
2271
    group_acl->allow = FALSE;
1✔
2272
  }
2273

2274
  /* Parse the given expression into an array, then retrieve the GID
2275
   * for each given name.
2276
   */
2277
  groups = pr_ctrls_parse_acl(group_acl_pool, grouplist);
4✔
2278

2279
  /* Allocate an array of gid_t's */
2280
  gid_list = make_array(group_acl_pool, 0, sizeof(gid_t));
4✔
2281

2282
  for (group = *groups; group != NULL; group = *++groups) {
15✔
2283

2284
    /* Handle a group name of "*" differently. */
2285
    if (strcmp(group, "*") == 0) {
12✔
2286
      group_acl->ngids = 1;
1✔
2287
      group_acl->gids = NULL;
1✔
2288
      destroy_pool(tmp_pool);
1✔
2289
      return 0;
1✔
2290
    }
2291

2292
    gid = pr_auth_name2gid(tmp_pool, group);
11✔
2293
    if (gid == (gid_t) -1) {
11✔
2294
      continue;
11✔
2295
    }
2296

2297
    *((gid_t *) push_array(gid_list)) = gid;
×
2298
  }
2299

2300
  group_acl->ngids = gid_list->nelts;
3✔
2301
  group_acl->gids = (gid_t *) gid_list->elts;
3✔
2302

2303
  destroy_pool(tmp_pool);
3✔
2304
  return 0;
3✔
2305
}
2306

2307
int pr_ctrls_set_user_acl(pool *user_acl_pool, ctrls_user_acl_t *user_acl,
10✔
2308
    const char *allow, char *userlist) {
2309
  char *user = NULL, **users = NULL;
10✔
2310
  array_header *uid_list = NULL;
10✔
2311
  uid_t uid = 0;
10✔
2312
  pool *tmp_pool = NULL;
10✔
2313

2314
  /* Sanity checks */
2315
  if (user_acl_pool == NULL ||
20✔
2316
      user_acl == NULL ||
10✔
2317
      allow == NULL ||
16✔
2318
      userlist == NULL) {
8✔
2319
    errno = EINVAL;
4✔
2320
    return -1;
4✔
2321
  }
2322

2323
  tmp_pool = make_sub_pool(user_acl_pool);
6✔
2324

2325
  if (strcasecmp(allow, "allow") == 0) {
6✔
2326
    user_acl->allow = TRUE;
5✔
2327

2328
  } else {
2329
    user_acl->allow = FALSE;
1✔
2330
  }
2331

2332
  /* Parse the given expression into an array, then retrieve the UID
2333
   * for each given name.
2334
   */
2335
  users = pr_ctrls_parse_acl(user_acl_pool, userlist);
6✔
2336

2337
  /* Allocate an array of uid_t's */
2338
  uid_list = make_array(user_acl_pool, 0, sizeof(uid_t));
6✔
2339

2340
  for (user = *users; user != NULL; user = *++users) {
23✔
2341

2342
    /* Handle a user name of "*" differently. */
2343
    if (strcmp(user, "*") == 0) {
18✔
2344
      user_acl->nuids = 1;
1✔
2345
      user_acl->uids = NULL;
1✔
2346
      destroy_pool(tmp_pool);
1✔
2347
      return 0;
1✔
2348
    }
2349

2350
    uid = pr_auth_name2uid(tmp_pool, user);
17✔
2351
    if (uid == (uid_t) -1) {
17✔
2352
      continue;
17✔
2353
    }
2354

2355
    *((uid_t *) push_array(uid_list)) = uid;
×
2356
  }
2357

2358
  user_acl->nuids = uid_list->nelts;
5✔
2359
  user_acl->uids = (uid_t *) uid_list->elts;
5✔
2360

2361
  destroy_pool(tmp_pool);
5✔
2362
  return 0;
5✔
2363
}
2364

2365
int pr_ctrls_set_module_acls2(ctrls_acttab_t *acttab, pool *acl_pool,
23✔
2366
    char **actions, const char *allow, const char *type, char *list,
2367
    const char **bad_action) {
2368
  register unsigned int i = 0;
23✔
2369
  int all_actions = FALSE;
23✔
2370

2371
  if (acttab == NULL ||
46✔
2372
      acl_pool == NULL ||
23✔
2373
      actions == NULL ||
38✔
2374
      type == NULL ||
32✔
2375
      bad_action == NULL) {
2376
    errno = EINVAL;
12✔
2377
    return -1;
12✔
2378
  }
2379

2380
  if (strcasecmp(type, "user") != 0 &&
16✔
2381
      strcasecmp(type, "group") != 0) {
5✔
2382
    errno = EINVAL;
3✔
2383
    return -1;
3✔
2384
  }
2385

2386
  /* First, sanity check the given list of actions against the actions
2387
   * in the given table.
2388
   */
2389
  for (i = 0; actions[i]; i++) {
6✔
2390
    register unsigned int j = 0;
8✔
2391
    int valid_action = FALSE;
8✔
2392

2393
    if (strcasecmp(actions[i], "all") == 0) {
8✔
2394
      continue;
2✔
2395
    }
2396

2397
    for (j = 0; acttab[j].act_action; j++) {
2✔
2398
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
6✔
2399
        valid_action = TRUE;
2400
        break;
2401
      }
2402
    }
2403

2404
    if (valid_action == FALSE) {
6✔
2405
      *bad_action = actions[i];
2✔
2406
      errno = EPERM;
2✔
2407
      return -1;
2✔
2408
    }
2409
  }
2410

2411
  for (i = 0; actions[i]; i++) {
6✔
2412
    register unsigned int j = 0;
6✔
2413

2414
    if (all_actions == FALSE &&
12✔
2415
        strcasecmp(actions[i], "all") == 0) {
6✔
2416
      all_actions = TRUE;
2✔
2417
    }
2418

2419
    for (j = 0; acttab[j].act_action; j++) {
12✔
2420
      int res = 0;
6✔
2421

2422
      if (all_actions == TRUE ||
10✔
2423
          strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2424

2425
        /* Use the type parameter to determine whether the list is of users or
2426
         * of groups.
2427
         */
2428
        if (strcasecmp(type, "user") == 0) {
6✔
2429
          res = pr_ctrls_set_user_acl(acl_pool,
4✔
2430
            &(acttab[j].act_acl->acl_users), allow, list);
4✔
2431

2432
        } else if (strcasecmp(type, "group") == 0) {
2✔
2433
          res = pr_ctrls_set_group_acl(acl_pool,
2✔
2434
            &(acttab[j].act_acl->acl_groups), allow, list);
2✔
2435
        }
2436

2437
        if (res < 0) {
6✔
2438
          *bad_action = actions[i];
×
2439
          return -1;
×
2440
        }
2441
      }
2442
    }
2443
  }
2444

2445
  return 0;
2446
}
2447

2448
char *pr_ctrls_set_module_acls(ctrls_acttab_t *acttab, pool *acl_pool,
11✔
2449
    char **actions, const char *allow, const char *type, char *list) {
2450
  int res;
2451
  char *bad_action = NULL;
11✔
2452

2453
  res = pr_ctrls_set_module_acls2(acttab, acl_pool, actions, allow, type, list,
11✔
2454
    (const char **) &bad_action);
2455
  if (res < 0) {
11✔
2456
    return bad_action;
8✔
2457
  }
2458

2459
  return 0;
2460
}
2461

2462
int pr_ctrls_unregister_module_actions2(ctrls_acttab_t *acttab,
11✔
2463
    char **actions, module *mod, const char **bad_action) {
2464
  register unsigned int i = 0;
11✔
2465

2466
  if (acttab == NULL ||
22✔
2467
      actions == NULL ||
11✔
2468
      mod == NULL ||
14✔
2469
      bad_action == NULL) {
7✔
2470
    errno = EINVAL;
7✔
2471
    return -1;
7✔
2472
  }
2473

2474
  /* First, sanity check the given actions against the actions supported by
2475
   * this module.
2476
   */
2477
  for (i = 0; actions[i]; i++) {
2✔
2478
    register unsigned int j = 0;
2479
    int valid_action = FALSE;
2480

2481
    for (j = 0; acttab[j].act_action; j++) {
2✔
2482
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2483
        valid_action = TRUE;
2484
        break;
2485
      }
2486
    }
2487

2488
    if (valid_action == FALSE) {
4✔
2489
      *bad_action = actions[i];
2✔
2490
      errno = EPERM;
2✔
2491
      return -1;
2✔
2492
    }
2493
  }
2494

2495
  /* Next, iterate through both lists again, looking for actions of the
2496
   * module _not_ in the given list.
2497
   */
2498
  for (i = 0; acttab[i].act_action; i++) {
2✔
2499
    register unsigned int j = 0;
2500
    int have_action = FALSE;
2501

2502
    for (j = 0; actions[j]; j++) {
×
2503
      if (strcmp(acttab[i].act_action, actions[j]) == 0) {
2✔
2504
        have_action = TRUE;
2505
        break;
2506
      }
2507
    }
2508

2509
    if (have_action == TRUE) {
2✔
2510
      pr_trace_msg(trace_channel, 4, "mod_%s.c: removing '%s' control",
2✔
2511
        mod->name, acttab[i].act_action);
2512
      pr_ctrls_unregister(mod, acttab[i].act_action);
2✔
2513
      destroy_pool(acttab[i].act_acl->acl_pool);
2✔
2514
    }
2515
  }
2516

2517
  return 0;
2518
}
2519

2520
char *pr_ctrls_unregister_module_actions(ctrls_acttab_t *acttab,
5✔
2521
    char **actions, module *mod) {
2522
  int res;
2523
  char *bad_action = NULL;
5✔
2524

2525
  res = pr_ctrls_unregister_module_actions2(acttab, actions, mod,
5✔
2526
    (const char **) &bad_action);
2527
  if (res < 0) {
5✔
2528
    return bad_action;
4✔
2529
  }
2530

2531
  return 0;
2532
}
2533

2534
int pr_ctrls_set_logfd(int fd) {
4✔
2535

2536
  /* Close any existing log fd. */
2537
  if (ctrls_logfd >= 0) {
4✔
2538
    (void) close(ctrls_logfd);
1✔
2539
  }
2540

2541
  ctrls_logfd = fd;
4✔
2542
  return 0;
4✔
2543
}
2544

2545
int pr_ctrls_log(const char *module_version, const char *fmt, ...) {
4✔
2546
  va_list msg;
2547
  int res;
2548

2549
  if (ctrls_logfd < 0) {
4✔
2550
    return 0;
2551
  }
2552

2553
  if (fmt == NULL) {
3✔
2554
    return 0;
2555
  }
2556

2557
  va_start(msg, fmt);
1✔
2558
  res = pr_log_vwritefile(ctrls_logfd, module_version, fmt, msg);
1✔
2559
  va_end(msg);
1✔
2560

2561
  return res;
1✔
2562
}
2563

2564
static void ctrls_cleanup_cb(void *user_data) {
39✔
2565
  ctrls_pool = NULL;
39✔
2566
  ctrls_action_list = NULL;
39✔
2567
  ctrls_active_list = NULL;
39✔
2568
  ctrls_free_list = NULL;
39✔
2569

2570
  action_lookup_next = NULL;
39✔
2571
  action_lookup_action = NULL;
39✔
2572
  action_lookup_module = NULL;
39✔
2573
}
39✔
2574

2575
/* Initialize the Controls API. */
2576
int init_ctrls2(const char *socket_path) {
39✔
2577
  struct stat st;
2578
  int fd, xerrno;
2579
  struct sockaddr_un sockun;
2580
  size_t socklen;
2581

2582
  if (ctrls_pool != NULL) {
39✔
2583
    destroy_pool(ctrls_pool);
×
2584
  }
2585

2586
  ctrls_pool = make_sub_pool(permanent_pool);
39✔
2587
  pr_pool_tag(ctrls_pool, "Controls Pool");
39✔
2588
  register_cleanup2(ctrls_pool, NULL, ctrls_cleanup_cb);
39✔
2589

2590
  /* Make sure all of the lists are zero'd out. */
2591
  ctrls_action_list = NULL;
39✔
2592
  ctrls_active_list = NULL;
39✔
2593
  ctrls_free_list = NULL;
39✔
2594

2595
   /* And that the lookup indices are (re)set as well... */
2596
  action_lookup_next = NULL;
39✔
2597
  action_lookup_action = NULL;
39✔
2598
  action_lookup_module = NULL;
39✔
2599

2600
  /* Run-time check to find out whether this platform identifies a
2601
   * Unix domain socket file descriptor via the S_ISFIFO macro, or
2602
   * the S_ISSOCK macro.
2603
   */
2604

2605
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
39✔
2606
  xerrno = errno;
39✔
2607

2608
  if (fd < 0) {
39✔
2609
    pr_log_debug(DEBUG10, "unable to create Unix domain socket: %s",
×
2610
      strerror(xerrno));
2611

2612
    errno = xerrno;
×
2613
    return -1;
×
2614
  }
2615

2616
  memset(&sockun, 0, sizeof(sockun));
39✔
2617
  sockun.sun_family = AF_UNIX;
39✔
2618
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
39✔
2619
  socklen = sizeof(struct sockaddr_un);
39✔
2620

2621
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
39✔
2622
    xerrno = errno;
×
2623

2624
    pr_log_debug(DEBUG10, "unable to bind to Unix domain socket at '%s': %s",
×
2625
      socket_path, strerror(xerrno));
2626
    (void) close(fd);
×
2627
    (void) unlink(socket_path);
×
2628

2629
    errno = xerrno;
×
2630
    return -1;
×
2631
  }
2632

2633
  if (fstat(fd, &st) < 0) {
39✔
2634
    xerrno = errno;
×
2635

2636
    pr_log_debug(DEBUG10, "unable to stat Unix domain socket at '%s': %s",
×
2637
      socket_path, strerror(xerrno));
2638
    (void) close(fd);
×
2639
    (void) unlink(socket_path);
×
2640

2641
    errno = xerrno;
×
2642
    return -1;
×
2643
  }
2644

2645
#if defined(S_ISFIFO)
2646
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISFIFO");
39✔
2647
  if (S_ISFIFO(st.st_mode)) {
39✔
2648
    ctrls_use_isfifo = TRUE;
×
2649
  }
2650
#else
2651
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISFIFO: "
2652
    "macro undefined");
2653
#endif /* S_ISFIFO */
2654

2655
#if defined(S_ISSOCK)
2656
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISSOCK");
39✔
2657
  if (S_ISSOCK(st.st_mode)) {
39✔
2658
    ctrls_use_isfifo = FALSE;
39✔
2659
  }
2660
#else
2661
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISSOCK: "
2662
    "macro undefined");
2663
#endif /* S_ISSOCK */
2664

2665
  pr_trace_msg(trace_channel, 9,
39✔
2666
    "using %s macro for Unix domain socket detection",
2667
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
39✔
2668

2669
  (void) close(fd);
39✔
2670
  (void) unlink(socket_path);
39✔
2671
  return 0;
39✔
2672
}
2673

2674
void init_ctrls(void) {
×
2675
  const char *socket_path = PR_RUN_DIR "/test.sock";
×
2676

2677
  (void) init_ctrls2(socket_path);
×
2678
}
×
2679
#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