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

proftpd / proftpd / 28259826111

26 Jun 2026 07:20PM UTC coverage: 93.032% (+0.6%) from 92.469%
28259826111

push

github

51363 of 55210 relevant lines covered (93.03%)

200.05 hits per line

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

80.84
/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
  pr_trace_msg(trace_channel, 22, "sending Controls response: '%.*s'",
7✔
479
    (int) msglen, msg);
480

7✔
481
  /* No interruptions. */
3✔
482
  pr_signals_block();
483

3✔
484
  pr_trace_msg(trace_channel, 27,
3✔
485
    "sending Controls message (%lu bytes) to fd %d", (unsigned long) msglen,
486
    fd);
487

4✔
488
  res = write(fd, &msglen, sizeof(uint32_t));
4✔
489
  xerrno = errno;
4✔
490

491
  if ((size_t) res != sizeof(uint32_t)) {
4✔
492
    pr_signals_unblock();
×
493

×
494
    errno = xerrno;
495
    return -1;
496
  }
×
497

498
  while (TRUE) {
×
499
    res = write(fd, msg, msglen);
×
500
    xerrno = errno;
501

502
    if ((size_t) res != msglen) {
4✔
503
      if (xerrno == EAGAIN) {
504
        pr_signals_handle();
505
        continue;
4✔
506
      }
4✔
507

508
      pr_signals_unblock();
509

6✔
510
      errno = xerrno;
511
      return -1;
6✔
512
    }
6✔
513

6✔
514
    break;
6✔
515
  }
6✔
516

517
  pr_signals_unblock();
6✔
518
  return 0;
6✔
519
}
520

3✔
521
int pr_ctrls_send_request(pool *p, int fd, const char *action,
3✔
522
    unsigned int argc, char **argv) {
523
  register unsigned int i;
524
  pool *tmp_pool;
3✔
525
  int res, xerrno;
3✔
526
  pr_json_object_t *json;
1✔
527
  pr_json_array_t *args;
1✔
528

529
  if (p == NULL ||
530
      fd < 0 ||
2✔
531
      action == NULL) {
2✔
532
    errno = EINVAL;
533
    return -1;
2✔
534
  }
535

2✔
536
  if (argc > 0 &&
2✔
537
      argv == NULL) {
538
    errno = EINVAL;
2✔
539
    return -1;
×
540
  }
×
541

542
  tmp_pool = make_sub_pool(p);
×
543
  pr_pool_tag(tmp_pool, "Controls API send_request pool");
×
544

545
  json = pr_json_object_alloc(tmp_pool);
546

2✔
547
  res = pr_json_object_set_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY, action);
548
  xerrno = errno;
6✔
549

2✔
550
  if (res < 0) {
2✔
551
    pr_json_object_free(json);
552
    destroy_pool(tmp_pool);
2✔
553

×
554
    errno = xerrno;
×
555
    return -1;
×
556
  }
557

×
558
  args = pr_json_array_alloc(tmp_pool);
×
559

560
  for (i = 0; i < argc; i++) {
561
    res = pr_json_array_append_string(tmp_pool, args, argv[i]);
562
    xerrno = errno;
2✔
563

2✔
564
    if (res < 0) {
565
      pr_json_array_free(args);
2✔
566
      pr_json_object_free(json);
×
567
      destroy_pool(tmp_pool);
×
568

×
569
      errno = xerrno;
570
      return -1;
×
571
    }
×
572
  }
573

574
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, args);
2✔
575
  xerrno = errno;
2✔
576

577
  if (res < 0) {
2✔
578
    pr_json_array_free(args);
2✔
579
    pr_json_object_free(json);
2✔
580
    destroy_pool(tmp_pool);
581

2✔
582
    errno = xerrno;
2✔
583
    return -1;
584
  }
585

15✔
586
  res = ctrls_send_msg(tmp_pool, fd, json);
15✔
587
  xerrno = errno;
15✔
588

15✔
589
  pr_json_array_free(args);
15✔
590
  pr_json_object_free(json);
15✔
591
  destroy_pool(tmp_pool);
15✔
592

15✔
593
  errno = xerrno;
15✔
594
  return res;
595
}
15✔
596

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

607
  if (cl == NULL ||
12✔
608
      cl->cl_ctrls == NULL) {
609
    errno = EINVAL;
610
    return -1;
611
  }
12✔
612

12✔
613
  if (cl->cl_fd < 0) {
614
    errno = EBADF;
12✔
615
    return -1;
1✔
616
  }
617

618
  /* No interruptions */
1✔
619
  pr_signals_block();
620

1✔
621
  /* Read in the size of the message, as JSON text. */
1✔
622

623
  nread = read(cl->cl_fd, &msglen, sizeof(uint32_t));
624
  xerrno = errno;
625

11✔
626
  if (nread < 0) {
1✔
627
    pr_signals_unblock();
628

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

10✔
636
  /* Watch for short reads. */
637
  if (nread != sizeof(uint32_t)) {
638
    pr_signals_unblock();
10✔
639

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

647
  tmp_pool = make_sub_pool(cl->cl_pool);
×
648
  pr_pool_tag(tmp_pool, "Controls API recv_request pool");
×
649

650
  pr_trace_msg(trace_channel, 27,
×
651
    "receiving Controls request message (%lu bytes) from fd %d",
×
652
    (unsigned long) msglen, cl->cl_fd);
653

654
  /* Impose min/max request size limit here, for Issue #2036. */
655
  if (msglen == 0) {
10✔
656
    destroy_pool(tmp_pool);
1✔
657
    pr_signals_unblock();
658

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

9✔
666
  if (msglen > CTRLS_MAX_REQ_SIZE) {
9✔
667
    destroy_pool(tmp_pool);
668
    pr_signals_unblock();
9✔
669

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

1✔
677
  /* Allocate one byte for the terminating NUL. */
1✔
678
  msg = pcalloc(tmp_pool, msglen + 1);
679

680
  nread = read(cl->cl_fd, msg, msglen);
8✔
681
  xerrno = errno;
682

8✔
683
  if (nread < 0) {
684
    destroy_pool(tmp_pool);
8✔
685
    pr_signals_unblock();
1✔
686

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

1✔
694
  /* Watch for short reads. */
695
  if ((unsigned int) nread != msglen) {
696
    destroy_pool(tmp_pool);
7✔
697
    pr_signals_unblock();
7✔
698

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

1✔
706
  pr_trace_msg(trace_channel, 22, "received Controls request: '%.*s'",
707
    (int) msglen, msg);
1✔
708

1✔
709
  json = pr_json_object_from_text(tmp_pool, msg);
710
  xerrno = errno;
711

6✔
712
  if (json == NULL) {
6✔
713
    destroy_pool(tmp_pool);
714
    pr_signals_unblock();
715

716
    (void) pr_trace_msg(trace_channel, 3,
717
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
12✔
718
      "receive request: %s", (int) msglen, msg, (unsigned long) msglen,
6✔
719
      strerror(xerrno));
1✔
720
    errno = EINVAL;
721
    return -1;
1✔
722
  }
1✔
723

1✔
724
  res = pr_json_object_get_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY,
1✔
725
    &reqaction);
726
  xerrno = errno;
727

1✔
728
  if (res < 0) {
1✔
729
    pr_json_object_free(json);
730
    destroy_pool(tmp_pool);
731
    pr_signals_unblock();
5✔
732

733
    (void) pr_trace_msg(trace_channel, 3,
14✔
734
      "unable to read message action (%s), unable to receive request",
4✔
735
      strerror(xerrno));
4✔
736
    errno = EINVAL;
737
    return -1;
4✔
738
  }
4✔
739

740
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, &args);
4✔
741
  xerrno = errno;
×
742

743
  if (res < 0) {
744
    pr_json_object_free(json);
×
745
    destroy_pool(tmp_pool);
×
746
    pr_signals_unblock();
×
747

×
748
    (void) pr_trace_msg(trace_channel, 3,
749
      "unable to read message arguments (%s), unable to receive request",
×
750
      strerror(xerrno));
×
751
    errno = EINVAL;
752
    return -1;
753
  }
4✔
754

4✔
755
  nreqargs = pr_json_array_count(args);
4✔
756
  pr_trace_msg(trace_channel, 19, "received request argc: %u", nreqargs);
757

4✔
758
  /* Find a matching action object, and use it to populate a ctrl object,
×
759
   * preparing the ctrl object for dispatching to the action handlers.
760
   */
761
  ctrl = ctrls_lookup_action(NULL, reqaction, TRUE);
×
762
  if (ctrl == NULL) {
×
763
    (void) pr_trace_msg(trace_channel, 3,
×
764
      "unknown action requested '%s', unable to receive request", reqaction);
×
765
    pr_json_array_free(args);
766
    pr_json_object_free(json);
×
767
    destroy_pool(tmp_pool);
×
768
    pr_signals_unblock();
769

770
    /* XXX This is where we could also add "did you mean" functionality. */
771
    errno = EINVAL;
772
    return -1;
5✔
773
  }
774

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

5✔
777
  for (i = 0; i < nreqargs; i++) {
778
    size_t reqarglen = 0;
779
    char *reqarg = NULL;
780

781
    res = pr_json_array_get_string(tmp_pool, args, i, &reqarg);
5✔
782
    xerrno = errno;
783

6✔
784
    if (res < 0) {
1✔
785
      (void) pr_trace_msg(trace_channel, 3,
786
        "unable to read message argument #%u (%s), unable to receive request",
787
        i+1, strerror(xerrno));
1✔
788
      pr_json_array_free(args);
789
      pr_json_object_free(json);
790
      destroy_pool(tmp_pool);
1✔
791
      pr_signals_unblock();
1✔
792

793
      errno = EINVAL;
1✔
794
      return -1;
795
    }
796

5✔
797
    reqarglen = strlen(reqarg);
5✔
798
    res = pr_ctrls_add_arg(ctrl, reqarg, reqarglen);
5✔
799
    xerrno = errno;
5✔
800

801
    if (res < 0) {
5✔
802
      pr_trace_msg(trace_channel, 3,
803
        "error adding message argument #%u (%s): %s", i+1, reqarg,
804
        strerror(xerrno));
8✔
805
      pr_json_array_free(args);
806
      pr_json_object_free(json);
8✔
807
      destroy_pool(tmp_pool);
8✔
808
      pr_signals_unblock();
8✔
809

8✔
810
      errno = xerrno;
8✔
811
      return -1;
812
    }
8✔
813
  }
8✔
814

2✔
815
  /* Add this ctrls object to the client object. */
2✔
816
  *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = ctrl;
817

818
  /* Set the flag that this control is ready to go */
6✔
819
  ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
6✔
820
  ctrl->ctrls_cl = cl;
1✔
821

1✔
822
  /* Copy the populated ctrl object args to ctrl objects for all other
823
   * matching action objects.
824
   */
5✔
825
  next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
5✔
826

827
  while (next_ctrl != NULL) {
5✔
828
    (void) pr_ctrls_copy_args(ctrl, next_ctrl);
829

5✔
830
    /* Add this ctrl object to the client object. */
831
    *((pr_ctrls_t **) push_array(cl->cl_ctrls)) = next_ctrl;
5✔
832

833
    /* Set the flag that this control is ready to go. */
5✔
834
    next_ctrl->ctrls_flags |= PR_CTRLS_FL_REQUESTED;
×
835
    next_ctrl->ctrls_cl = cl;
×
836

837
    next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
×
838
  }
×
839

840
  pr_json_array_free(args);
841
  pr_json_object_free(json);
5✔
842
  destroy_pool(tmp_pool);
843
  pr_signals_unblock();
14✔
844

4✔
845
  return 0;
4✔
846
}
847

4✔
848
int pr_ctrls_send_response(pool *p, int fd, int status, unsigned int argc,
×
849
    char **argv) {
×
850
  register unsigned int i;
×
851
  pool *tmp_pool;
852
  int res, xerrno;
×
853
  pr_json_object_t *json;
×
854
  pr_json_array_t *resps;
855

856
  if (p == NULL ||
857
      fd < 0) {
5✔
858
    errno = EINVAL;
5✔
859
    return -1;
860
  }
5✔
861

×
862
  if (argc > 0 &&
×
863
      argv == NULL) {
×
864
    errno = EINVAL;
865
    return -1;
×
866
  }
×
867

868
  tmp_pool = make_sub_pool(p);
869
  pr_pool_tag(tmp_pool, "Controls API send_response pool");
5✔
870

5✔
871
  json = pr_json_object_alloc(tmp_pool);
872

5✔
873
  res = pr_json_object_set_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY,
5✔
874
    (double) status);
5✔
875
  xerrno = errno;
876

5✔
877
  if (res < 0) {
5✔
878
    pr_json_object_free(json);
879
    destroy_pool(tmp_pool);
880

10✔
881
    errno = xerrno;
10✔
882
    return -1;
10✔
883
  }
10✔
884

10✔
885
  resps = pr_json_array_alloc(tmp_pool);
10✔
886

10✔
887
  for (i = 0; i < argc; i++) {
10✔
888
    res = pr_json_array_append_string(tmp_pool, resps, argv[i]);
10✔
889
    xerrno = errno;
10✔
890

891
    if (res < 0) {
892
      pr_json_array_free(resps);
10✔
893
      pr_json_object_free(json);
10✔
894
      destroy_pool(tmp_pool);
895

3✔
896
      errno = xerrno;
3✔
897
      return -1;
898
    }
899
  }
900

7✔
901
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, resps);
902
  xerrno = errno;
903

904
  if (res < 0) {
7✔
905
    pr_json_array_free(resps);
7✔
906
    pr_json_object_free(json);
907
    destroy_pool(tmp_pool);
7✔
908

1✔
909
    errno = xerrno;
910
    return -1;
1✔
911
  }
×
912

913
  res = ctrls_send_msg(tmp_pool, fd, json);
914
  xerrno = errno;
×
915

×
916
  pr_json_array_free(resps);
917
  pr_json_object_free(json);
918
  destroy_pool(tmp_pool);
1✔
919

920
  errno = xerrno;
921
  return res;
1✔
922
}
1✔
923

924
int pr_ctrls_recv_response(pool *p, int fd, int *status, char ***respargv) {
925
  register int i = 0;
6✔
926
  pool *tmp_pool;
6✔
927
  int nread, res, respargc = 0, xerrno;
928
  uint32_t msglen = 0;
929
  char *msg = NULL;
6✔
930
  pr_json_object_t *json = NULL;
6✔
931
  pr_json_array_t *resps = NULL;
6✔
932
  double dv;
933
  array_header *resparr = NULL;
6✔
934

×
935
  /* Sanity checks */
936
  if (p == NULL ||
937
      fd < 0 ||
×
938
      status == NULL) {
×
939
    errno = EINVAL;
940
    return -1;
×
941
  }
×
942

943
  /* No interruptions. */
944
  pr_signals_block();
945

6✔
946
  /* Read in the size of the message, as JSON text. */
1✔
947

948
  nread = read(fd, &msglen, sizeof(uint32_t));
949
  xerrno = errno;
1✔
950

1✔
951
  if (nread < 0) {
952
    pr_signals_unblock();
1✔
953

1✔
954
    pr_trace_msg(trace_channel, 3,
955
      "error reading %lu bytes of response message size: %s",
956
      sizeof(msglen), strerror(xerrno));
5✔
957

5✔
958
    errno = xerrno;
959
    return -1;
5✔
960
  }
1✔
961

962
  /* Watch for short reads. */
963
  if (nread != sizeof(uint32_t)) {
964
    pr_signals_unblock();
1✔
965

1✔
966
    (void) pr_trace_msg(trace_channel, 3,
967
      "short read (%d of %u bytes) of response message, unable to receive "
1✔
968
      "response", nread, (unsigned int) sizeof(uint32_t));
1✔
969
    errno = EPERM;
970
    return -1;
971
  }
4✔
972

4✔
973
  tmp_pool = make_sub_pool(p);
974
  pr_pool_tag(tmp_pool, "Controls API recv_response pool");
4✔
975

1✔
976
  pr_trace_msg(trace_channel, 27,
977
    "receiving Controls response message (%lu bytes) from fd %d",
978
    (unsigned long) msglen, fd);
1✔
979

1✔
980
  /* Impose min/max response size limit here, for Issue #2036. */
1✔
981
  if (msglen == 0) {
982
    destroy_pool(tmp_pool);
1✔
983
    pr_signals_unblock();
1✔
984

985
    (void) pr_trace_msg(trace_channel, 3,
986
      "message size (%lu bytes) less than min (1 byte), unable to receive "
3✔
987
      "response", (unsigned long) msglen);
3✔
988
    errno = EINVAL;
989
    return -1;
3✔
990
  }
3✔
991

992
  if (msglen > CTRLS_MAX_RESP_SIZE) {
3✔
993
    destroy_pool(tmp_pool);
1✔
994
    pr_signals_unblock();
995

996
    (void) pr_trace_msg(trace_channel, 3,
1✔
997
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
1✔
998
      "response", (unsigned long) msglen, (unsigned long) CTRLS_MAX_RESP_SIZE);
1✔
999
    errno = E2BIG;
1000
    return -1;
1✔
1001
  }
1✔
1002

1003
  /* Allocate one byte for the terminating NUL. */
1004
  msg = pcalloc(tmp_pool, msglen + 1);
2✔
1005
  nread = read(fd, msg, msglen);
2✔
1006
  xerrno = errno;
1007

2✔
1008
  if (nread < 0) {
1009
    destroy_pool(tmp_pool);
1010
    pr_signals_unblock();
5✔
1011

2✔
1012
    pr_trace_msg(trace_channel, 3,
1013
      "error reading %lu bytes of response message: %s",
1014
      (unsigned long) msglen, strerror(xerrno));
1015
    errno = xerrno;
1016
    return -1;
1017
  }
2✔
1018

2✔
1019
  /* Watch for short reads. */
1020
  if ((unsigned int) nread != msglen) {
2✔
1021
    destroy_pool(tmp_pool);
1✔
1022
    pr_signals_unblock();
1023

1024
    (void) pr_trace_msg(trace_channel, 3,
1✔
1025
      "short read (%d of %u bytes) of message text, unable to receive response",
1✔
1026
      nread, (unsigned int) msglen);
1✔
1027
    errno = EPERM;
1✔
1028
    return -1;
1029
  }
1✔
1030

1✔
1031
  json = pr_json_object_from_text(tmp_pool, msg);
1032
  xerrno = errno;
1033

1✔
1034
  if (json == NULL) {
1035
    destroy_pool(tmp_pool);
1036
    pr_signals_unblock();
1✔
1037

×
1038
    (void) pr_trace_msg(trace_channel, 3,
1039
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
1040
      "receive response: %s", (int) msglen, msg, (unsigned long) msglen,
1✔
1041
      strerror(xerrno));
1✔
1042
    errno = EINVAL;
1✔
1043
    return -1;
1✔
1044
  }
1045

1✔
1046
  res = pr_json_object_get_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY, &dv);
1047
  xerrno = errno;
1048

7✔
1049
  if (res < 0) {
1050
    pr_json_object_free(json);
1051
    destroy_pool(tmp_pool);
1052
    pr_signals_unblock();
7✔
1053

7✔
1054
    (void) pr_trace_msg(trace_channel, 3,
7✔
1055
      "unable to read response status (%s), unable to receive response",
1056
      strerror(xerrno));
1057
    errno = EINVAL;
6✔
1058
    return -1;
1059
  }
1060

13✔
1061
  *status = (int) dv;
1062
  pr_trace_msg(trace_channel, 19, "received response status: %d", *status);
13✔
1063

1064
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, &resps);
1065
  xerrno = errno;
13✔
1066

×
1067
  if (res < 0) {
×
1068
    (void) pr_trace_msg(trace_channel, 3,
1069
      "unable to read message responses (%s), unable to receive response",
1070
      strerror(xerrno));
13✔
1071
    pr_json_object_free(json);
×
1072
    destroy_pool(tmp_pool);
1073
    pr_signals_unblock();
1074

13✔
1075
    errno = EINVAL;
7✔
1076
    return -1;
×
1077
  }
1078

1079
  respargc = pr_json_array_count(resps);
7✔
1080
  pr_trace_msg(trace_channel, 19, "received response argc: %u", respargc);
7✔
1081

7✔
1082
  resparr = make_array(p, 0, sizeof(char *));
1083

1084
  /* Read each response, and add it to the array */
7✔
1085
  for (i = 0; i < respargc; i++) {
1086
    char *resp = NULL;
1087

1088
    /* TODO: Handle other response types, such as arrays or objects, for
1089
     * more complex responses.  Think of an action that dumps the memory
1090
     * pools, for example.
1091
     */
7✔
1092
    res = pr_json_array_get_string(tmp_pool, resps, i, &resp);
7✔
1093
    xerrno = errno;
7✔
1094

1095
    if (res < 0) {
7✔
1096
      (void) pr_trace_msg(trace_channel, 3,
1✔
1097
        "unable to read message response #%u (%s), unable to receive response",
1✔
1098
        i+1, strerror(xerrno));
1099
      pr_json_array_free(resps);
1100
      pr_json_object_free(json);
1101
      destroy_pool(tmp_pool);
6✔
1102
      pr_signals_unblock();
1✔
1103

1✔
1104
      errno = EINVAL;
1105
      return -1;
1106
    }
13✔
1107

8✔
1108
    *((char **) push_array(resparr)) = pstrdup(p, resp);
2✔
1109
  }
2✔
1110

1✔
1111
  if (respargv != NULL) {
1✔
1112
    *respargv = ((char **) resparr->elts);
1113
  }
1114

1✔
1115
  pr_json_array_free(resps);
1116
  pr_json_object_free(json);
1117
  destroy_pool(tmp_pool);
2✔
1118
  pr_signals_unblock();
2✔
1119

1120
  return respargc;
2✔
1121
}
2✔
1122

×
1123
static pr_ctrls_t *ctrls_lookup_action(module *mod, const char *action,
1124
    unsigned char skip_disabled) {
1125

2✔
1126
  /* (Re)set the current indices */
1✔
1127
  action_lookup_next = ctrls_action_list;
1✔
1128
  action_lookup_action = action;
1129
  action_lookup_module = mod;
1130

1✔
1131
  /* Wrapper around ctrls_lookup_next_action() */
1132
  return ctrls_lookup_next_action(mod, skip_disabled);
1133
}
2✔
1134

2✔
1135
static pr_ctrls_t *ctrls_lookup_next_action(module *mod,
1136
    unsigned char skip_disabled) {
2✔
1137
  register ctrls_action_t *act = NULL;
2✔
1138

1139
  /* Sanity check */
2✔
1140
  if (action_lookup_action == NULL) {
2✔
1141
    errno = EINVAL;
1142
    return NULL;
1143
  }
1144

1145
  if (mod != action_lookup_module) {
1146
    return ctrls_lookup_action(mod, action_lookup_action, skip_disabled);
1147
  }
7✔
1148

1149
  for (act = action_lookup_next; act; act = act->next) {
7✔
1150
    if (skip_disabled && (act->flags & PR_CTRLS_ACT_DISABLED)) {
7✔
1151
      continue;
1152
    }
1153

7✔
1154
    if (strcmp(act->action, action_lookup_action) == 0 &&
7✔
1155
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
1✔
1156
      action_lookup_next = act->next;
1✔
1157

1✔
1158
      /* Use this action object to prepare a ctrl object. */
1✔
1159
      return ctrls_prepare(act);
1160
    }
1161
  }
1162

6✔
1163
  return NULL;
1✔
1164
}
1✔
1165

1166
int pr_get_registered_actions(pr_ctrls_t *ctrl, int flags) {
1167
  register ctrls_action_t *act = NULL;
10✔
1168
  int count = 0;
5✔
1169

×
1170
  if (ctrl == NULL) {
×
1171
    errno = EINVAL;
1172
    return -1;
1173
  }
5✔
1174

4✔
1175
  /* Are ctrls blocked? */
3✔
1176
  if (ctrls_blocked == TRUE) {
5✔
1177
    errno = EPERM;
5✔
1178
    return -1;
5✔
1179
  }
1180

1181
  for (act = ctrls_action_list; act; act = act->next) {
1182
    switch (flags) {
5✔
1183
      case CTRLS_GET_ACTION_ALL:
1✔
1184
        if (act->module != NULL) {
1✔
1185
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1186
            act->module->name);
1187

1188
        } else {
1189
           pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1190
        }
1191

1192
        count++;
1193
        break;
1194

1195
      case CTRLS_GET_ACTION_ENABLED:
1196
        if (act->flags & PR_CTRLS_ACT_DISABLED) {
1197
          continue;
1198
        }
1199

1200
        if (act->module != NULL) {
1201
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1202
            act->module->name);
1203

1204
        } else {
1205
          pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1206
        }
1207

1208
        count++;
1209
        break;
1210

1211
      case CTRLS_GET_DESC:
1212
        pr_ctrls_add_response(ctrl, "%s: %s", act->action,
1213
          act->desc);
1214
        count++;
1215
        break;
1216
    }
1217
  }
1218

1219
  return count;
1220
}
3✔
1221

3✔
1222
int pr_set_registered_actions(module *mod, const char *action,
3✔
1223
    unsigned char skip_disabled, unsigned int flags) {
1224
  register ctrls_action_t *act = NULL;
3✔
1225
  unsigned char have_action = FALSE;
1✔
1226

1✔
1227
  /* Is flags a valid combination of settable flags? */
1228
  if (flags > 0 &&
1229
      flags != PR_CTRLS_ACT_SOLITARY &&
1230
      flags != PR_CTRLS_ACT_DISABLED &&
2✔
1231
      flags != (PR_CTRLS_ACT_SOLITARY|PR_CTRLS_ACT_DISABLED)) {
1232
    errno = EINVAL;
1233
    return -1;
2✔
1234
  }
2✔
1235

×
1236
  /* Are ctrls blocked? */
1237
  if (ctrls_blocked == TRUE) {
×
1238
    errno = EPERM;
1239
    return -1;
×
1240
  }
×
1241

1242
  for (act = ctrls_action_list; act; act = act->next) {
1243
    if (skip_disabled == TRUE &&
2✔
1244
        (act->flags & PR_CTRLS_ACT_DISABLED)) {
×
1245
      continue;
1246
    }
×
1247

×
1248
    if ((action == NULL ||
1249
         strcmp(action, "all") == 0 ||
×
1250
         strcmp(act->action, action) == 0) &&
×
1251
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
1252
      have_action = TRUE;
1253
      act->flags = flags;
1254
    }
2✔
1255
  }
1256

1257
  if (have_action == FALSE) {
1258
    errno = ENOENT;
1259
    return -1;
1260
  }
1261

1262
  return 0;
1263
}
2✔
1264

2✔
1265
#if !defined(SO_PEERCRED) && \
2✔
1266
    !defined(HAVE_GETPEEREID) && \
2✔
1267
    !defined(HAVE_GETPEERUCRED) && \
1268
    defined(LOCAL_CREDS)
1269
static int ctrls_connect_local_creds(int fd) {
2✔
1270
  char buf[1] = {'\0'};
1271
  int res;
1272

2✔
1273
  /* The backend doesn't care what we send here, but it wants
×
1274
   * exactly one character to force recvmsg() to block and wait
1275
   * for us.
×
1276
   */
1277

×
1278
  res = write(fd, buf, 1);
×
1279
  while (res < 0) {
×
1280
    if (errno == EINTR) {
1281
      pr_signals_handle();
×
1282

×
1283
      res = write(fd, buf, 1);
1284
      continue;
1285
    }
1286

2✔
1287
    pr_trace_msg(trace_channel, 5,
×
1288
      "error writing credentials byte for LOCAL_CREDS to fd %d: %s", fd,
1289
      strerror(errno));
×
1290
    return -1;
1291
  }
×
1292

×
1293
  return res;
×
1294
}
1295
#endif /* !SCM_CREDS */
×
1296

×
1297
int pr_ctrls_connect(const char *socket_file) {
1298
  int fd = -1, len = 0;
1299
  struct sockaddr_un cl_sock, ctrl_sock;
1300
  struct stat st;
2✔
1301

1302
  if (socket_file == NULL) {
2✔
1303
    errno = EINVAL;
2✔
1304
    return -1;
2✔
1305
  }
1306

2✔
1307
  /* No interruptions */
1✔
1308
  pr_signals_block();
1309

1✔
1310
  /* Create a Unix domain socket */
1311
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
1✔
1312
  if (fd < 0) {
1✔
1313
    int xerrno = errno;
1✔
1314

1315
    pr_signals_unblock();
1✔
1316

1✔
1317
    errno = xerrno;
1318
    return -1;
1319
  }
1320

1321
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
1322
    int xerrno = errno;
1323

1324
    (void) close(fd);
1325
    pr_signals_unblock();
1326

1327
    errno = xerrno;
1328
    return -1;
1329
  }
1330

1331
  /* Fill in the socket address */
1332
  memset(&cl_sock, 0, sizeof(cl_sock));
1333

1334
  /* This first part is clever.  First, this process creates a socket in
1335
   * the file system.  It _then_ connect()s to the server.  Upon accept()ing
1✔
1336
   * the connection, the server examines the created socket to see that it
1✔
1337
   * is indeed a socket, with the proper mode and time.  Clever, but not
1338
   * ideal.
1339
   */
3✔
1340

1341
  cl_sock.sun_family = AF_UNIX;
3✔
1342
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
1343
    CTRLS_SOCKET_PREFIX, (unsigned int) getpid());
×
1344
  len = sizeof(cl_sock);
1345

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

1355
      (void) close(fd);
2✔
1356
      pr_signals_unblock();
2✔
1357

1358
      errno = EEXIST;
1359
      return -1;
1360
    }
×
1361
  }
1362

1363
  (void) unlink(cl_sock.sun_path);
1364

1365
  /* Make it a socket */
×
1366
  if (bind(fd, (struct sockaddr *) &cl_sock, len) < 0) {
1367
    int xerrno = errno;
×
1368

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

1375
    errno = xerrno;
1376
    return -1;
1377
  }
×
1378

×
1379
  /* Set the proper mode */
1380
  if (chmod(cl_sock.sun_path, PR_CTRLS_CL_MODE) < 0) {
1381
    int xerrno = errno;
×
1382

×
1383
    pr_trace_msg(trace_channel, 19, "error setting local socket mode: %s",
1384
      strerror(xerrno));
1385
    (void) unlink(cl_sock.sun_path);
×
1386
    (void) close(fd);
×
1387
    pr_signals_unblock();
1388

1389
    errno = xerrno;
×
1390
    return -1;
×
1391
  }
1392

1393
  /* Now connect to the real server */
1394
  memset(&ctrl_sock, 0, sizeof(ctrl_sock));
1395

1396
  ctrl_sock.sun_family = AF_UNIX;
1397
  sstrncpy(ctrl_sock.sun_path, socket_file, sizeof(ctrl_sock.sun_path));
1398
  len = sizeof(ctrl_sock);
1399

1400
  if (connect(fd, (struct sockaddr *) &ctrl_sock, len) < 0) {
1401
    int xerrno = errno;
1402

1403
    pr_trace_msg(trace_channel, 19, "error connecting to local socket '%s': %s",
1404
      ctrl_sock.sun_path, strerror(xerrno));
1405
    (void) unlink(cl_sock.sun_path);
1406
    (void) close(fd);
1407
    pr_signals_unblock();
1408

1409
    errno = xerrno;
1410
    return -1;
1411
  }
1412

1413
#if !defined(SO_PEERCRED) && \
1414
    !defined(HAVE_GETPEEREID) && \
1415
    !defined(HAVE_GETPEERUCRED) && \
1416
    defined(LOCAL_CREDS)
1417
  if (ctrls_connect_local_creds(fd) < 0) {
1418
    int xerrno = errno;
1419

1420
    pr_trace_msg(trace_channel, 19, "error sending creds to local socket: %s",
1421
      strerror(xerrno));
1422
    (void) unlink(cl_sock.sun_path);
1423
    (void) close(fd);
1424
    pr_signals_unblock();
1425

1426
    errno = xerrno;
1427
    return -1;
1428
  }
1429
#endif /* LOCAL_CREDS */
1430

1431
  pr_signals_unblock();
1432
  return fd;
1433
}
1434

1435
int pr_ctrls_issock_unix(mode_t sock_mode) {
1436

1437
  if (ctrls_use_isfifo == TRUE) {
1438
#if defined(S_ISFIFO)
1439
    if (S_ISFIFO(sock_mode)) {
1440
      return 0;
1441
    }
1442
#endif /* S_ISFIFO */
1443
  } else {
1444
#if defined(S_ISSOCK)
1445
    if (S_ISSOCK(sock_mode)) {
1446
      return 0;
1447
    }
1448
#endif /* S_ISSOCK */
1449
  }
1450

1451
  errno = ENOSYS;
1452
  return -1;
1453
}
1454

1455
#if defined(SO_PEERCRED)
1456
static int ctrls_get_creds_peercred(int fd, uid_t *uid, gid_t *gid,
1457
    pid_t *pid) {
1458
# if defined(HAVE_STRUCT_SOCKPEERCRED)
1459
  struct sockpeercred cred;
1460
# else
1461
  struct ucred cred;
1462
# endif /* HAVE_STRUCT_SOCKPEERCRED */
1463
  socklen_t cred_len;
1464

1465
  cred_len = sizeof(cred);
1466
  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) {
1467
    int xerrno = errno;
1468

1469
    pr_trace_msg(trace_channel, 2,
1470
      "error obtaining peer credentials using SO_PEERCRED: %s",
1471
      strerror(xerrno));
1472

1473
    errno = EPERM;
1474
    return -1;
1475
  }
1476

1477
  if (uid != NULL) {
1478
    *uid = cred.uid;
1479
  }
1480

1481
  if (gid != NULL) {
1482
    *gid = cred.gid;
1483
  }
1484

1485
  if (pid != NULL) {
1486
    *pid = cred.pid;
1487
  }
1488

1489
  return 0;
1490
}
1491
#endif /* SO_PEERCRED */
1492

1493
#if !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1494
static int ctrls_get_creds_peereid(int fd, uid_t *uid, gid_t *gid) {
1495
  if (getpeereid(fd, uid, gid) < 0) {
1496
    int xerrno = errno;
1497

1498
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1499
      "getpeereid(2) on fd %d: %s", fd, strerror(xerrno));
1500

1501
    errno = xerrno;
1502
    return -1;
1503
  }
1504

1505
  return 0;
1506
}
1507
#endif /* !HAVE_GETPEEREID */
1508

1509
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1510
    defined(HAVE_GETPEERUCRED)
1511
static int ctrls_get_creds_peerucred(int fd, uid_t *uid, gid_t *gid) {
1512
  ucred_t *cred = NULL;
1513

1514
  if (getpeerucred(fd, &cred) < 0) {
1515
    int xerrno = errno;
1516

1517
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1518
      "getpeerucred(3) on fd %d: %s", fd, strerror(xerrno));
1519

1520
    errno = xerrno;
1521
    return -1;
1522
  }
1523

1524
  if (uid != NULL) {
1525
    *uid = ucred_getruid(cred);
1526
  }
1527

1528
  if (gid != NULL) {
1529
    *gid = ucred_getrgid(cred);
1530
  }
1531

1532
  ucred_free(cred);
1533
  return 0;
1534
}
1535
#endif /* !HAVE_GETPEERUCRED */
1536

1537
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1538
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1539
static int ctrls_get_creds_local(int fd, uid_t *uid, gid_t *gid,
1540
    pid_t *pid) {
1541
  int res;
1542
  char buf[1];
1543
  struct iovec iov;
1544
  struct msghdr msg;
1545

1546
# if defined(SOCKCREDSIZE)
1547
#  define MINCREDSIZE                (sizeof(struct cmsghdr) + SOCKCREDSIZE(0))
1548
# else
1549
#  if defined(HAVE_STRUCT_CMSGCRED)
1550
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
1551
#  elif defined(HAVE_STRUCT_SOCKCRED)
1552
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct sockcred))
1553
#  endif
1554
# endif /* !SOCKCREDSIZE */
1555

1556
  char control[MINCREDSIZE];
1557

1558
  iov.iov_base = buf;
1559
  iov.iov_len = 1;
1560

×
1561
  memset(&msg, 0, sizeof(msg));
1562
  msg.msg_iov = &iov;
×
1563
  msg.msg_iovlen = 1;
×
1564
  msg.msg_control = control;
×
1565
  msg.msg_controllen = sizeof(control);
×
1566
  msg.msg_flags = 0;
1567

1568
  res = recvmsg(fd, &msg, 0);
×
1569
  while (res < 0) {
×
1570
    int xerrno = errno;
×
1571

1572
    if (xerrno == EINTR) {
×
1573
      pr_signals_handle();
×
1574

×
1575
      res = recvmsg(fd, &msg, 0);
1576
      continue;
1577
    }
×
1578

×
1579
    pr_trace_msg(trace_channel, 6,
1580
      "error calling recvmsg() on fd %d: %s", fd, strerror(xerrno));
×
1581

1582
    errno = xerrno;
×
1583
    return -1;
×
1584
  }
1585

×
1586
  if (msg.msg_controllen > 0) {
1587
#if defined(HAVE_STRUCT_CMSGCRED)
1588
    struct cmsgcred cred;
×
1589
#elif defined(HAVE_STRUCT_SOCKCRED)
×
1590
    struct sockcred cred;
×
1591
#endif /* !CMSGCRED and !SOCKCRED */
×
1592

1593
    struct cmsghdr *hdr = (struct cmsghdr *) control;
1594

1595
    if (hdr->cmsg_level != SOL_SOCKET) {
×
1596
      pr_trace_msg(trace_channel, 5,
1597
        "message received via recvmsg() on fd %d was not a SOL_SOCKET message",
×
1598
        fd);
1599

×
1600
      errno = EINVAL;
×
1601
      return -1;
×
1602
    }
1603

1604
    if (hdr->cmsg_len < MINCREDSIZE) {
1605
      pr_trace_msg(trace_channel, 5,
×
1606
        "message received via recvmsg() on fd %d was not of proper "
1607
        "length (%u bytes)", fd, MINCREDSIZE);
×
1608

×
1609
      errno = EINVAL;
×
1610
      return -1;
×
1611
    }
×
1612

1613
    if (hdr->cmsg_type != SCM_CREDS) {
×
1614
      pr_trace_msg(trace_channel, 5,
1615
        "message received via recvmsg() on fd %d was not of type SCM_CREDS",
1616
        fd);
1617

×
1618
      errno = EINVAL;
×
1619
      return -1;
1620
    }
×
1621

1622
#if defined(HAVE_STRUCT_CMSGCRED)
1623
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct cmsgcred));
1624

1625
    if (uid != NULL) {
×
1626
      *uid = cred.cmcred_uid;
×
1627
    }
1628

×
1629
    if (gid != NULL) {
1630
      *gid = cred.cmcred_gid;
1631
    }
1632

1633
    if (pid != NULL) {
×
1634
      *pid = cred.cmcred_pid;
×
1635
    }
1636

×
1637
#elif defined(HAVE_STRUCT_SOCKCRED)
1638
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct sockcred));
1639

1640
    if (uid != NULL) {
1641
      *uid = cred.sc_uid;
×
1642
    }
1643

×
1644
    if (gid != NULL) {
×
1645
      *gid = cred.sc_gid;
×
1646
    }
1647
#endif
1648

×
1649
    return 0;
×
1650
  }
1651

×
1652
  return -1;
×
1653
}
1654
#endif /* !SCM_CREDS */
1655

1656
static int ctrls_get_creds_basic(struct sockaddr_un *sock, int cl_fd,
×
1657
    unsigned int max_age, uid_t *uid, gid_t *gid, pid_t *pid) {
×
1658
  pid_t cl_pid = 0;
×
1659
  char *tmp = NULL, *endp = NULL;
1660
  time_t stale_time;
1661
  struct stat st;
×
1662
  unsigned long file_pid = 0;
×
1663

1664
  /* Check the path -- hmmm... */
1665
  PRIVS_ROOT
×
1666
  while (lstat(sock->sun_path, &st) < 0) {
×
1667
    int xerrno = errno;
1668

1669
    if (xerrno == EINTR) {
×
1670
      pr_signals_handle();
×
1671
      continue;
1672
    }
1673

1674
    PRIVS_RELINQUISH
1675
    pr_trace_msg(trace_channel, 2, "error: unable to stat %s: %s",
1676
      sock->sun_path, strerror(xerrno));
2✔
1677

1678
    errno = xerrno;
2✔
1679
    return -1;
2✔
1680
  }
2✔
1681
  PRIVS_RELINQUISH
1682

2✔
1683
  /* Is it a socket? */
1684
  if (pr_ctrls_issock_unix(st.st_mode) < 0) {
2✔
1685
    errno = ENOTSOCK;
2✔
1686
    return -1;
1687
  }
2✔
1688

2✔
1689
  /* Are the perms _not_ rwx------? */
×
1690
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
1691
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
×
1692
    pr_trace_msg(trace_channel, 3,
×
1693
      "error: unable to accept connection: incorrect mode");
×
1694
    errno = EPERM;
1695
    return -1;
1696
  }
2✔
1697

1698
  /* Is it new enough? */
1699
  stale_time = time(NULL) - max_age;
2✔
1700

2✔
1701
  if (st.st_atime < stale_time ||
1702
      st.st_ctime < stale_time ||
1703
      st.st_mtime < stale_time) {
1704
    pool *tmp_pool;
×
1705
    char *msg = "error: stale connection";
1706

1707
    pr_trace_msg(trace_channel, 3,
×
1708
      "unable to accept connection: stale connection");
1709

×
1710
    /* Log the times being compared, to aid in debugging this situation. */
1711
    if (st.st_atime < stale_time) {
1712
      time_t age = stale_time - st.st_atime;
1713

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

1719
    if (st.st_ctime < stale_time) {
1720
      time_t age = stale_time - st.st_ctime;
1721

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

1727
    if (st.st_mtime < stale_time) {
1728
      time_t age = stale_time - st.st_mtime;
1729

1730
      pr_trace_msg(trace_channel, 3,
1731
        "last modified time of '%s' is %lu secs old (must be less than %u "
1732
        "secs)", sock->sun_path, (unsigned long) age, max_age);
×
1733
    }
×
1734

1735
    tmp_pool = make_sub_pool(permanent_pool);
×
1736

×
1737
    if (pr_ctrls_send_response(tmp_pool, cl_fd, -1, 1, &msg) < 0) {
1738
      pr_trace_msg(trace_channel, 2, "error sending message: %s",
1739
        strerror(errno));
1740
    }
1741

1742
    destroy_pool(tmp_pool);
×
1743

×
1744
    errno = ETIMEDOUT;
×
1745
    return -1;
1746
  }
×
1747

1748
  /* Parse the PID out of the path */
1749
  tmp = sock->sun_path;
46✔
1750
  tmp += strlen(CTRLS_SOCKET_PREFIX);
42✔
1751

3✔
1752
  file_pid = strtoul(tmp, &endp, 10);
1753
  if (endp != NULL &&
46✔
1754
      *endp != '\0') {
35✔
1755
    pr_trace_msg(trace_channel, 3,
3✔
1756
      "error: unable to accept connection: invalid PID");
1757
    errno = EINVAL;
4✔
1758
    return -1;
4✔
1759
  }
1760

7✔
1761
  cl_pid = (pid_t) file_pid;
4✔
1762

1763
  /* Return the IDs of the caller */
1✔
1764
  *uid = st.st_uid;
1✔
1765
  *gid = st.st_gid;
1766
  *pid = cl_pid;
1767

1✔
1768
  return 0;
1✔
1769
}
1770

1771
int pr_ctrls_accept(int fd, uid_t *uid, gid_t *gid, pid_t *pid,
1772
    unsigned int max_age) {
1773
  socklen_t len = 0;
1774
  struct sockaddr_un sock;
1775
  int cl_fd = -1, res = -1, xerrno;
1776
  uid_t basic_uid;
12✔
1777
  gid_t basic_gid;
12✔
1778
  pid_t basic_pid;
12✔
1779

1780
  len = sizeof(sock);
1781

12✔
1782
  cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
1✔
1783
  xerrno = errno;
1✔
1784

1785
  while (cl_fd < 0) {
1786
    if (xerrno == EINTR) {
11✔
1787
      pr_signals_handle();
1788

18✔
1789
      cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
7✔
1790
      xerrno = errno;
1791
      continue;
7✔
1792
    }
7✔
1793

1794
    pr_trace_msg(trace_channel, 3,
1✔
1795
      "error: unable to accept on local socket: %s", strerror(xerrno));
1796

1797
    errno = xerrno;
1✔
1798
    return -1;
1799
  }
1800

1801
  /* NULL terminate the name */
1802
  sock.sun_path[sizeof(sock.sun_path)-1] = '\0';
1803

6✔
1804
  if (strncmp(sock.sun_path, CTRLS_SOCKET_PREFIX,
1✔
1805
      strlen(CTRLS_SOCKET_PREFIX)) != 0) {
1806
    pr_trace_msg(trace_channel, 6, "refusing unexpected client path '%s'",
1✔
1807
      sock.sun_path);
1808
    (void) close(cl_fd);
1809
    errno = EINVAL;
1810
    return -1;
5✔
1811
  }
1✔
1812

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

1826
    (void) close(cl_fd);
1827
    errno = xerrno;
1✔
1828
    return res;
1✔
1829
  }
1✔
1830

1831
#if defined(SO_PEERCRED)
1832
  pr_trace_msg(trace_channel, 5,
2✔
1833
    "checking client credentials using SO_PEERCRED");
2✔
1834
  res = ctrls_get_creds_peercred(cl_fd, uid, gid, pid);
1✔
1835

1836
#elif !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1837
  pr_trace_msg(trace_channel, 5,
1838
    "checking client credentials using getpeereid(2)");
1✔
1839
  res = ctrls_get_creds_peereid(cl_fd, uid, gid);
1840

1841
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1✔
1842
      defined(HAVE_GETPEERUCRED)
2✔
1843
  pr_trace_msg(trace_channel, 5,
1✔
1844
    "checking client credentials using getpeerucred(3)");
1✔
1845
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1✔
1846

1847
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1✔
1848
      !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1849
  pr_trace_msg(trace_channel, 5,
1850
    "checking client credentials using SCM_CREDS");
1✔
1851
  res = ctrls_get_creds_local(cl_fd, uid, gid, pid);
×
1852
#endif
1853

1854
  if (res < 0) {
×
1855
    pr_trace_msg(trace_channel, 5,
1856
      "unable to obtain kernel-enforced client credentials: %s",
1857
       strerror(errno));
1✔
1858

1✔
1859
    /* Fall back to using the Stevens' obtained credentials. */
1✔
1860
    if (uid != NULL) {
1861
      *uid = basic_uid;
1✔
1862
    }
1863

1864
    if (gid != NULL) {
1865
      *gid = basic_gid;
1866
    }
1867

2✔
1868
    if (pid != NULL) {
2✔
1869
      *pid = basic_pid;
1870
    }
1871
  }
1872

1873
  /* Done with the path now */
1874
  PRIVS_ROOT
1875
  (void) unlink(sock.sun_path);
1876
  PRIVS_RELINQUISH
1877

1878
  return cl_fd;
1879
}
1880

1881
void pr_block_ctrls(void) {
1882
  ctrls_blocked = TRUE;
1883
}
1884

1885
void pr_unblock_ctrls(void) {
1886
  ctrls_blocked = FALSE;
1887
}
1888

2✔
1889
int pr_ctrls_check_actions(void) {
×
1890
  register ctrls_action_t *act = NULL;
1891

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

1905
  return 0;
10✔
1906
}
10✔
1907

1908
int pr_run_ctrls(module *mod, const char *action) {
10✔
1909
  register pr_ctrls_t *ctrl = NULL;
1✔
1910
  time_t now;
1✔
1911

1912
  /* Are ctrls blocked? */
1913
  if (ctrls_blocked == TRUE) {
1914
    errno = EPERM;
1915
    return -1;
1916
  }
9✔
1917

1918
  now = time(NULL);
1919

4✔
1920
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
2✔
1921
    int res;
1✔
1922

1923
    if (mod != NULL &&
1924
        ctrl->ctrls_module != NULL &&
1925
        ctrl->ctrls_module != mod) {
7✔
1926
      pr_trace_msg(trace_channel, 19,
4✔
1927
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1928
        mod, ctrl->ctrls_module);
1929
      continue;
9✔
1930
    }
2✔
1931

1932
    /* Be watchful of the various client-side flags.  Note: if
1933
     * ctrl->ctrls_cl is ever NULL, it means there's a bug in the code.
1934
     */
1935
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
1936
      pr_trace_msg(trace_channel, 19,
1937
        "skipping ctrl due to missing client HAVEREQ flag");
1938
      continue;
1939
    }
1940

10✔
1941
    /* Has this control been disabled? */
10✔
1942
    if (ctrl->ctrls_flags & PR_CTRLS_ACT_DISABLED) {
1943
      pr_trace_msg(trace_channel, 19,
10✔
1944
        "skipping ctrl due to ACT_DISABLED flag");
1✔
1945
      continue;
1✔
1946
    }
1947

1948
    /* Is it time to trigger this ctrl? */
1949
    if (!(ctrl->ctrls_flags & PR_CTRLS_FL_REQUESTED)) {
1950
      pr_trace_msg(trace_channel, 19,
1951
        "skipping ctrl due to missing CTRLS_REQUESTED flag");
9✔
1952
      continue;
1953
    }
1954

4✔
1955
    if (ctrl->ctrls_when > now) {
2✔
1956
      pr_trace_msg(trace_channel, 19,
1✔
1957
        "skipping ctrl because it is still pending: now = %lu, ctrl when = %lu",
1958
        (unsigned long) now, (unsigned long) ctrl->ctrls_when);
1959
      ctrl->ctrls_flags |= PR_CTRLS_FL_PENDING;
1960
      pr_ctrls_add_response(ctrl, "request pending");
7✔
1961
      continue;
3✔
1962
    }
1963

1964
    if (action == NULL ||
9✔
1965
        strcmp(ctrl->ctrls_action, action) == 0) {
2✔
1966
      pr_trace_msg(trace_channel, 7, "calling '%s' control handler",
1967
        ctrl->ctrls_action);
1968

1969
    } else {
1970
      continue;
1971
    }
1972

9✔
1973
    pr_unblock_ctrls();
1974
    res = ctrl->ctrls_cb(ctrl,
9✔
1975
      (ctrl->ctrls_cb_args ? ctrl->ctrls_cb_args->nelts : 0),
1976
      (ctrl->ctrls_cb_args ? (char **) ctrl->ctrls_cb_args->elts : NULL));
9✔
1977
    pr_block_ctrls();
8✔
1978

7✔
1979
    pr_trace_msg(trace_channel, 19,
7✔
1980
      "ran '%s' ctrl, callback value = %d", ctrl->ctrls_action, res);
4✔
1981

4✔
1982
    if (res >= PR_CTRLS_STATUS_PENDING) {
1983
      pr_trace_msg(trace_channel, 1, "'%s' ctrl returned inappropriate "
1984
        "value %d, treating as GENERIC_ERROR (%d)", ctrl->ctrls_action, res,
8✔
1985
        PR_CTRLS_STATUS_GENERIC_ERROR);
5✔
1986
      res = PR_CTRLS_STATUS_GENERIC_ERROR;
4✔
1987
    }
1988

4✔
1989
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_REQUESTED;
6✔
1990
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_PENDING;
3✔
1991
    ctrl->ctrls_flags |= PR_CTRLS_FL_HANDLED;
3✔
1992

3✔
1993
    ctrl->ctrls_cb_retval = res;
1994
  }
1995

4✔
1996
  return 0;
4✔
1997
}
1998

1999
int pr_ctrls_reset(void) {
2000
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
2001

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

46✔
2020
  for (ctrl = ctrls_active_list; ctrl; ctrl = next_ctrl) {
2021
    next_ctrl = ctrl->ctrls_next;
46✔
2022

46✔
2023
    if (ctrl->ctrls_cb_retval < PR_CTRLS_STATUS_PENDING) {
46✔
2024
      pr_ctrls_free(ctrl);
12✔
2025
    }
12✔
2026
  }
2027

2028
  return 0;
34✔
2029
}
34✔
2030

×
2031
/* From include/mod_ctrls.h */
2032

2033
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
34✔
2034
 * otherwise. Note that the default is to deny everyone, unless an ACL has
2035
 * been configured.
2036
 */
2037
int pr_ctrls_check_group_acl(gid_t cl_gid, const ctrls_group_acl_t *group_acl) {
34✔
2038
  int res = FALSE;
2039

34✔
2040
  if (group_acl == NULL) {
×
2041
    errno = EINVAL;
×
2042
    return -1;
2043
  }
2044

240✔
2045
  /* Note: the special condition of ngids of 1 and gids of NULL signals
103✔
2046
   * that all groups are to be treated according to the allow member.
2047
   */
103✔
2048
  if (group_acl->gids != NULL) {
2049
    register unsigned int i = 0;
×
2050

×
2051
    for (i = 0; i < group_acl->ngids; i++) {
2052
      gid_t group_acl_gid;
2053

2054
      group_acl_gid = (group_acl->gids)[i];
103✔
2055
      pr_trace_msg(trace_channel, 29,
103✔
2056
        "comparing client GID %lu against group ACL GID %lu (#%u)",
2057
        (unsigned long) cl_gid, (unsigned long) group_acl_gid, i+1);
2058
      if (group_acl_gid == cl_gid) {
34✔
2059
        res = TRUE;
22✔
2060
      }
2061
    }
2062

34✔
2063
  } else if (group_acl->ngids == 1) {
34✔
2064
    res = TRUE;
2065
  }
2066

14✔
2067
  if (!group_acl->allow) {
14✔
2068
    res = !res;
14✔
2069
  }
14✔
2070

2071
  return res;
14✔
2072
}
14✔
2073

2✔
2074
/* Returns TRUE if the given cl_uid is allowed by the user ACL, FALSE
2✔
2075
 * otherwise. Note that the default is to deny everyone, unless an ACL has
2076
 * been configured.
2077
 */
12✔
2078
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
12✔
2079
  int res = FALSE;
2080

2081
  if (user_acl == NULL) {
12✔
2082
    errno = EINVAL;
2083
    return -1;
2084
  }
46✔
2085

34✔
2086
  /* Note: the special condition of nuids of 1 and uids of NULL signals
2087
   * that all users are to be treated according to the allow member.
34✔
2088
   */
2089
  if (user_acl->uids != NULL) {
2090
    register unsigned int i = 0;
34✔
2091

2092
    for (i = 0; i < user_acl->nuids; i++) {
2093
      uid_t user_acl_uid;
2094

12✔
2095
      user_acl_uid = (user_acl->uids)[i];
2096

12✔
2097
      pr_trace_msg(trace_channel, 29,
12✔
2098
        "comparing client UID %lu against user ACL UID %lu (#%u)",
2099
        (unsigned long) cl_uid, (unsigned long) user_acl_uid, i+1);
2100
      if (user_acl_uid == cl_uid) {
12✔
2101
        res = TRUE;
2102
      }
2103
    }
8✔
2104

2105
  } else if (user_acl->nuids == 1) {
8✔
2106
    res = TRUE;
8✔
2107
  }
8✔
2108

8✔
2109
  if (!user_acl->allow) {
2110
    res = !res;
8✔
2111
  }
8✔
2112

6✔
2113
  return res;
6✔
2114
}
4✔
2115

4✔
2116
/* Returns TRUE for allowed, FALSE for denied. */
2117
int pr_ctrls_check_acl(const pr_ctrls_t *ctrl,
2118
    const ctrls_acttab_t *acttab, const char *action) {
4✔
2119
  register unsigned int i = 0;
2120
  int unknown_action = TRUE;
4✔
2121

3✔
2122
  if (ctrl == NULL ||
2123
      ctrl->ctrls_cl == NULL ||
2124
      acttab == NULL ||
1✔
2125
      action == NULL) {
2126
    errno = EINVAL;
2127
    return -1;
2128
  }
2129

2130
  for (i = 0; acttab[i].act_action; i++) {
4✔
2131
    if (strcmp(acttab[i].act_action, action) == 0) {
2132
      int user_check = FALSE, group_check = FALSE;
2133

4✔
2134
      unknown_action = FALSE;
2135

15✔
2136
      if (acttab[i].act_acl != NULL) {
2137
        user_check = pr_ctrls_check_user_acl(ctrl->ctrls_cl->cl_uid,
2138
          &(acttab[i].act_acl->acl_users));
12✔
2139
        pr_trace_msg(trace_channel, 19,
1✔
2140
          "checking user ACL for action '%s' with UID %lu returned %s", action,
1✔
2141
          (unsigned long) ctrl->ctrls_cl->cl_uid,
1✔
2142
          user_check ? "true" : "false");
1✔
2143

2144
        group_check = pr_ctrls_check_group_acl(ctrl->ctrls_cl->cl_gid,
2145
          &(acttab[i].act_acl->acl_groups));
11✔
2146
        pr_trace_msg(trace_channel, 19,
11✔
2147
          "checking group ACL for action '%s' with GID %lu returned %s", action,
11✔
2148
          (unsigned long) ctrl->ctrls_cl->cl_gid,
2149
          group_check ? "true" : "false");
2150
      }
×
2151

2152
      if (user_check != TRUE &&
2153
          group_check != TRUE) {
3✔
2154
        /* Known action is explicitly denied for user and group by this ACL. */
3✔
2155
        return FALSE;
2156
      }
3✔
2157
    }
3✔
2158
  }
2159

2160
  if (unknown_action == TRUE) {
10✔
2161
    pr_trace_msg(trace_channel, 19,
2162
      "checked ACL for unknown/unmatched action '%s', returning false", action);
10✔
2163

10✔
2164
    /* Fail-close by returning false here for unmatched actions. */
10✔
2165
    return FALSE;
10✔
2166
  }
2167

2168
  return TRUE;
10✔
2169
}
10✔
2170

8✔
2171
int pr_ctrls_init_acl(ctrls_acl_t *acl) {
8✔
2172
  if (acl == NULL) {
4✔
2173
    errno = EINVAL;
4✔
2174
    return -1;
2175
  }
2176

6✔
2177
  memset(acl, 0, sizeof(ctrls_acl_t));
2178
  acl->acl_users.allow = acl->acl_groups.allow = TRUE;
6✔
2179

5✔
2180
  return 0;
2181
}
2182

1✔
2183
static char *ctrls_argsep(char **arg) {
2184
  char *ret = NULL, *dst = NULL;
2185
  char quote_mode = 0;
2186

2187
  if (arg == NULL ||
2188
      !*arg ||
6✔
2189
      !**arg) {
2190
    errno = EINVAL;
2191
    return NULL;
6✔
2192
  }
2193

23✔
2194
  while (**arg &&
2195
         PR_ISSPACE(**arg)) {
2196
    (*arg)++;
18✔
2197
  }
1✔
2198

1✔
2199
  if (!**arg) {
1✔
2200
    return NULL;
1✔
2201
  }
2202

2203
  ret = dst = *arg;
17✔
2204

17✔
2205
  if (**arg == '\"') {
17✔
2206
    quote_mode++;
2207
    (*arg)++;
2208
  }
×
2209

2210
  while (**arg && **arg != ',' &&
2211
      (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
5✔
2212

5✔
2213
    if (**arg == '\\' && quote_mode) {
2214
      /* escaped char */
5✔
2215
      if (*((*arg) + 1)) {
5✔
2216
        *dst = *(++(*arg));
2217
      }
2218
    }
23✔
2219

2220
    *dst++ = **arg;
2221
    ++(*arg);
23✔
2222
  }
23✔
2223

2224
  if (**arg) {
23✔
2225
    (*arg)++;
23✔
2226
  }
19✔
2227

19✔
2228
  *dst = '\0';
2229
  return ret;
12✔
2230
}
12✔
2231

2232
char **pr_ctrls_parse_acl(pool *acl_pool, const char *acl_text) {
2233
  char *name = NULL, *acl_text_dup = NULL, **acl_list = NULL;
11✔
2234
  array_header *acl_arr = NULL;
5✔
2235
  pool *tmp_pool = NULL;
3✔
2236

3✔
2237
  if (acl_pool == NULL ||
2238
      acl_text == NULL) {
2239
    errno = EINVAL;
2240
    return NULL;
2241
  }
2242

14✔
2243
  tmp_pool = make_sub_pool(acl_pool);
8✔
2244
  acl_text_dup = pstrdup(tmp_pool, acl_text);
8✔
2245

2246
  /* Allocate an array */
8✔
2247
  acl_arr = make_array(acl_pool, 0, sizeof(char **));
2✔
2248

2249
  /* Add each name to the array */
2250
  while ((name = ctrls_argsep(&acl_text_dup)) != NULL) {
8✔
2251
    char *text;
6✔
2252

2253
    text = pstrdup(acl_pool, name);
2254

2255
    /* Push the name into the ACL array */
2256
    *((char **) push_array(acl_arr)) = text;
2257
  }
6✔
2258

2✔
2259
  /* Terminate the temp array with a NULL, as is proper. */
2✔
2260
  *((char **) push_array(acl_arr)) = NULL;
2✔
2261

2262
  acl_list = (char **) acl_arr->elts;
2263
  destroy_pool(tmp_pool);
2264

12✔
2265
  /* return the array of names */
6✔
2266
  return acl_list;
2267
}
6✔
2268

6✔
2269
int pr_ctrls_set_group_acl(pool *group_acl_pool, ctrls_group_acl_t *group_acl,
2✔
2270
    const char *allow, char *grouplist) {
2271
  char *group = NULL, **groups = NULL;
2272
  array_header *gid_list = NULL;
12✔
2273
  gid_t gid = 0;
6✔
2274
  pool *tmp_pool = NULL;
2275

6✔
2276
  if (group_acl_pool == NULL ||
4✔
2277
      group_acl == NULL ||
2278
      allow == NULL ||
2279
      grouplist == NULL) {
2280
    errno = EINVAL;
2281
    return -1;
6✔
2282
  }
4✔
2283

4✔
2284
  tmp_pool = make_sub_pool(group_acl_pool);
2285

2✔
2286
  if (strcasecmp(allow, "allow") == 0) {
2✔
2287
    group_acl->allow = TRUE;
2✔
2288

2289
  } else {
2290
    group_acl->allow = FALSE;
6✔
2291
  }
×
2292

×
2293
  /* Parse the given expression into an array, then retrieve the GID
2294
   * for each given name.
2295
   */
2296
  groups = pr_ctrls_parse_acl(group_acl_pool, grouplist);
2297

2298
  /* Allocate an array of gid_t's */
2299
  gid_list = make_array(group_acl_pool, 0, sizeof(gid_t));
2300

2301
  for (group = *groups; group != NULL; group = *++groups) {
11✔
2302

2303
    /* Handle a group name of "*" differently. */
11✔
2304
    if (strcmp(group, "*") == 0) {
11✔
2305
      group_acl->ngids = 1;
2306
      group_acl->gids = NULL;
11✔
2307
      destroy_pool(tmp_pool);
2308
      return 0;
11✔
2309
    }
8✔
2310

2311
    gid = pr_auth_name2gid(tmp_pool, group);
2312
    if (gid == (gid_t) -1) {
2313
      continue;
2314
    }
2315

11✔
2316
    *((gid_t *) push_array(gid_list)) = gid;
2317
  }
11✔
2318

2319
  group_acl->ngids = gid_list->nelts;
11✔
2320
  group_acl->gids = (gid_t *) gid_list->elts;
11✔
2321

7✔
2322
  destroy_pool(tmp_pool);
7✔
2323
  return 0;
7✔
2324
}
7✔
2325

2326
int pr_ctrls_set_user_acl(pool *user_acl_pool, ctrls_user_acl_t *user_acl,
2327
    const char *allow, char *userlist) {
2328
  char *user = NULL, **users = NULL;
2329
  array_header *uid_list = NULL;
2330
  uid_t uid = 0;
6✔
2331
  pool *tmp_pool = NULL;
2332

6✔
2333
  /* Sanity checks */
2334
  if (user_acl_pool == NULL ||
6✔
2335
      user_acl == NULL ||
4✔
2336
      allow == NULL ||
2337
      userlist == NULL) {
2338
    errno = EINVAL;
2339
    return -1;
2340
  }
2341

4✔
2342
  tmp_pool = make_sub_pool(user_acl_pool);
2✔
2343

2✔
2344
  if (strcasecmp(allow, "allow") == 0) {
2✔
2345
    user_acl->allow = TRUE;
2346

2347
  } else {
2348
    user_acl->allow = FALSE;
2349
  }
2350

2351
  /* Parse the given expression into an array, then retrieve the UID
4✔
2352
   * for each given name.
2353
   */
2✔
2354
  users = pr_ctrls_parse_acl(user_acl_pool, userlist);
2355

2✔
2356
  /* Allocate an array of uid_t's */
2✔
2357
  uid_list = make_array(user_acl_pool, 0, sizeof(uid_t));
2358

2359
  for (user = *users; user != NULL; user = *++users) {
2360

2361
    /* Handle a user name of "*" differently. */
2362
    if (strcmp(user, "*") == 0) {
2✔
2363
      user_acl->nuids = 1;
2✔
2364
      user_acl->uids = NULL;
2365
      destroy_pool(tmp_pool);
2✔
2366
      return 0;
2✔
2367
    }
2368

2369
    uid = pr_auth_name2uid(tmp_pool, user);
2370
    if (uid == (uid_t) -1) {
2371
      continue;
2372
    }
2373

5✔
2374
    *((uid_t *) push_array(uid_list)) = uid;
2375
  }
5✔
2376

5✔
2377
  user_acl->nuids = uid_list->nelts;
2378
  user_acl->uids = (uid_t *) uid_list->elts;
5✔
2379

2380
  destroy_pool(tmp_pool);
5✔
2381
  return 0;
4✔
2382
}
2383

2384
int pr_ctrls_set_module_acls2(ctrls_acttab_t *acttab, pool *acl_pool,
2385
    char **actions, const char *allow, const char *type, char *list,
2386
    const char **bad_action) {
2387
  register unsigned int i = 0;
4✔
2388
  int all_actions = FALSE;
2389

2390
  if (acttab == NULL ||
4✔
2391
      acl_pool == NULL ||
1✔
2392
      actions == NULL ||
2393
      type == NULL ||
2394
      bad_action == NULL) {
4✔
2395
    errno = EINVAL;
4✔
2396
    return -1;
2397
  }
2398

4✔
2399
  if (strcasecmp(type, "user") != 0 &&
4✔
2400
      strcasecmp(type, "group") != 0) {
4✔
2401
    errno = EINVAL;
2402
    return -1;
4✔
2403
  }
2404

2405
  /* First, sanity check the given list of actions against the actions
2406
   * in the given table.
3✔
2407
   */
2408
  for (i = 0; actions[i]; i++) {
2409
    register unsigned int j = 0;
2410
    int valid_action = FALSE;
1✔
2411

1✔
2412
    if (strcasecmp(actions[i], "all") == 0) {
1✔
2413
      continue;
2414
    }
1✔
2415

2416
    for (j = 0; acttab[j].act_action; j++) {
2417
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
34✔
2418
        valid_action = TRUE;
34✔
2419
        break;
34✔
2420
      }
34✔
2421
    }
34✔
2422

2423
    if (valid_action == FALSE) {
34✔
2424
      *bad_action = actions[i];
34✔
2425
      errno = EPERM;
34✔
2426
      return -1;
34✔
2427
    }
2428
  }
2429

34✔
2430
  for (i = 0; actions[i]; i++) {
34✔
2431
    register unsigned int j = 0;
34✔
2432

34✔
2433
    if (all_actions == FALSE &&
34✔
2434
        strcasecmp(actions[i], "all") == 0) {
2435
      all_actions = TRUE;
34✔
2436
    }
×
2437

2438
    for (j = 0; acttab[j].act_action; j++) {
2439
      int res = 0;
34✔
2440

34✔
2441
      if (all_actions == TRUE ||
34✔
2442
          strcmp(actions[i], acttab[j].act_action) == 0) {
2443

2444
        /* Use the type parameter to determine whether the list is of users or
34✔
2445
         * of groups.
34✔
2446
         */
34✔
2447
        if (strcasecmp(type, "user") == 0) {
2448
          res = pr_ctrls_set_user_acl(acl_pool,
2449
            &(acttab[j].act_acl->acl_users), allow, list);
34✔
2450

34✔
2451
        } else if (strcasecmp(type, "group") == 0) {
34✔
2452
          res = pr_ctrls_set_group_acl(acl_pool,
2453
            &(acttab[j].act_acl->acl_groups), allow, list);
2454
        }
2455

2456
        if (res < 0) {
2457
          *bad_action = actions[i];
2458
          return -1;
34✔
2459
        }
34✔
2460
      }
2461
    }
34✔
2462
  }
×
2463

2464
  return 0;
2465
}
×
2466

×
2467
char *pr_ctrls_set_module_acls(ctrls_acttab_t *acttab, pool *acl_pool,
2468
    char **actions, const char *allow, const char *type, char *list) {
2469
  int res;
34✔
2470
  char *bad_action = NULL;
34✔
2471

34✔
2472
  res = pr_ctrls_set_module_acls2(acttab, acl_pool, actions, allow, type, list,
34✔
2473
    (const char **) &bad_action);
2474
  if (res < 0) {
34✔
2475
    return bad_action;
×
2476
  }
2477

×
2478
  return 0;
2479
}
×
2480

×
2481
int pr_ctrls_unregister_module_actions2(ctrls_acttab_t *acttab,
2482
    char **actions, module *mod, const char **bad_action) {
×
2483
  register unsigned int i = 0;
×
2484

2485
  if (acttab == NULL ||
2486
      actions == NULL ||
34✔
2487
      mod == NULL ||
×
2488
      bad_action == NULL) {
2489
    errno = EINVAL;
×
2490
    return -1;
2491
  }
×
2492

×
2493
  /* First, sanity check the given actions against the actions supported by
2494
   * this module.
×
2495
   */
×
2496
  for (i = 0; actions[i]; i++) {
2497
    register unsigned int j = 0;
2498
    int valid_action = FALSE;
2499

34✔
2500
    for (j = 0; acttab[j].act_action; j++) {
34✔
2501
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
×
2502
        valid_action = TRUE;
2503
        break;
2504
      }
2505
    }
2506

2507
    if (valid_action == FALSE) {
2508
      *bad_action = actions[i];
2509
      errno = EPERM;
34✔
2510
      return -1;
34✔
2511
    }
34✔
2512
  }
2513

2514
  /* Next, iterate through both lists again, looking for actions of the
2515
   * module _not_ in the given list.
2516
   */
2517
  for (i = 0; acttab[i].act_action; i++) {
2518
    register unsigned int j = 0;
34✔
2519
    int have_action = FALSE;
2520

34✔
2521
    for (j = 0; actions[j]; j++) {
2522
      if (strcmp(acttab[i].act_action, actions[j]) == 0) {
34✔
2523
        have_action = TRUE;
34✔
2524
        break;
34✔
2525
      }
2526
    }
2527

×
2528
    if (have_action == TRUE) {
×
2529
      pr_trace_msg(trace_channel, 4, "mod_%s.c: removing '%s' control",
2530
        mod->name, acttab[i].act_action);
×
2531
      pr_ctrls_unregister(mod, acttab[i].act_action);
×
2532
      destroy_pool(acttab[i].act_acl->acl_pool);
2533
    }
2534
  }
2535

2536
  return 0;
2537
}
2538

2539
char *pr_ctrls_unregister_module_actions(ctrls_acttab_t *acttab,
2540
    char **actions, module *mod) {
2541
  int res;
2542
  char *bad_action = NULL;
2543

2544
  res = pr_ctrls_unregister_module_actions2(acttab, actions, mod,
2545
    (const char **) &bad_action);
2546
  if (res < 0) {
2547
    return bad_action;
2548
  }
2549

2550
  return 0;
2551
}
2552

2553
int pr_ctrls_set_logfd(int fd) {
2554

2555
  /* Close any existing log fd. */
2556
  if (ctrls_logfd >= 0) {
2557
    (void) close(ctrls_logfd);
2558
  }
2559

2560
  ctrls_logfd = fd;
2561
  return 0;
2562
}
2563

2564
int pr_ctrls_log(const char *module_version, const char *fmt, ...) {
2565
  va_list msg;
2566
  int res;
2567

2568
  if (ctrls_logfd < 0) {
2569
    return 0;
2570
  }
2571

2572
  if (fmt == NULL) {
2573
    return 0;
2574
  }
2575

2576
  va_start(msg, fmt);
2577
  res = pr_log_vwritefile(ctrls_logfd, module_version, fmt, msg);
2578
  va_end(msg);
2579

2580
  return res;
2581
}
2582

2583
static void ctrls_cleanup_cb(void *user_data) {
2584
  ctrls_pool = NULL;
2585
  ctrls_action_list = NULL;
2586
  ctrls_active_list = NULL;
2587
  ctrls_free_list = NULL;
2588

2589
  action_lookup_next = NULL;
2590
  action_lookup_action = NULL;
2591
  action_lookup_module = NULL;
2592
}
2593

2594
/* Initialize the Controls API. */
2595
int init_ctrls2(const char *socket_path) {
2596
  struct stat st;
2597
  int fd, xerrno;
2598
  struct sockaddr_un sockun;
2599
  size_t socklen;
2600

2601
  if (ctrls_pool != NULL) {
2602
    destroy_pool(ctrls_pool);
2603
  }
2604

2605
  ctrls_pool = make_sub_pool(permanent_pool);
2606
  pr_pool_tag(ctrls_pool, "Controls Pool");
2607
  register_cleanup2(ctrls_pool, NULL, ctrls_cleanup_cb);
2608

2609
  /* Make sure all of the lists are zero'd out. */
2610
  ctrls_action_list = NULL;
2611
  ctrls_active_list = NULL;
2612
  ctrls_free_list = NULL;
2613

2614
   /* And that the lookup indices are (re)set as well... */
2615
  action_lookup_next = NULL;
2616
  action_lookup_action = NULL;
2617
  action_lookup_module = NULL;
2618

2619
  /* Run-time check to find out whether this platform identifies a
2620
   * Unix domain socket file descriptor via the S_ISFIFO macro, or
2621
   * the S_ISSOCK macro.
2622
   */
2623

2624
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
2625
  xerrno = errno;
2626

2627
  if (fd < 0) {
2628
    pr_log_debug(DEBUG10, "unable to create Unix domain socket: %s",
2629
      strerror(xerrno));
2630

2631
    errno = xerrno;
2632
    return -1;
2633
  }
2634

2635
  memset(&sockun, 0, sizeof(sockun));
2636
  sockun.sun_family = AF_UNIX;
2637
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
2638
  socklen = sizeof(struct sockaddr_un);
2639

2640
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
2641
    xerrno = errno;
2642

2643
    pr_log_debug(DEBUG10, "unable to bind to Unix domain socket at '%s': %s",
2644
      socket_path, strerror(xerrno));
2645
    (void) close(fd);
2646
    (void) unlink(socket_path);
2647

2648
    errno = xerrno;
2649
    return -1;
2650
  }
2651

2652
  if (fstat(fd, &st) < 0) {
2653
    xerrno = errno;
2654

2655
    pr_log_debug(DEBUG10, "unable to stat Unix domain socket at '%s': %s",
2656
      socket_path, strerror(xerrno));
2657
    (void) close(fd);
2658
    (void) unlink(socket_path);
2659

2660
    errno = xerrno;
2661
    return -1;
2662
  }
2663

2664
#if defined(S_ISFIFO)
2665
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISFIFO");
2666
  if (S_ISFIFO(st.st_mode)) {
2667
    ctrls_use_isfifo = TRUE;
2668
  }
2669
#else
2670
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISFIFO: "
2671
    "macro undefined");
2672
#endif /* S_ISFIFO */
2673

2674
#if defined(S_ISSOCK)
2675
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISSOCK");
2676
  if (S_ISSOCK(st.st_mode)) {
2677
    ctrls_use_isfifo = FALSE;
2678
  }
2679
#else
2680
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISSOCK: "
2681
    "macro undefined");
2682
#endif /* S_ISSOCK */
2683

2684
  pr_trace_msg(trace_channel, 9,
2685
    "using %s macro for Unix domain socket detection",
2686
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
2687

2688
  (void) close(fd);
2689
  (void) unlink(socket_path);
2690
  return 0;
2691
}
2692

2693
void init_ctrls(void) {
2694
  const char *socket_path = PR_RUN_DIR "/test.sock";
2695

2696
  (void) init_ctrls2(socket_path);
2697
}
2698
#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