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

proftpd / proftpd / 20282785065

16 Dec 2025 09:17PM UTC coverage: 93.026% (+0.4%) from 92.637%
20282785065

push

github

51325 of 55173 relevant lines covered (93.03%)

216.52 hits per line

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

80.91
/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);
7✔
88
static pr_ctrls_t *ctrls_lookup_action(module *, const char *, unsigned char);
7✔
89
static pr_ctrls_t *ctrls_lookup_next_action(module *, unsigned char);
90

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

14✔
94
  pr_block_ctrls();
95

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

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

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

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

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

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

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

27✔
125
  return act;
27✔
126
}
127

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

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

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

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

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

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

9✔
153
  return ctrl;
9✔
154
}
1✔
155

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

162
  /* Make sure that ctrls are blocked while we're doing this */
8✔
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 {
8✔
170
    ctrls_active_list = ctrl->ctrls_next;
2✔
171
  }
172

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

8✔
177
  /* Clear its fields, and add it to the free list */
8✔
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;
183
  ctrl->ctrls_cb = NULL;
8✔
184
  ctrl->ctrls_cb_retval = PR_CTRLS_STATUS_PENDING;
4✔
185
  ctrl->ctrls_flags = 0;
4✔
186

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

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

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

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

15✔
203
int pr_ctrls_register(const module *mod, const char *action,
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 */
3✔
209
  if (action == NULL ||
3✔
210
      desc == NULL ||
211
      cb == NULL) {
212
    errno = EINVAL;
12✔
213
    return -1;
214
  }
215

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

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

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

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

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

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

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

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

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

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

12✔
259
  ctrls_action_list = act;
260

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

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

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

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

275
    if ((action == NULL || strcmp(act->action, action) == 0) &&
276
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
12✔
277
      have_action = TRUE;
×
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 {
12✔
284
        ctrls_action_list = act->next;
4✔
285
      }
286

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

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

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

7✔
300
  pr_unblock_ctrls();
7✔
301

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

11✔
307
  return 0;
11✔
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;
11✔
312

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

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

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

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

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

6✔
344
  return 0;
6✔
345
}
6✔
346

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

3✔
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) {
359
    return 0;
360
  }
361

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

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

6✔
376
  return 0;
6✔
377
}
6✔
378

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

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

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

14✔
399
  return 0;
14✔
400
}
14✔
401

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

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

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

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

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

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

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

5✔
437
  return 0;
5✔
438
}
1✔
439

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

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

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

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

7✔
463
  return 0;
7✔
464
}
7✔
465

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

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

7✔
476
  msglen = strlen(msg);
477

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

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

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

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

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

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

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

505
      pr_signals_unblock();
4✔
506

4✔
507
      errno = xerrno;
508
      return -1;
509
    }
6✔
510

511
    break;
6✔
512
  }
6✔
513

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

6✔
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;
3✔
521
  pool *tmp_pool;
3✔
522
  int res, xerrno;
523
  pr_json_object_t *json;
524
  pr_json_array_t *args;
3✔
525

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

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

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

542
  json = pr_json_object_alloc(tmp_pool);
×
543

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

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

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

×
555
  args = pr_json_array_alloc(tmp_pool);
×
556

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

11✔
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
  }
1✔
632

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

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

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

647
  pr_trace_msg(trace_channel, 27,
×
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) {
653
    destroy_pool(tmp_pool);
654
    pr_signals_unblock();
655

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

749
  nreqargs = pr_json_array_count(args);
×
750
  pr_trace_msg(trace_channel, 19, "received request argc: %u", nreqargs);
×
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.
4✔
754
   */
4✔
755
  ctrl = ctrls_lookup_action(NULL, reqaction, TRUE);
4✔
756
  if (ctrl == NULL) {
757
    (void) pr_trace_msg(trace_channel, 3,
4✔
758
      "unknown action requested '%s', unable to receive request", reqaction);
×
759
    pr_json_array_free(args);
760
    pr_json_object_free(json);
761
    destroy_pool(tmp_pool);
×
762
    pr_signals_unblock();
×
763

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

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

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

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

778
    if (res < 0) {
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));
5✔
782
      pr_json_array_free(args);
783
      pr_json_object_free(json);
6✔
784
      destroy_pool(tmp_pool);
1✔
785
      pr_signals_unblock();
786

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

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

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

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

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

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

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

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

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

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

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

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

×
839
  return 0;
840
}
841

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

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

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

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

865
  json = pr_json_object_alloc(tmp_pool);
×
866

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

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

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

879
  resps = pr_json_array_alloc(tmp_pool);
880

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

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

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

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

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

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

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

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

914
  errno = xerrno;
×
915
  return res;
×
916
}
917

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

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

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

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

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

945
  if (nread < 0) {
6✔
946
    pr_signals_unblock();
1✔
947

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

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

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

5✔
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;
964
    return -1;
1✔
965
  }
1✔
966

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7✔
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.
7✔
1085
     */
1086
    res = pr_json_array_get_string(tmp_pool, resps, i, &resp);
1087
    xerrno = errno;
1088

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

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

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

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

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

1114
  return respargc;
1✔
1115
}
1116

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

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

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

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

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

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

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

7✔
1148
    if (strcmp(act->action, action_lookup_action) == 0 &&
1149
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
7✔
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
    }
7✔
1155
  }
1✔
1156

1✔
1157
  return NULL;
1✔
1158
}
1✔
1159

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

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

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

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

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

1186
        count++;
1187
        break;
1188

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

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

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

1202
        count++;
1203
        break;
1204

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

1213
  return count;
1214
}
1215

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

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

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

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

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

×
1251
  if (have_action == FALSE) {
1252
    errno = ENOENT;
1253
    return -1;
1254
  }
2✔
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) {
2✔
1264
  char buf[1] = {'\0'};
2✔
1265
  int res;
2✔
1266

2✔
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.
2✔
1270
   */
1271

1272
  res = write(fd, buf, 1);
2✔
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

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

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

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

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

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

1309
    pr_signals_unblock();
1✔
1310

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

1315
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
1✔
1316
    int xerrno = errno;
1✔
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));
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;
1✔
1336
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
1✔
1337
    CTRLS_SOCKET_PREFIX, (unsigned int) getpid());
1338
  len = sizeof(cl_sock);
1339

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

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

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

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

1359
  /* Make it a socket */
1360
  if (bind(fd, (struct sockaddr *) &cl_sock, len) < 0) {
×
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) {
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));
1389

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

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

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

1403
    errno = xerrno;
1404
    return -1;
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();
1426
  return fd;
1427
}
1428

1429
int pr_ctrls_issock_unix(mode_t sock_mode) {
1430

1431
  if (ctrls_use_isfifo == TRUE) {
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)) {
1440
      return 0;
1441
    }
1442
#endif /* S_ISSOCK */
1443
  }
1444

1445
  errno = ENOSYS;
1446
  return -1;
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

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

2✔
1683
  /* Are the perms _not_ rwx------? */
1684
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
2✔
1685
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
2✔
1686
    pr_trace_msg(trace_channel, 3,
1687
      "error: unable to accept connection: incorrect mode");
2✔
1688
    errno = EPERM;
2✔
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 ||
2✔
1697
      st.st_mtime < stale_time) {
1698
    pool *tmp_pool;
1699
    char *msg = "error: stale connection";
2✔
1700

2✔
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,
46✔
1750
      "error: unable to accept connection: invalid PID");
42✔
1751
    errno = EINVAL;
3✔
1752
    return -1;
1753
  }
46✔
1754

35✔
1755
  cl_pid = (pid_t) file_pid;
3✔
1756

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

4✔
1762
  return 0;
1763
}
1✔
1764

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

1774
  len = sizeof(sock);
1775

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

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

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

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

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

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

1✔
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;
6✔
1804
    return -1;
1✔
1805
  }
1806

1✔
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
5✔
1811
   * will use them as well.
1✔
1812
   */
1813
  pr_trace_msg(trace_channel, 5,
1✔
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) {
4✔
1818
    xerrno = errno;
1✔
1819

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

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

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

1✔
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)");
1✔
1839
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1840

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

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

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

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

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

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

1872
  return cl_fd;
1873
}
1874

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

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

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

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

1899
  return 0;
1900
}
1901

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

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

1912
  now = time(NULL);
1913

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

1917
    if (mod != NULL &&
4✔
1918
        ctrl->ctrls_module != NULL &&
2✔
1919
        ctrl->ctrls_module != mod) {
1✔
1920
      pr_trace_msg(trace_channel, 19,
1921
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1922
        mod, ctrl->ctrls_module);
1923
      continue;
7✔
1924
    }
4✔
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.
9✔
1928
     */
2✔
1929
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
1930
      pr_trace_msg(trace_channel, 19,
1931
        "skipping ctrl due to missing client HAVEREQ flag");
1932
      continue;
1933
    }
1934

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

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

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

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

9✔
1963
    } else {
2✔
1964
      continue;
1965
    }
1966

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

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

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

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

4✔
1987
    ctrl->ctrls_cb_retval = res;
6✔
1988
  }
3✔
1989

3✔
1990
  return 0;
3✔
1991
}
1992

1993
int pr_ctrls_reset(void) {
4✔
1994
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
4✔
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
7✔
2004
   * old request args, and replace them with the new ones.
7✔
2005
   *
1✔
2006
   * This requires that the return value of the ctrl callback be explicitly
1✔
2007
   * documented.
2008
   *
2009
   * How about: ctrls_cb_retval = 1  pending
6✔
2010
   *                              0  processed, OK    (reset)
6✔
2011
   *                             -1  processed, error (reset)
2012
   */
6✔
2013

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

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

46✔
2022
  return 0;
12✔
2023
}
12✔
2024

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

34✔
2027
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
34✔
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) {
34✔
2032
  int res = FALSE;
2033

2034
  if (group_acl == NULL) {
2035
    errno = EINVAL;
34✔
2036
    return -1;
2037
  }
34✔
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) {
240✔
2043
    register unsigned int i = 0;
103✔
2044

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

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

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

2059
  return res;
2060
}
34✔
2061

34✔
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.
14✔
2065
 */
14✔
2066
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
14✔
2067
  int res = FALSE;
14✔
2068

2069
  if (user_acl == NULL) {
14✔
2070
    errno = EINVAL;
14✔
2071
    return -1;
2✔
2072
  }
2✔
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.
12✔
2076
   */
12✔
2077
  if (user_acl->uids != NULL) {
2078
    register unsigned int i = 0;
2079

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

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

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

2094
  return res;
12✔
2095
}
12✔
2096

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

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

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

2115
      unknown_action = FALSE;
2116

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

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

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

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

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

×
2149
  return TRUE;
2150
}
2151

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

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

10✔
2161
  return 0;
10✔
2162
}
10✔
2163

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

10✔
2168
  if (arg == NULL ||
8✔
2169
      !*arg ||
8✔
2170
      !**arg) {
4✔
2171
    errno = EINVAL;
4✔
2172
    return NULL;
2173
  }
2174

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

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

2184
  ret = dst = *arg;
2185

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

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

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

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

2205
  if (**arg) {
2206
    (*arg)++;
×
2207
  }
2208

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

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

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

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

2227
  /* Allocate an array */
12✔
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) {
11✔
2232
    char *text;
5✔
2233

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

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

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

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

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

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

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

2265
  tmp_pool = make_sub_pool(group_acl_pool);
6✔
2266

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

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

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

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

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

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

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

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

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

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

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

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

7✔
2323
  tmp_pool = make_sub_pool(user_acl_pool);
2324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

34✔
2445
  return 0;
2446
}
2447

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

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

2459
  return 0;
34✔
2460
}
×
2461

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

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

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

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

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

2495
  /* Next, iterate through both lists again, looking for actions of the
2496
   * module _not_ in the given list.
2497
   */
34✔
2498
  for (i = 0; acttab[i].act_action; i++) {
34✔
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) {
2504
        have_action = TRUE;
2505
        break;
2506
      }
2507
    }
34✔
2508

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

34✔
2517
  return 0;
2518
}
34✔
2519

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

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

2531
  return 0;
2532
}
2533

2534
int pr_ctrls_set_logfd(int fd) {
2535

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

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

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

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

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

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

2561
  return res;
2562
}
2563

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

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

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

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

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

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

2595
   /* And that the lookup indices are (re)set as well... */
2596
  action_lookup_next = NULL;
2597
  action_lookup_action = NULL;
2598
  action_lookup_module = NULL;
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);
2606
  xerrno = errno;
2607

2608
  if (fd < 0) {
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));
2617
  sockun.sun_family = AF_UNIX;
2618
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
2619
  socklen = sizeof(struct sockaddr_un);
2620

2621
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
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) {
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");
2647
  if (S_ISFIFO(st.st_mode)) {
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");
2657
  if (S_ISSOCK(st.st_mode)) {
2658
    ctrls_use_isfifo = FALSE;
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,
2666
    "using %s macro for Unix domain socket detection",
2667
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
2668

2669
  (void) close(fd);
2670
  (void) unlink(socket_path);
2671
  return 0;
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 · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc