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

proftpd / proftpd / 21452178162

28 Jan 2026 07:22PM UTC coverage: 93.026% (+0.4%) from 92.638%
21452178162

push

github

51330 of 55178 relevant lines covered (93.03%)

222.09 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_MAX_REQ_SIZE        1024
46
#define CTRLS_MAX_RESP_SIZE        (1024 * 1024)
47

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

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

64
static unsigned char ctrls_blocked = FALSE;
65

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

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

72
static int ctrls_use_isfifo = FALSE;
73

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

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

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

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

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

92
  pr_block_ctrls();
93

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

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

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

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

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

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

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

123
  return act;
124
}
27✔
125

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

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

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

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

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

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

151
  return ctrl;
152
}
9✔
153

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

257
  ctrls_action_list = act;
12✔
258

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

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

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

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

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

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

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

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

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

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

298
  pr_unblock_ctrls();
15✔
299

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

305
  return 0;
306
}
11✔
307

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

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

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

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

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

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

342
  return 0;
343
}
6✔
344

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

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

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

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

374
  return 0;
375
}
6✔
376

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

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

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

397
  return 0;
398
}
14✔
399

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

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

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

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

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

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

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

435
  return 0;
436
}
5✔
437

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

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

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

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

461
  return 0;
462
}
7✔
463

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

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

474
  msglen = strlen(msg);
475

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

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

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

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

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

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

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

4✔
503
      pr_signals_unblock();
504

505
      errno = xerrno;
4✔
506
      return -1;
4✔
507
    }
508

509
    break;
6✔
510
  }
511

6✔
512
  pr_signals_unblock();
6✔
513
  return 0;
6✔
514
}
6✔
515

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

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

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

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

×
540
  json = pr_json_object_alloc(tmp_pool);
×
541

542
  res = pr_json_object_set_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY, action);
×
543
  xerrno = errno;
×
544

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

6✔
549
    errno = xerrno;
2✔
550
    return -1;
2✔
551
  }
552

2✔
553
  args = pr_json_array_alloc(tmp_pool);
×
554

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

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

2✔
564
      errno = xerrno;
565
      return -1;
2✔
566
    }
×
567
  }
×
568

×
569
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, args);
570
  xerrno = errno;
×
571

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

577
    errno = xerrno;
2✔
578
    return -1;
2✔
579
  }
2✔
580

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

584
  pr_json_array_free(args);
585
  pr_json_object_free(json);
15✔
586
  destroy_pool(tmp_pool);
15✔
587

15✔
588
  errno = xerrno;
15✔
589
  return res;
15✔
590
}
15✔
591

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

13✔
602
  if (cl == NULL ||
1✔
603
      cl->cl_ctrls == NULL) {
1✔
604
    errno = EINVAL;
605
    return -1;
606
  }
607

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

12✔
613
  /* No interruptions */
614
  pr_signals_block();
12✔
615

1✔
616
  /* Read in the size of the message, as JSON text. */
617

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

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

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

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

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

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

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

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

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

1✔
661
  if (msglen > CTRLS_MAX_REQ_SIZE) {
1✔
662
    destroy_pool(tmp_pool);
1✔
663
    pr_signals_unblock();
664

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

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

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

1✔
678
  if (nread < 0) {
679
    destroy_pool(tmp_pool);
680
    pr_signals_unblock();
8✔
681

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

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

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

1✔
701
  json = pr_json_object_from_text(tmp_pool, msg);
702
  xerrno = errno;
703

1✔
704
  if (json == NULL) {
1✔
705
    destroy_pool(tmp_pool);
1✔
706
    pr_signals_unblock();
707

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

716
  res = pr_json_object_get_string(tmp_pool, json, CTRLS_REQ_ACTION_KEY,
717
    &reqaction);
12✔
718
  xerrno = errno;
6✔
719

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

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

5✔
732
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_REQ_ARGS_KEY, &args);
733
  xerrno = errno;
14✔
734

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

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

×
747
  nreqargs = pr_json_array_count(args);
×
748
  pr_trace_msg(trace_channel, 19, "received request argc: %u", nreqargs);
749

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

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

×
767
  pr_trace_msg(trace_channel, 19, "known action '%s' requested", reqaction);
×
768

769
  for (i = 0; i < nreqargs; i++) {
770
    size_t reqarglen = 0;
771
    char *reqarg = NULL;
772

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

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

1✔
785
      errno = EINVAL;
786
      return -1;
787
    }
1✔
788

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

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

5✔
802
      errno = xerrno;
803
      return -1;
804
    }
8✔
805
  }
806

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

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

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

6✔
819
  while (next_ctrl != NULL) {
6✔
820
    (void) pr_ctrls_copy_args(ctrl, next_ctrl);
1✔
821

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

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

829
    next_ctrl = ctrls_lookup_next_action(NULL, TRUE);
5✔
830
  }
831

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

837
  return 0;
×
838
}
×
839

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

4✔
848
  if (p == NULL ||
×
849
      fd < 0) {
×
850
    errno = EINVAL;
×
851
    return -1;
852
  }
×
853

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

860
  tmp_pool = make_sub_pool(p);
5✔
861
  pr_pool_tag(tmp_pool, "Controls API send_response pool");
×
862

×
863
  json = pr_json_object_alloc(tmp_pool);
×
864

865
  res = pr_json_object_set_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY,
×
866
    (double) status);
×
867
  xerrno = errno;
868

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

5✔
873
    errno = xerrno;
5✔
874
    return -1;
5✔
875
  }
876

5✔
877
  resps = pr_json_array_alloc(tmp_pool);
5✔
878

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

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

10✔
888
      errno = xerrno;
10✔
889
      return -1;
10✔
890
    }
891
  }
892

10✔
893
  res = pr_json_object_set_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, resps);
10✔
894
  xerrno = errno;
895

3✔
896
  if (res < 0) {
3✔
897
    pr_json_array_free(resps);
898
    pr_json_object_free(json);
899
    destroy_pool(tmp_pool);
900

7✔
901
    errno = xerrno;
902
    return -1;
903
  }
904

7✔
905
  res = ctrls_send_msg(tmp_pool, fd, json);
7✔
906
  xerrno = errno;
907

7✔
908
  pr_json_array_free(resps);
1✔
909
  pr_json_object_free(json);
910
  destroy_pool(tmp_pool);
1✔
911

×
912
  errno = xerrno;
913
  return res;
914
}
×
915

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

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

×
935
  /* No interruptions. */
936
  pr_signals_block();
937

×
938
  /* Read in the size of the message, as JSON text. */
×
939

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

943
  if (nread < 0) {
944
    pr_signals_unblock();
945

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

1✔
950
    errno = xerrno;
1✔
951
    return -1;
952
  }
1✔
953

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

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

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

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

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

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

1✔
984
  if (msglen > CTRLS_MAX_RESP_SIZE) {
985
    destroy_pool(tmp_pool);
986
    pr_signals_unblock();
3✔
987

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

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

1000
  if (nread < 0) {
1✔
1001
    destroy_pool(tmp_pool);
1✔
1002
    pr_signals_unblock();
1003

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

5✔
1011
  /* Watch for short reads. */
2✔
1012
  if ((unsigned int) nread != msglen) {
1013
    destroy_pool(tmp_pool);
1014
    pr_signals_unblock();
1015

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

1023
  json = pr_json_object_from_text(tmp_pool, msg);
1024
  xerrno = errno;
1✔
1025

1✔
1026
  if (json == NULL) {
1✔
1027
    destroy_pool(tmp_pool);
1✔
1028
    pr_signals_unblock();
1029

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

×
1038
  res = pr_json_object_get_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY, &dv);
1039
  xerrno = errno;
1040

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

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

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

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

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

×
1067
    errno = EINVAL;
×
1068
    return -1;
1069
  }
1070

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

1074
  resparr = make_array(p, 0, sizeof(char *));
13✔
1075

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

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

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

7✔
1096
      errno = EINVAL;
1✔
1097
      return -1;
1✔
1098
    }
1099

1100
    *((char **) push_array(resparr)) = pstrdup(p, resp);
1101
  }
6✔
1102

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

13✔
1107
  pr_json_array_free(resps);
8✔
1108
  pr_json_object_free(json);
2✔
1109
  destroy_pool(tmp_pool);
2✔
1110
  pr_signals_unblock();
1✔
1111

1✔
1112
  return respargc;
1113
}
1114

1✔
1115
static pr_ctrls_t *ctrls_lookup_action(module *mod, const char *action,
1116
    unsigned char skip_disabled) {
1117

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

×
1123
  /* Wrapper around ctrls_lookup_next_action() */
1124
  return ctrls_lookup_next_action(mod, skip_disabled);
1125
}
2✔
1126

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

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

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

2✔
1141
  for (act = action_lookup_next; act; act = act->next) {
1142
    if (skip_disabled && (act->flags & PR_CTRLS_ACT_DISABLED)) {
1143
      continue;
1144
    }
1145

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

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

7✔
1155
  return NULL;
1✔
1156
}
1✔
1157

1✔
1158
int pr_get_registered_actions(pr_ctrls_t *ctrl, int flags) {
1✔
1159
  register ctrls_action_t *act = NULL;
1160
  int count = 0;
1161

1162
  if (ctrl == NULL) {
6✔
1163
    errno = EINVAL;
1✔
1164
    return -1;
1✔
1165
  }
1166

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

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

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

1✔
1184
        count++;
1✔
1185
        break;
1186

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

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

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

1200
        count++;
1201
        break;
1202

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

1211
  return count;
1212
}
1213

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

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

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

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

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

1249
  if (have_action == FALSE) {
×
1250
    errno = ENOENT;
×
1251
    return -1;
1252
  }
1253

1254
  return 0;
2✔
1255
}
1256

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

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

2✔
1270
  res = write(fd, buf, 1);
1271
  while (res < 0) {
1272
    if (errno == EINTR) {
2✔
1273
      pr_signals_handle();
×
1274

1275
      res = write(fd, buf, 1);
×
1276
      continue;
1277
    }
×
1278

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

1285
  return res;
1286
}
2✔
1287
#endif /* !SCM_CREDS */
×
1288

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

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

1299
  /* No interruptions */
1300
  pr_signals_block();
2✔
1301

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

2✔
1307
    pr_signals_unblock();
1✔
1308

1309
    errno = xerrno;
1✔
1310
    return -1;
1311
  }
1✔
1312

1✔
1313
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
1✔
1314
    int xerrno = errno;
1315

1✔
1316
    (void) close(fd);
1✔
1317
    pr_signals_unblock();
1318

1319
    errno = xerrno;
1320
    return -1;
1321
  }
1322

1323
  /* Fill in the socket address */
1324
  memset(&cl_sock, 0, sizeof(cl_sock));
1325

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

1333
  cl_sock.sun_family = AF_UNIX;
1334
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
1335
    "/tmp/ftp.cl", (unsigned int) getpid());
1✔
1336
  len = sizeof(cl_sock);
1✔
1337

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

1347
      (void) close(fd);
1348
      pr_signals_unblock();
1349

3✔
1350
      errno = EEXIST;
1351
      return -1;
1352
    }
1353
  }
1354

1355
  (void) unlink(cl_sock.sun_path);
2✔
1356

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

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

1367
    errno = xerrno;
×
1368
    return -1;
1369
  }
×
1370

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

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

1381
    errno = xerrno;
×
1382
    return -1;
×
1383
  }
1384

1385
  /* Now connect to the real server */
×
1386
  memset(&ctrl_sock, 0, sizeof(ctrl_sock));
×
1387

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

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

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

1401
    errno = xerrno;
1402
    return -1;
1403
  }
1404

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

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

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

1423
  pr_signals_unblock();
1424
  return fd;
1425
}
1426

1427
int pr_ctrls_issock_unix(mode_t sock_mode) {
1428

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

1443
  errno = ENOSYS;
1444
  return -1;
1445
}
1446

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

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

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

1465
    errno = EPERM;
1466
    return -1;
1467
  }
1468

1469
  if (uid != NULL) {
1470
    *uid = cred.uid;
1471
  }
1472

1473
  if (gid != NULL) {
1474
    *gid = cred.gid;
1475
  }
1476

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

1481
  return 0;
1482
}
1483
#endif /* SO_PEERCRED */
1484

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

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

1493
    errno = xerrno;
1494
    return -1;
1495
  }
1496

1497
  return 0;
1498
}
1499
#endif /* !HAVE_GETPEEREID */
1500

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

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

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

1512
    errno = xerrno;
1513
    return -1;
1514
  }
1515

1516
  if (uid != NULL) {
1517
    *uid = ucred_getruid(cred);
1518
  }
1519

1520
  if (gid != NULL) {
1521
    *gid = ucred_getrgid(cred);
1522
  }
1523

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

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

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

1548
  char control[MINCREDSIZE];
1549

1550
  iov.iov_base = buf;
1551
  iov.iov_len = 1;
1552

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

1560
  res = recvmsg(fd, &msg, 0);
×
1561
  while (res < 0) {
1562
    int xerrno = errno;
×
1563

×
1564
    if (xerrno == EINTR) {
×
1565
      pr_signals_handle();
×
1566

1567
      res = recvmsg(fd, &msg, 0);
1568
      continue;
×
1569
    }
×
1570

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

×
1574
    errno = xerrno;
×
1575
    return -1;
1576
  }
1577

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

1585
    struct cmsghdr *hdr = (struct cmsghdr *) control;
×
1586

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

×
1592
      errno = EINVAL;
1593
      return -1;
1594
    }
1595

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

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

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

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

×
1614
#if defined(HAVE_STRUCT_CMSGCRED)
1615
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct cmsgcred));
1616

1617
    if (uid != NULL) {
×
1618
      *uid = cred.cmcred_uid;
×
1619
    }
1620

×
1621
    if (gid != NULL) {
1622
      *gid = cred.cmcred_gid;
1623
    }
1624

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

×
1629
#elif defined(HAVE_STRUCT_SOCKCRED)
1630
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct sockcred));
1631

1632
    if (uid != NULL) {
1633
      *uid = cred.sc_uid;
×
1634
    }
×
1635

1636
    if (gid != NULL) {
×
1637
      *gid = cred.sc_gid;
1638
    }
1639
#endif
1640

1641
    return 0;
×
1642
  }
1643

×
1644
  return -1;
×
1645
}
×
1646
#endif /* !SCM_CREDS */
1647

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

1655
  /* Check the path -- hmmm... */
1656
  PRIVS_ROOT
×
1657
  while (stat(sock->sun_path, &st) < 0) {
×
1658
    int xerrno = errno;
×
1659

1660
    if (xerrno == EINTR) {
1661
      pr_signals_handle();
×
1662
      continue;
×
1663
    }
1664

1665
    PRIVS_RELINQUISH
×
1666
    pr_trace_msg(trace_channel, 2, "error: unable to stat %s: %s",
×
1667
      sock->sun_path, strerror(xerrno));
1668
    (void) close(cl_fd);
1669

×
1670
    errno = xerrno;
×
1671
    return -1;
1672
  }
1673
  PRIVS_RELINQUISH
1674

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

1682
  /* Are the perms _not_ rwx------? */
2✔
1683
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
1684
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
2✔
1685
    pr_trace_msg(trace_channel, 3,
2✔
1686
      "error: unable to accept connection: incorrect mode");
1687
    (void) close(cl_fd);
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
    (void) close(cl_fd);
1738

1739
    errno = ETIMEDOUT;
1740
    return -1;
1741
  }
1742

×
1743
  /* Parse the PID out of the path */
×
1744
  tmp = sock->sun_path;
×
1745
  tmp += strlen("/tmp/ftp.cl");
1746
  cl_pid = atol(tmp);
×
1747

1748
  /* Return the IDs of the caller */
1749
  if (uid != NULL) {
46✔
1750
    *uid = st.st_uid;
42✔
1751
  }
3✔
1752

1753
  if (gid != NULL) {
46✔
1754
    *gid = st.st_gid;
35✔
1755
  }
3✔
1756

1757
  if (pid != NULL) {
4✔
1758
    *pid = cl_pid;
4✔
1759
  }
1760

7✔
1761
  return 0;
4✔
1762
}
1763

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

1770
  len = sizeof(sock);
1771

1772
  cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
1773
  xerrno = errno;
1774

1775
  while (cl_fd < 0) {
1776
    if (xerrno == EINTR) {
12✔
1777
      pr_signals_handle();
12✔
1778

12✔
1779
      cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
1780
      xerrno = errno;
1781
      continue;
12✔
1782
    }
1✔
1783

1✔
1784
    pr_trace_msg(trace_channel, 3,
1785
      "error: unable to accept on local socket: %s", strerror(xerrno));
1786

11✔
1787
    errno = xerrno;
1788
    return -1;
18✔
1789
  }
7✔
1790

1791
  /* NULL terminate the name */
7✔
1792
  sock.sun_path[sizeof(sock.sun_path)-1] = '\0';
7✔
1793

1794
#if defined(SO_PEERCRED)
1✔
1795
  pr_trace_msg(trace_channel, 5,
1796
    "checking client credentials using SO_PEERCRED");
1797
  res = ctrls_get_creds_peercred(cl_fd, uid, gid, pid);
1✔
1798

1799
#elif !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1800
  pr_trace_msg(trace_channel, 5,
1801
    "checking client credentials using getpeereid(2)");
1802
  res = ctrls_get_creds_peereid(cl_fd, uid, gid);
1803

6✔
1804
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1✔
1805
      defined(HAVE_GETPEERUCRED)
1806
  pr_trace_msg(trace_channel, 5,
1✔
1807
    "checking client credentials using getpeerucred(3)");
1808
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1809

1810
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
5✔
1811
      !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1✔
1812
  pr_trace_msg(trace_channel, 5,
1813
    "checking client credentials using SCM_CREDS");
1✔
1814
  res = ctrls_get_creds_local(cl_fd, uid, gid, pid);
1815
#endif
1816

1817
  /* Fallback to the Stevens method of determining connection credentials,
4✔
1818
   * if the kernel-enforced methods did not pan out.
1✔
1819
   */
1820
  if (res < 0) {
1✔
1821
    pr_trace_msg(trace_channel, 5,
1822
      "checking client credentials using Stevens' method");
1823
    res = ctrls_get_creds_basic(&sock, cl_fd, max_age, uid, gid, pid);
3✔
1824
    if (res < 0) {
1✔
1825
      return res;
1826
    }
1827
  }
1✔
1828

1✔
1829
  /* Done with the path now */
1✔
1830
  PRIVS_ROOT
1831
  (void) unlink(sock.sun_path);
1832
  PRIVS_RELINQUISH
2✔
1833

2✔
1834
  return cl_fd;
1✔
1835
}
1836

1837
void pr_block_ctrls(void) {
1838
  ctrls_blocked = TRUE;
1✔
1839
}
1840

1841
void pr_unblock_ctrls(void) {
1✔
1842
  ctrls_blocked = FALSE;
2✔
1843
}
1✔
1844

1✔
1845
int pr_ctrls_check_actions(void) {
1✔
1846
  register ctrls_action_t *act = NULL;
1847

1✔
1848
  for (act = ctrls_action_list; act; act = act->next) {
1849
    if (act->flags & PR_CTRLS_ACT_SOLITARY) {
1850
      /* This is a territorial action -- only one instance allowed */
1✔
1851
      if (ctrls_lookup_action(NULL, act->action, FALSE)) {
×
1852
        pr_log_pri(PR_LOG_NOTICE,
1853
          "duplicate controls for '%s' action not allowed",
1854
          act->action);
×
1855
        errno = EEXIST;
1856
        return -1;
1857
      }
1✔
1858
    }
1✔
1859
  }
1✔
1860

1861
  return 0;
1✔
1862
}
1863

1864
int pr_run_ctrls(module *mod, const char *action) {
1865
  register pr_ctrls_t *ctrl = NULL;
1866
  time_t now;
1867

2✔
1868
  /* Are ctrls blocked? */
2✔
1869
  if (ctrls_blocked == TRUE) {
1870
    errno = EPERM;
1871
    return -1;
1872
  }
1873

1874
  now = time(NULL);
1875

1876
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
1877
    int res;
1878

1879
    if (mod != NULL &&
1880
        ctrl->ctrls_module != NULL &&
1881
        ctrl->ctrls_module != mod) {
1882
      pr_trace_msg(trace_channel, 19,
1883
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1884
        mod, ctrl->ctrls_module);
1885
      continue;
1886
    }
1887

1888
    /* Be watchful of the various client-side flags.  Note: if
2✔
1889
     * ctrl->ctrls_cl is ever NULL, it means there's a bug in the code.
×
1890
     */
×
1891
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
1892
      pr_trace_msg(trace_channel, 19,
1893
        "skipping ctrl due to missing client HAVEREQ flag");
1894
      continue;
2✔
1895
    }
1896

1897
    /* Has this control been disabled? */
1898
    if (ctrl->ctrls_flags & PR_CTRLS_ACT_DISABLED) {
1899
      pr_trace_msg(trace_channel, 19,
1900
        "skipping ctrl due to ACT_DISABLED flag");
1901
      continue;
1902
    }
1903

10✔
1904
    /* Is it time to trigger this ctrl? */
10✔
1905
    if (!(ctrl->ctrls_flags & PR_CTRLS_FL_REQUESTED)) {
1906
      pr_trace_msg(trace_channel, 19,
10✔
1907
        "skipping ctrl due to missing CTRLS_REQUESTED flag");
1✔
1908
      continue;
1✔
1909
    }
1910

1911
    if (ctrl->ctrls_when > now) {
1912
      pr_trace_msg(trace_channel, 19,
1913
        "skipping ctrl because it is still pending: now = %lu, ctrl when = %lu",
1914
        (unsigned long) now, (unsigned long) ctrl->ctrls_when);
9✔
1915
      ctrl->ctrls_flags |= PR_CTRLS_FL_PENDING;
1916
      pr_ctrls_add_response(ctrl, "request pending");
1917
      continue;
4✔
1918
    }
2✔
1919

1✔
1920
    if (action == NULL ||
1921
        strcmp(ctrl->ctrls_action, action) == 0) {
1922
      pr_trace_msg(trace_channel, 7, "calling '%s' control handler",
1923
        ctrl->ctrls_action);
7✔
1924

4✔
1925
    } else {
1926
      continue;
1927
    }
9✔
1928

2✔
1929
    pr_unblock_ctrls();
1930
    res = ctrl->ctrls_cb(ctrl,
1931
      (ctrl->ctrls_cb_args ? ctrl->ctrls_cb_args->nelts : 0),
1932
      (ctrl->ctrls_cb_args ? (char **) ctrl->ctrls_cb_args->elts : NULL));
1933
    pr_block_ctrls();
1934

1935
    pr_trace_msg(trace_channel, 19,
1936
      "ran '%s' ctrl, callback value = %d", ctrl->ctrls_action, res);
1937

1938
    if (res >= PR_CTRLS_STATUS_PENDING) {
10✔
1939
      pr_trace_msg(trace_channel, 1, "'%s' ctrl returned inappropriate "
10✔
1940
        "value %d, treating as GENERIC_ERROR (%d)", ctrl->ctrls_action, res,
1941
        PR_CTRLS_STATUS_GENERIC_ERROR);
10✔
1942
      res = PR_CTRLS_STATUS_GENERIC_ERROR;
1✔
1943
    }
1✔
1944

1945
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_REQUESTED;
1946
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_PENDING;
1947
    ctrl->ctrls_flags |= PR_CTRLS_FL_HANDLED;
1948

1949
    ctrl->ctrls_cb_retval = res;
9✔
1950
  }
1951

1952
  return 0;
4✔
1953
}
2✔
1954

1✔
1955
int pr_ctrls_reset(void) {
1956
  pr_ctrls_t *ctrl = NULL, *next_ctrl = NULL;
1957

1958
  /* NOTE: need a clean_ctrls() or somesuch that will, after sending any
7✔
1959
   * responses, iterate through the list and "free" any ctrls whose
3✔
1960
   * ctrls_cb_retval is zero.  This feature is used to handle things like
1961
   * shutdown requests in the future -- the request is only considered
1962
   * "processed" when the callback returns zero.  Any non-zero requests are
9✔
1963
   * not cleared, and are considered "pending".  However, this brings up the
2✔
1964
   * complication of an additional request for that action being issued by the
1965
   * client before the request is processed.  Simplest solution: remove the
1966
   * old request args, and replace them with the new ones.
1967
   *
1968
   * This requires that the return value of the ctrl callback be explicitly
1969
   * documented.
1970
   *
9✔
1971
   * How about: ctrls_cb_retval = 1  pending
1972
   *                              0  processed, OK    (reset)
9✔
1973
   *                             -1  processed, error (reset)
1974
   */
9✔
1975

8✔
1976
  for (ctrl = ctrls_active_list; ctrl; ctrl = next_ctrl) {
7✔
1977
    next_ctrl = ctrl->ctrls_next;
7✔
1978

4✔
1979
    if (ctrl->ctrls_cb_retval < PR_CTRLS_STATUS_PENDING) {
4✔
1980
      pr_ctrls_free(ctrl);
1981
    }
1982
  }
8✔
1983

5✔
1984
  return 0;
4✔
1985
}
1986

4✔
1987
/* From include/mod_ctrls.h */
6✔
1988

3✔
1989
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
3✔
1990
 * otherwise. Note that the default is to deny everyone, unless an ACL has
3✔
1991
 * been configured.
1992
 */
1993
int pr_ctrls_check_group_acl(gid_t cl_gid, const ctrls_group_acl_t *group_acl) {
4✔
1994
  int res = FALSE;
4✔
1995

1996
  if (group_acl == NULL) {
1997
    errno = EINVAL;
1998
    return -1;
1999
  }
2000

2001
  /* Note: the special condition of ngids of 1 and gids of NULL signals
2002
   * that all groups are to be treated according to the allow member.
2003
   */
7✔
2004
  if (group_acl->gids != NULL) {
7✔
2005
    register unsigned int i = 0;
1✔
2006

1✔
2007
    for (i = 0; i < group_acl->ngids; i++) {
2008
      if ((group_acl->gids)[i] == cl_gid) {
2009
        res = TRUE;
6✔
2010
      }
6✔
2011
    }
2012

6✔
2013
  } else if (group_acl->ngids == 1) {
2014
    res = TRUE;
2015
  }
46✔
2016

46✔
2017
  if (!group_acl->allow) {
46✔
2018
    res = !res;
2019
  }
46✔
2020

46✔
2021
  return res;
46✔
2022
}
12✔
2023

12✔
2024
/* Returns TRUE if the given cl_uid is allowed by the user ACL, FALSE
2025
 * otherwise. Note that the default is to deny everyone, unless an ACL has
2026
 * been configured.
34✔
2027
 */
34✔
2028
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
×
2029
  int res = FALSE;
2030

2031
  if (user_acl == NULL) {
34✔
2032
    errno = EINVAL;
2033
    return -1;
2034
  }
2035

34✔
2036
  /* Note: the special condition of nuids of 1 and uids of NULL signals
2037
   * that all users are to be treated according to the allow member.
34✔
2038
   */
×
2039
  if (user_acl->uids != NULL) {
×
2040
    register unsigned int i = 0;
2041

2042
    for (i = 0; i < user_acl->nuids; i++) {
240✔
2043
      if ((user_acl->uids)[i] == cl_uid) {
103✔
2044
        res = TRUE;
2045
      }
103✔
2046
    }
2047

×
2048
  } else if (user_acl->nuids == 1) {
×
2049
    res = TRUE;
2050
  }
2051

2052
  if (!user_acl->allow) {
103✔
2053
    res = !res;
103✔
2054
  }
2055

2056
  return res;
34✔
2057
}
22✔
2058

2059
/* Returns TRUE for allowed, FALSE for denied. */
2060
int pr_ctrls_check_acl(const pr_ctrls_t *ctrl,
34✔
2061
    const ctrls_acttab_t *acttab, const char *action) {
34✔
2062
  register unsigned int i = 0;
2063
  int unknown_action = TRUE;
2064

14✔
2065
  if (ctrl == NULL ||
14✔
2066
      ctrl->ctrls_cl == NULL ||
14✔
2067
      acttab == NULL ||
14✔
2068
      action == NULL) {
2069
    errno = EINVAL;
14✔
2070
    return -1;
14✔
2071
  }
2✔
2072

2✔
2073
  for (i = 0; acttab[i].act_action; i++) {
2074
    if (strcmp(acttab[i].act_action, action) == 0) {
2075
      int user_check = FALSE, group_check = FALSE;
12✔
2076

12✔
2077
      unknown_action = FALSE;
2078

2079
      if (acttab[i].act_acl != NULL) {
12✔
2080
        user_check = pr_ctrls_check_user_acl(ctrl->ctrls_cl->cl_uid,
2081
          &(acttab[i].act_acl->acl_users));
2082
        pr_trace_msg(trace_channel, 19,
46✔
2083
          "checking user ACL for action '%s' with UID %lu returned %s", action,
34✔
2084
          (unsigned long) ctrl->ctrls_cl->cl_uid,
2085
          user_check ? "true" : "false");
34✔
2086

2087
        group_check = pr_ctrls_check_group_acl(ctrl->ctrls_cl->cl_gid,
2088
          &(acttab[i].act_acl->acl_groups));
34✔
2089
        pr_trace_msg(trace_channel, 19,
2090
          "checking group ACL for action '%s' with GID %lu returned %s", action,
2091
          (unsigned long) ctrl->ctrls_cl->cl_gid,
2092
          group_check ? "true" : "false");
12✔
2093
      }
2094

12✔
2095
      if (user_check != TRUE &&
12✔
2096
          group_check != TRUE) {
2097
        /* Known action is explicitly denied for user and group by this ACL. */
2098
        return FALSE;
12✔
2099
      }
2100
    }
2101
  }
8✔
2102

2103
  if (unknown_action == TRUE) {
8✔
2104
    pr_trace_msg(trace_channel, 19,
8✔
2105
      "checked ACL for unknown/unmatched action '%s', returning false", action);
8✔
2106

8✔
2107
    /* Fail-close by returning false here for unmatched actions. */
2108
    return FALSE;
8✔
2109
  }
8✔
2110

6✔
2111
  return TRUE;
6✔
2112
}
4✔
2113

4✔
2114
int pr_ctrls_init_acl(ctrls_acl_t *acl) {
2115
  if (acl == NULL) {
2116
    errno = EINVAL;
4✔
2117
    return -1;
2118
  }
4✔
2119

3✔
2120
  memset(acl, 0, sizeof(ctrls_acl_t));
2121
  acl->acl_users.allow = acl->acl_groups.allow = TRUE;
2122

1✔
2123
  return 0;
2124
}
2125

2126
static char *ctrls_argsep(char **arg) {
2127
  char *ret = NULL, *dst = NULL;
2128
  char quote_mode = 0;
4✔
2129

2130
  if (arg == NULL ||
2131
      !*arg ||
4✔
2132
      !**arg) {
2133
    errno = EINVAL;
15✔
2134
    return NULL;
2135
  }
2136

12✔
2137
  while (**arg &&
1✔
2138
         PR_ISSPACE(**arg)) {
1✔
2139
    (*arg)++;
1✔
2140
  }
1✔
2141

2142
  if (!**arg) {
2143
    return NULL;
11✔
2144
  }
11✔
2145

11✔
2146
  ret = dst = *arg;
2147

2148
  if (**arg == '\"') {
×
2149
    quote_mode++;
2150
    (*arg)++;
2151
  }
3✔
2152

3✔
2153
  while (**arg && **arg != ',' &&
2154
      (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
3✔
2155

3✔
2156
    if (**arg == '\\' && quote_mode) {
2157
      /* escaped char */
2158
      if (*((*arg) + 1)) {
10✔
2159
        *dst = *(++(*arg));
2160
      }
10✔
2161
    }
10✔
2162

10✔
2163
    *dst++ = **arg;
10✔
2164
    ++(*arg);
2165
  }
2166

10✔
2167
  if (**arg) {
10✔
2168
    (*arg)++;
8✔
2169
  }
8✔
2170

4✔
2171
  *dst = '\0';
4✔
2172
  return ret;
2173
}
2174

6✔
2175
char **pr_ctrls_parse_acl(pool *acl_pool, const char *acl_text) {
2176
  char *name = NULL, *acl_text_dup = NULL, **acl_list = NULL;
6✔
2177
  array_header *acl_arr = NULL;
5✔
2178
  pool *tmp_pool = NULL;
2179

2180
  if (acl_pool == NULL ||
1✔
2181
      acl_text == NULL) {
2182
    errno = EINVAL;
2183
    return NULL;
2184
  }
2185

2186
  tmp_pool = make_sub_pool(acl_pool);
6✔
2187
  acl_text_dup = pstrdup(tmp_pool, acl_text);
2188

2189
  /* Allocate an array */
6✔
2190
  acl_arr = make_array(acl_pool, 0, sizeof(char **));
2191

23✔
2192
  /* Add each name to the array */
2193
  while ((name = ctrls_argsep(&acl_text_dup)) != NULL) {
2194
    char *text;
18✔
2195

1✔
2196
    text = pstrdup(acl_pool, name);
1✔
2197

1✔
2198
    /* Push the name into the ACL array */
1✔
2199
    *((char **) push_array(acl_arr)) = text;
2200
  }
2201

17✔
2202
  /* Terminate the temp array with a NULL, as is proper. */
17✔
2203
  *((char **) push_array(acl_arr)) = NULL;
17✔
2204

2205
  acl_list = (char **) acl_arr->elts;
2206
  destroy_pool(tmp_pool);
×
2207

2208
  /* return the array of names */
2209
  return acl_list;
5✔
2210
}
5✔
2211

2212
int pr_ctrls_set_group_acl(pool *group_acl_pool, ctrls_group_acl_t *group_acl,
5✔
2213
    const char *allow, char *grouplist) {
5✔
2214
  char *group = NULL, **groups = NULL;
2215
  array_header *gid_list = NULL;
2216
  gid_t gid = 0;
23✔
2217
  pool *tmp_pool = NULL;
2218

2219
  if (group_acl_pool == NULL ||
23✔
2220
      group_acl == NULL ||
23✔
2221
      allow == NULL ||
2222
      grouplist == NULL) {
23✔
2223
    errno = EINVAL;
23✔
2224
    return -1;
19✔
2225
  }
19✔
2226

2227
  tmp_pool = make_sub_pool(group_acl_pool);
12✔
2228

12✔
2229
  if (strcasecmp(allow, "allow") == 0) {
2230
    group_acl->allow = TRUE;
2231

11✔
2232
  } else {
5✔
2233
    group_acl->allow = FALSE;
3✔
2234
  }
3✔
2235

2236
  /* Parse the given expression into an array, then retrieve the GID
2237
   * for each given name.
2238
   */
2239
  groups = pr_ctrls_parse_acl(group_acl_pool, grouplist);
2240

14✔
2241
  /* Allocate an array of gid_t's */
8✔
2242
  gid_list = make_array(group_acl_pool, 0, sizeof(gid_t));
8✔
2243

2244
  for (group = *groups; group != NULL; group = *++groups) {
8✔
2245

2✔
2246
    /* Handle a group name of "*" differently. */
2247
    if (strcmp(group, "*") == 0) {
2248
      group_acl->ngids = 1;
8✔
2249
      group_acl->gids = NULL;
6✔
2250
      destroy_pool(tmp_pool);
2251
      return 0;
2252
    }
2253

2254
    gid = pr_auth_name2gid(tmp_pool, group);
2255
    if (gid == (gid_t) -1) {
6✔
2256
      continue;
2✔
2257
    }
2✔
2258

2✔
2259
    *((gid_t *) push_array(gid_list)) = gid;
2260
  }
2261

2262
  group_acl->ngids = gid_list->nelts;
12✔
2263
  group_acl->gids = (gid_t *) gid_list->elts;
6✔
2264

2265
  destroy_pool(tmp_pool);
6✔
2266
  return 0;
6✔
2267
}
2✔
2268

2269
int pr_ctrls_set_user_acl(pool *user_acl_pool, ctrls_user_acl_t *user_acl,
2270
    const char *allow, char *userlist) {
12✔
2271
  char *user = NULL, **users = NULL;
6✔
2272
  array_header *uid_list = NULL;
2273
  uid_t uid = 0;
6✔
2274
  pool *tmp_pool = NULL;
4✔
2275

2276
  /* Sanity checks */
2277
  if (user_acl_pool == NULL ||
2278
      user_acl == NULL ||
2279
      allow == NULL ||
6✔
2280
      userlist == NULL) {
4✔
2281
    errno = EINVAL;
4✔
2282
    return -1;
2283
  }
2✔
2284

2✔
2285
  tmp_pool = make_sub_pool(user_acl_pool);
2✔
2286

2287
  if (strcasecmp(allow, "allow") == 0) {
2288
    user_acl->allow = TRUE;
6✔
2289

×
2290
  } else {
×
2291
    user_acl->allow = FALSE;
2292
  }
2293

2294
  /* Parse the given expression into an array, then retrieve the UID
2295
   * for each given name.
2296
   */
2297
  users = pr_ctrls_parse_acl(user_acl_pool, userlist);
2298

2299
  /* Allocate an array of uid_t's */
11✔
2300
  uid_list = make_array(user_acl_pool, 0, sizeof(uid_t));
2301

11✔
2302
  for (user = *users; user != NULL; user = *++users) {
11✔
2303

2304
    /* Handle a user name of "*" differently. */
11✔
2305
    if (strcmp(user, "*") == 0) {
2306
      user_acl->nuids = 1;
11✔
2307
      user_acl->uids = NULL;
8✔
2308
      destroy_pool(tmp_pool);
2309
      return 0;
2310
    }
2311

2312
    uid = pr_auth_name2uid(tmp_pool, user);
2313
    if (uid == (uid_t) -1) {
11✔
2314
      continue;
2315
    }
11✔
2316

2317
    *((uid_t *) push_array(uid_list)) = uid;
11✔
2318
  }
11✔
2319

7✔
2320
  user_acl->nuids = uid_list->nelts;
7✔
2321
  user_acl->uids = (uid_t *) uid_list->elts;
7✔
2322

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

2327
int pr_ctrls_set_module_acls2(ctrls_acttab_t *acttab, pool *acl_pool,
2328
    char **actions, const char *allow, const char *type, char *list,
6✔
2329
    const char **bad_action) {
2330
  register unsigned int i = 0;
6✔
2331
  int all_actions = FALSE;
2332

6✔
2333
  if (acttab == NULL ||
4✔
2334
      acl_pool == NULL ||
2335
      actions == NULL ||
2336
      type == NULL ||
2337
      bad_action == NULL) {
2338
    errno = EINVAL;
2339
    return -1;
4✔
2340
  }
2✔
2341

2✔
2342
  if (strcasecmp(type, "user") != 0 &&
2✔
2343
      strcasecmp(type, "group") != 0) {
2344
    errno = EINVAL;
2345
    return -1;
2346
  }
2347

2348
  /* First, sanity check the given list of actions against the actions
2349
   * in the given table.
4✔
2350
   */
2351
  for (i = 0; actions[i]; i++) {
2✔
2352
    register unsigned int j = 0;
2353
    int valid_action = FALSE;
2✔
2354

2✔
2355
    if (strcasecmp(actions[i], "all") == 0) {
2356
      continue;
2357
    }
2358

2359
    for (j = 0; acttab[j].act_action; j++) {
2360
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
2✔
2361
        valid_action = TRUE;
2✔
2362
        break;
2363
      }
2✔
2364
    }
2✔
2365

2366
    if (valid_action == FALSE) {
2367
      *bad_action = actions[i];
2368
      errno = EPERM;
2369
      return -1;
2370
    }
2371
  }
5✔
2372

2373
  for (i = 0; actions[i]; i++) {
5✔
2374
    register unsigned int j = 0;
5✔
2375

2376
    if (all_actions == FALSE &&
5✔
2377
        strcasecmp(actions[i], "all") == 0) {
2378
      all_actions = TRUE;
5✔
2379
    }
4✔
2380

2381
    for (j = 0; acttab[j].act_action; j++) {
2382
      int res = 0;
2383

2384
      if (all_actions == TRUE ||
2385
          strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2386

2387
        /* Use the type parameter to determine whether the list is of users or
2388
         * of groups.
4✔
2389
         */
1✔
2390
        if (strcasecmp(type, "user") == 0) {
2391
          res = pr_ctrls_set_user_acl(acl_pool,
2392
            &(acttab[j].act_acl->acl_users), allow, list);
4✔
2393

4✔
2394
        } else if (strcasecmp(type, "group") == 0) {
2395
          res = pr_ctrls_set_group_acl(acl_pool,
2396
            &(acttab[j].act_acl->acl_groups), allow, list);
4✔
2397
        }
4✔
2398

4✔
2399
        if (res < 0) {
2400
          *bad_action = actions[i];
4✔
2401
          return -1;
2402
        }
2403
      }
2404
    }
3✔
2405
  }
2406

2407
  return 0;
2408
}
1✔
2409

1✔
2410
char *pr_ctrls_set_module_acls(ctrls_acttab_t *acttab, pool *acl_pool,
1✔
2411
    char **actions, const char *allow, const char *type, char *list) {
2412
  int res;
1✔
2413
  char *bad_action = NULL;
2414

2415
  res = pr_ctrls_set_module_acls2(acttab, acl_pool, actions, allow, type, list,
34✔
2416
    (const char **) &bad_action);
34✔
2417
  if (res < 0) {
34✔
2418
    return bad_action;
34✔
2419
  }
34✔
2420

2421
  return 0;
34✔
2422
}
34✔
2423

34✔
2424
int pr_ctrls_unregister_module_actions2(ctrls_acttab_t *acttab,
34✔
2425
    char **actions, module *mod, const char **bad_action) {
2426
  register unsigned int i = 0;
2427

34✔
2428
  if (acttab == NULL ||
34✔
2429
      actions == NULL ||
34✔
2430
      mod == NULL ||
34✔
2431
      bad_action == NULL) {
34✔
2432
    errno = EINVAL;
2433
    return -1;
34✔
2434
  }
×
2435

2436
  /* First, sanity check the given actions against the actions supported by
2437
   * this module.
34✔
2438
   */
34✔
2439
  for (i = 0; actions[i]; i++) {
34✔
2440
    register unsigned int j = 0;
2441
    int valid_action = FALSE;
2442

34✔
2443
    for (j = 0; acttab[j].act_action; j++) {
34✔
2444
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
34✔
2445
        valid_action = TRUE;
2446
        break;
2447
      }
34✔
2448
    }
34✔
2449

34✔
2450
    if (valid_action == FALSE) {
2451
      *bad_action = actions[i];
2452
      errno = EPERM;
2453
      return -1;
2454
    }
2455
  }
2456

34✔
2457
  /* Next, iterate through both lists again, looking for actions of the
34✔
2458
   * module _not_ in the given list.
2459
   */
34✔
2460
  for (i = 0; acttab[i].act_action; i++) {
×
2461
    register unsigned int j = 0;
2462
    int have_action = FALSE;
2463

×
2464
    for (j = 0; actions[j]; j++) {
×
2465
      if (strcmp(acttab[i].act_action, actions[j]) == 0) {
2466
        have_action = TRUE;
2467
        break;
34✔
2468
      }
34✔
2469
    }
34✔
2470

34✔
2471
    if (have_action == TRUE) {
2472
      pr_trace_msg(trace_channel, 4, "mod_%s.c: removing '%s' control",
34✔
2473
        mod->name, acttab[i].act_action);
×
2474
      pr_ctrls_unregister(mod, acttab[i].act_action);
2475
      destroy_pool(acttab[i].act_acl->acl_pool);
×
2476
    }
2477
  }
×
2478

×
2479
  return 0;
2480
}
×
2481

×
2482
char *pr_ctrls_unregister_module_actions(ctrls_acttab_t *acttab,
2483
    char **actions, module *mod) {
2484
  int res;
34✔
2485
  char *bad_action = NULL;
×
2486

2487
  res = pr_ctrls_unregister_module_actions2(acttab, actions, mod,
×
2488
    (const char **) &bad_action);
2489
  if (res < 0) {
×
2490
    return bad_action;
×
2491
  }
2492

×
2493
  return 0;
×
2494
}
2495

2496
int pr_ctrls_set_logfd(int fd) {
2497

34✔
2498
  /* Close any existing log fd. */
34✔
2499
  if (ctrls_logfd >= 0) {
×
2500
    (void) close(ctrls_logfd);
2501
  }
2502

2503
  ctrls_logfd = fd;
2504
  return 0;
2505
}
2506

2507
int pr_ctrls_log(const char *module_version, const char *fmt, ...) {
34✔
2508
  va_list msg;
34✔
2509
  int res;
34✔
2510

2511
  if (ctrls_logfd < 0) {
2512
    return 0;
2513
  }
2514

2515
  if (fmt == NULL) {
2516
    return 0;
34✔
2517
  }
2518

34✔
2519
  va_start(msg, fmt);
2520
  res = pr_log_vwritefile(ctrls_logfd, module_version, fmt, msg);
34✔
2521
  va_end(msg);
34✔
2522

34✔
2523
  return res;
2524
}
2525

×
2526
static void ctrls_cleanup_cb(void *user_data) {
×
2527
  ctrls_pool = NULL;
2528
  ctrls_action_list = NULL;
×
2529
  ctrls_active_list = NULL;
×
2530
  ctrls_free_list = NULL;
2531

2532
  action_lookup_next = NULL;
2533
  action_lookup_action = NULL;
2534
  action_lookup_module = NULL;
2535
}
2536

2537
/* Initialize the Controls API. */
2538
int init_ctrls2(const char *socket_path) {
2539
  struct stat st;
2540
  int fd, xerrno;
2541
  struct sockaddr_un sockun;
2542
  size_t socklen;
2543

2544
  if (ctrls_pool != NULL) {
2545
    destroy_pool(ctrls_pool);
2546
  }
2547

2548
  ctrls_pool = make_sub_pool(permanent_pool);
2549
  pr_pool_tag(ctrls_pool, "Controls Pool");
2550
  register_cleanup2(ctrls_pool, NULL, ctrls_cleanup_cb);
2551

2552
  /* Make sure all of the lists are zero'd out. */
2553
  ctrls_action_list = NULL;
2554
  ctrls_active_list = NULL;
2555
  ctrls_free_list = NULL;
2556

2557
   /* And that the lookup indices are (re)set as well... */
2558
  action_lookup_next = NULL;
2559
  action_lookup_action = NULL;
2560
  action_lookup_module = NULL;
2561

2562
  /* Run-time check to find out whether this platform identifies a
2563
   * Unix domain socket file descriptor via the S_ISFIFO macro, or
2564
   * the S_ISSOCK macro.
2565
   */
2566

2567
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
2568
  xerrno = errno;
2569

2570
  if (fd < 0) {
2571
    pr_log_debug(DEBUG10, "unable to create Unix domain socket: %s",
2572
      strerror(xerrno));
2573

2574
    errno = xerrno;
2575
    return -1;
2576
  }
2577

2578
  memset(&sockun, 0, sizeof(sockun));
2579
  sockun.sun_family = AF_UNIX;
2580
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
2581
  socklen = sizeof(struct sockaddr_un);
2582

2583
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
2584
    xerrno = errno;
2585

2586
    pr_log_debug(DEBUG10, "unable to bind to Unix domain socket at '%s': %s",
2587
      socket_path, strerror(xerrno));
2588
    (void) close(fd);
2589
    (void) unlink(socket_path);
2590

2591
    errno = xerrno;
2592
    return -1;
2593
  }
2594

2595
  if (fstat(fd, &st) < 0) {
2596
    xerrno = errno;
2597

2598
    pr_log_debug(DEBUG10, "unable to stat Unix domain socket at '%s': %s",
2599
      socket_path, strerror(xerrno));
2600
    (void) close(fd);
2601
    (void) unlink(socket_path);
2602

2603
    errno = xerrno;
2604
    return -1;
2605
  }
2606

2607
#if defined(S_ISFIFO)
2608
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISFIFO");
2609
  if (S_ISFIFO(st.st_mode)) {
2610
    ctrls_use_isfifo = TRUE;
2611
  }
2612
#else
2613
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISFIFO: "
2614
    "macro undefined");
2615
#endif /* S_ISFIFO */
2616

2617
#if defined(S_ISSOCK)
2618
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISSOCK");
2619
  if (S_ISSOCK(st.st_mode)) {
2620
    ctrls_use_isfifo = FALSE;
2621
  }
2622
#else
2623
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISSOCK: "
2624
    "macro undefined");
2625
#endif /* S_ISSOCK */
2626

2627
  pr_trace_msg(trace_channel, 9,
2628
    "using %s macro for Unix domain socket detection",
2629
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
2630

2631
  (void) close(fd);
2632
  (void) unlink(socket_path);
2633
  return 0;
2634
}
2635

2636
void init_ctrls(void) {
2637
  const char *socket_path = PR_RUN_DIR "/test.sock";
2638

2639
  (void) init_ctrls2(socket_path);
2640
}
2641
#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