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

proftpd / proftpd / 26127302554

19 May 2026 07:34PM UTC coverage: 92.635% (-0.4%) from 93.024%
26127302554

push

github

Castaglia
Make a flaky, racy mod_copy regression test more reliable.

47303 of 51064 relevant lines covered (92.63%)

241.07 hits per line

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

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

24
/* Controls API routines */
25

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

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

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

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

41
#if defined(PR_USE_CTRLS)
42

43
#include "mod_ctrls.h"
44

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

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

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

64
static unsigned char ctrls_blocked = FALSE;
65

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

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

72
static int ctrls_use_isfifo = FALSE;
73

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

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

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

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

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

92
  pr_block_ctrls();
93

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

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

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

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

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

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

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

123
  return act;
12✔
124
}
125

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

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

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

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

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

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

151
  return ctrl;
27✔
152
}
153

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

257
  ctrls_action_list = act;
12✔
258

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

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

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

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

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

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

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

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

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

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

298
  pr_unblock_ctrls();
299

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

305
  return 0;
306
}
307

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

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

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

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

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

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

342
  return 0;
8✔
343
}
344

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

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

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

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

374
  return 0;
2✔
375
}
376

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

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

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

397
  return 0;
1✔
398
}
399

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

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

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

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

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

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

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

435
  return 0;
12✔
436
}
437

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

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

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

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

461
  return 0;
462
}
463

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

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

474
  msglen = strlen(msg);
7✔
475

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

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

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

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

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

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

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

502
      pr_signals_unblock();
×
503

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

508
    break;
509
  }
510

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

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

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

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

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

539
  json = pr_json_object_alloc(tmp_pool);
2✔
540

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

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

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

552
  args = pr_json_array_alloc(tmp_pool);
2✔
553

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

825
  return 0;
5✔
826
}
827

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

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

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

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

851
  json = pr_json_object_alloc(tmp_pool);
5✔
852

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

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

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

865
  resps = pr_json_array_alloc(tmp_pool);
5✔
866

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

946
    if (nread < 0) {
947
      (void) pr_trace_msg(trace_channel, 3,
948
        "error reading %u of response message size: %s",
949
        (unsigned int) sizeof(uint32_t), strerror(xerrno));
950
      errno = xerrno;
951
      return -1;
952
    }
953

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

961
  tmp_pool = make_sub_pool(p);
7✔
962
  pr_pool_tag(tmp_pool, "Controls API recv_response pool");
7✔
963

964
  pr_trace_msg(trace_channel, 27,
7✔
965
    "receiving Controls response message (%lu bytes) from fd %d",
966
    (unsigned long) msglen, fd);
967

968
  /* Impose max response size limit here, for Issue #2036. */
969
  if (msglen > CTRLS_MAX_RESP_SIZE) {
7✔
970
    destroy_pool(tmp_pool);
1✔
971
    pr_signals_unblock();
1✔
972

973
    (void) pr_trace_msg(trace_channel, 3,
1✔
974
      "message size (%lu bytes) exceeds max (%lu bytes), unable to receive "
975
      "response", (unsigned long) msglen, (unsigned long) CTRLS_MAX_RESP_SIZE);
976
    errno = E2BIG;
1✔
977
    return -1;
1✔
978
  }
979

980
  /* Allocate one byte for the terminating NUL. */
981
  msg = pcalloc(tmp_pool, msglen + 1);
6✔
982
  nread = read(fd, msg, msglen);
12✔
983
  xerrno = errno;
6✔
984

985
  if (nread < 0) {
6✔
986
    destroy_pool(tmp_pool);
×
987
    pr_signals_unblock();
×
988

989
    pr_trace_msg(trace_channel, 3,
×
990
      "error reading %lu bytes of response message: %s",
991
      (unsigned long) msglen, strerror(xerrno));
992
    errno = xerrno;
×
993
    return -1;
×
994
  }
995

996
  /* Watch for short reads. */
997
  if ((unsigned int) nread != msglen) {
6✔
998
    destroy_pool(tmp_pool);
1✔
999
    pr_signals_unblock();
1✔
1000

1001
    (void) pr_trace_msg(trace_channel, 3,
1✔
1002
      "short read (%d of %u bytes) of message text, unable to receive response",
1003
      nread, (unsigned int) msglen);
1004
    errno = EPERM;
1✔
1005
    return -1;
1✔
1006
  }
1007

1008
  json = pr_json_object_from_text(tmp_pool, msg);
5✔
1009
  xerrno = errno;
5✔
1010

1011
  if (json == NULL) {
5✔
1012
    destroy_pool(tmp_pool);
1✔
1013
    pr_signals_unblock();
1✔
1014

1015
    (void) pr_trace_msg(trace_channel, 3,
1✔
1016
      "read invalid JSON message text ('%.*s' [%lu bytes]), unable to "
1017
      "receive response: %s", (int) msglen, msg, (unsigned long) msglen,
1018
      strerror(xerrno));
1019
    errno = EINVAL;
1✔
1020
    return -1;
1✔
1021
  }
1022

1023
  res = pr_json_object_get_number(tmp_pool, json, CTRLS_RESP_STATUS_KEY, &dv);
4✔
1024
  xerrno = errno;
4✔
1025

1026
  if (res < 0) {
4✔
1027
    pr_json_object_free(json);
1✔
1028
    destroy_pool(tmp_pool);
1✔
1029
    pr_signals_unblock();
1✔
1030

1031
    (void) pr_trace_msg(trace_channel, 3,
1✔
1032
      "unable to read response status (%s), unable to receive response",
1033
      strerror(xerrno));
1034
    errno = EINVAL;
1✔
1035
    return -1;
1✔
1036
  }
1037

1038
  *status = (int) dv;
3✔
1039
  pr_trace_msg(trace_channel, 19, "received response status: %d", *status);
3✔
1040

1041
  res = pr_json_object_get_array(tmp_pool, json, CTRLS_RESP_RESPS_KEY, &resps);
3✔
1042
  xerrno = errno;
3✔
1043

1044
  if (res < 0) {
3✔
1045
    (void) pr_trace_msg(trace_channel, 3,
1✔
1046
      "unable to read message responses (%s), unable to receive response",
1047
      strerror(xerrno));
1048
    pr_json_object_free(json);
1✔
1049
    destroy_pool(tmp_pool);
1✔
1050
    pr_signals_unblock();
1✔
1051

1052
    errno = EINVAL;
1✔
1053
    return -1;
1✔
1054
  }
1055

1056
  respargc = pr_json_array_count(resps);
2✔
1057
  pr_trace_msg(trace_channel, 19, "received response argc: %u", respargc);
2✔
1058

1059
  resparr = make_array(p, 0, sizeof(char *));
2✔
1060

1061
  /* Read each response, and add it to the array */
1062
  for (i = 0; i < respargc; i++) {
3✔
1063
    char *resp = NULL;
2✔
1064

1065
    /* TODO: Handle other response types, such as arrays or objects, for
1066
     * more complex responses.  Think of an action that dumps the memory
1067
     * pools, for example.
1068
     */
1069
    res = pr_json_array_get_string(tmp_pool, resps, i, &resp);
2✔
1070
    xerrno = errno;
2✔
1071

1072
    if (res < 0) {
2✔
1073
      (void) pr_trace_msg(trace_channel, 3,
1✔
1074
        "unable to read message response #%u (%s), unable to receive response",
1075
        i+1, strerror(xerrno));
1076
      pr_json_array_free(resps);
1✔
1077
      pr_json_object_free(json);
1✔
1078
      destroy_pool(tmp_pool);
1✔
1079
      pr_signals_unblock();
1✔
1080

1081
      errno = EINVAL;
1✔
1082
      return -1;
1✔
1083
    }
1084

1085
    *((char **) push_array(resparr)) = pstrdup(p, resp);
1✔
1086
  }
1087

1088
  if (respargv != NULL) {
1✔
1089
    *respargv = ((char **) resparr->elts);
×
1090
  }
1091

1092
  pr_json_array_free(resps);
1✔
1093
  pr_json_object_free(json);
1✔
1094
  destroy_pool(tmp_pool);
1✔
1095
  pr_signals_unblock();
1✔
1096

1097
  return respargc;
1✔
1098
}
1099

1100
static pr_ctrls_t *ctrls_lookup_action(module *mod, const char *action,
×
1101
    unsigned char skip_disabled) {
1102

1103
  /* (Re)set the current indices */
1104
  action_lookup_next = ctrls_action_list;
7✔
1105
  action_lookup_action = action;
7✔
1106
  action_lookup_module = mod;
7✔
1107

1108
  /* Wrapper around ctrls_lookup_next_action() */
1109
  return ctrls_lookup_next_action(mod, skip_disabled);
7✔
1110
}
1111

1112
static pr_ctrls_t *ctrls_lookup_next_action(module *mod,
13✔
1113
    unsigned char skip_disabled) {
1114
  register ctrls_action_t *act = NULL;
13✔
1115

1116
  /* Sanity check */
1117
  if (action_lookup_action == NULL) {
13✔
1118
    errno = EINVAL;
×
1119
    return NULL;
×
1120
  }
1121

1122
  if (mod != action_lookup_module) {
13✔
1123
    return ctrls_lookup_action(mod, action_lookup_action, skip_disabled);
×
1124
  }
1125

1126
  for (act = action_lookup_next; act; act = act->next) {
13✔
1127
    if (skip_disabled && (act->flags & PR_CTRLS_ACT_DISABLED)) {
7✔
1128
      continue;
×
1129
    }
1130

1131
    if (strcmp(act->action, action_lookup_action) == 0 &&
14✔
1132
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
13✔
1133
      action_lookup_next = act->next;
7✔
1134

1135
      /* Use this action object to prepare a ctrl object. */
1136
      return ctrls_prepare(act);
7✔
1137
    }
1138
  }
1139

1140
  return NULL;
1141
}
1142

1143
int pr_get_registered_actions(pr_ctrls_t *ctrl, int flags) {
7✔
1144
  register ctrls_action_t *act = NULL;
7✔
1145
  int count = 0;
7✔
1146

1147
  if (ctrl == NULL) {
7✔
1148
    errno = EINVAL;
1✔
1149
    return -1;
1✔
1150
  }
1151

1152
  /* Are ctrls blocked? */
1153
  if (ctrls_blocked == TRUE) {
6✔
1154
    errno = EPERM;
1✔
1155
    return -1;
1✔
1156
  }
1157

1158
  for (act = ctrls_action_list; act; act = act->next) {
13✔
1159
    switch (flags) {
8✔
1160
      case CTRLS_GET_ACTION_ALL:
2✔
1161
        if (act->module != NULL) {
2✔
1162
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1163
            act->module->name);
1164

1165
        } else {
1166
           pr_ctrls_add_response(ctrl, "%s (core)", act->action);
1✔
1167
        }
1168

1169
        count++;
2✔
1170
        break;
2✔
1171

1172
      case CTRLS_GET_ACTION_ENABLED:
2✔
1173
        if (act->flags & PR_CTRLS_ACT_DISABLED) {
2✔
1174
          continue;
×
1175
        }
1176

1177
        if (act->module != NULL) {
2✔
1178
          pr_ctrls_add_response(ctrl, "%s (mod_%s.c)", act->action,
1✔
1179
            act->module->name);
1180

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

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

1188
      case CTRLS_GET_DESC:
2✔
1189
        pr_ctrls_add_response(ctrl, "%s: %s", act->action,
2✔
1190
          act->desc);
1191
        count++;
2✔
1192
        break;
2✔
1193
    }
1194
  }
1195

1196
  return count;
1197
}
1198

1199
int pr_set_registered_actions(module *mod, const char *action,
7✔
1200
    unsigned char skip_disabled, unsigned int flags) {
1201
  register ctrls_action_t *act = NULL;
7✔
1202
  unsigned char have_action = FALSE;
7✔
1203

1204
  /* Is flags a valid combination of settable flags? */
1205
  if (flags > 0 &&
14✔
1206
      flags != PR_CTRLS_ACT_SOLITARY &&
7✔
1207
      flags != PR_CTRLS_ACT_DISABLED &&
2✔
1208
      flags != (PR_CTRLS_ACT_SOLITARY|PR_CTRLS_ACT_DISABLED)) {
1✔
1209
    errno = EINVAL;
1✔
1210
    return -1;
1✔
1211
  }
1212

1213
  /* Are ctrls blocked? */
1214
  if (ctrls_blocked == TRUE) {
6✔
1215
    errno = EPERM;
1✔
1216
    return -1;
1✔
1217
  }
1218

1219
  for (act = ctrls_action_list; act; act = act->next) {
10✔
1220
    if (skip_disabled == TRUE &&
5✔
1221
        (act->flags & PR_CTRLS_ACT_DISABLED)) {
×
1222
      continue;
×
1223
    }
1224

1225
    if ((action == NULL ||
9✔
1226
         strcmp(action, "all") == 0 ||
7✔
1227
         strcmp(act->action, action) == 0) &&
8✔
1228
        (act->module == mod || mod == ANY_MODULE || mod == NULL)) {
5✔
1229
      have_action = TRUE;
5✔
1230
      act->flags = flags;
5✔
1231
    }
1232
  }
1233

1234
  if (have_action == FALSE) {
5✔
1235
    errno = ENOENT;
1✔
1236
    return -1;
1✔
1237
  }
1238

1239
  return 0;
1240
}
1241

1242
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1243
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1244
static int ctrls_connect_local_creds(int fd) {
1245
  char buf[1] = {'\0'};
1246
  int res;
1247

1248
  /* The backend doesn't care what we send here, but it wants
1249
   * exactly one character to force recvmsg() to block and wait
1250
   * for us.
1251
   */
1252

1253
  res = write(fd, buf, 1);
1254
  while (res < 0) {
1255
    if (errno == EINTR) {
1256
      pr_signals_handle();
1257

1258
      res = write(fd, buf, 1);
1259
      continue;
1260
    }
1261

1262
    pr_trace_msg(trace_channel, 5,
1263
      "error writing credentials byte for LOCAL_CREDS to fd %d: %s", fd,
1264
      strerror(errno));
1265
    return -1;
1266
  }
1267

1268
  return res;
1269
}
1270
#endif /* !SCM_CREDS */
1271

1272
int pr_ctrls_connect(const char *socket_file) {
3✔
1273
  int fd = -1, len = 0;
3✔
1274
  struct sockaddr_un cl_sock, ctrl_sock;
1275

1276
  if (socket_file == NULL) {
3✔
1277
    errno = EINVAL;
1✔
1278
    return -1;
1✔
1279
  }
1280

1281
  /* No interruptions */
1282
  pr_signals_block();
2✔
1283

1284
  /* Create a Unix domain socket */
1285
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
2✔
1286
  if (fd < 0) {
2✔
1287
    int xerrno = errno;
×
1288

1289
    pr_signals_unblock();
×
1290

1291
    errno = xerrno;
×
1292
    return -1;
×
1293
  }
1294

1295
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
2✔
1296
    int xerrno = errno;
×
1297

1298
    (void) close(fd);
×
1299
    pr_signals_unblock();
×
1300

1301
    errno = xerrno;
×
1302
    return -1;
×
1303
  }
1304

1305
  /* Fill in the socket address */
1306
  memset(&cl_sock, 0, sizeof(cl_sock));
2✔
1307

1308
  /* This first part is clever.  First, this process creates a socket in
1309
   * the file system.  It _then_ connect()s to the server.  Upon accept()ing
1310
   * the connection, the server examines the created socket to see that it
1311
   * is indeed a socket, with the proper mode and time.  Clever, but not
1312
   * ideal.
1313
   */
1314

1315
  cl_sock.sun_family = AF_UNIX;
2✔
1316
  pr_snprintf(cl_sock.sun_path, sizeof(cl_sock.sun_path) - 1, "%s%05u",
2✔
1317
    "/tmp/ftp.cl", (unsigned int) getpid());
2✔
1318
  len = sizeof(cl_sock);
2✔
1319

1320
  /* Make sure the file doesn't already exist */
1321
  (void) unlink(cl_sock.sun_path);
2✔
1322

1323
  /* Make it a socket */
1324
  if (bind(fd, (struct sockaddr *) &cl_sock, len) < 0) {
2✔
1325
    int xerrno = errno;
×
1326

1327
    pr_trace_msg(trace_channel, 19, "error binding local socket to '%s': %s",
×
1328
      cl_sock.sun_path, strerror(xerrno));
1329
    (void) unlink(cl_sock.sun_path);
×
1330
    (void) close(fd);
×
1331
    pr_signals_unblock();
×
1332

1333
    errno = xerrno;
×
1334
    return -1;
×
1335
  }
1336

1337
  /* Set the proper mode */
1338
  if (chmod(cl_sock.sun_path, PR_CTRLS_CL_MODE) < 0) {
2✔
1339
    int xerrno = errno;
×
1340

1341
    pr_trace_msg(trace_channel, 19, "error setting local socket mode: %s",
×
1342
      strerror(xerrno));
1343
    (void) unlink(cl_sock.sun_path);
×
1344
    (void) close(fd);
×
1345
    pr_signals_unblock();
×
1346

1347
    errno = xerrno;
×
1348
    return -1;
×
1349
  }
1350

1351
  /* Now connect to the real server */
1352
  memset(&ctrl_sock, 0, sizeof(ctrl_sock));
2✔
1353

1354
  ctrl_sock.sun_family = AF_UNIX;
2✔
1355
  sstrncpy(ctrl_sock.sun_path, socket_file, sizeof(ctrl_sock.sun_path));
2✔
1356
  len = sizeof(ctrl_sock);
2✔
1357

1358
  if (connect(fd, (struct sockaddr *) &ctrl_sock, len) < 0) {
2✔
1359
    int xerrno = errno;
1✔
1360

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

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

1371
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1372
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1373
  if (ctrls_connect_local_creds(fd) < 0) {
1374
    int xerrno = errno;
1375

1376
    pr_trace_msg(trace_channel, 19, "error sending creds to local socket: %s",
1377
      strerror(xerrno));
1378
    (void) unlink(cl_sock.sun_path);
1379
    (void) close(fd);
1380
    pr_signals_unblock();
1381

1382
    errno = xerrno;
1383
    return -1;
1384
  }
1385
#endif /* LOCAL_CREDS */
1386

1387
  pr_signals_unblock();
1✔
1388
  return fd;
1✔
1389
}
1390

1391
int pr_ctrls_issock_unix(mode_t sock_mode) {
3✔
1392

1393
  if (ctrls_use_isfifo == TRUE) {
3✔
1394
#if defined(S_ISFIFO)
1395
    if (S_ISFIFO(sock_mode)) {
×
1396
      return 0;
1397
    }
1398
#endif /* S_ISFIFO */
1399
  } else {
1400
#if defined(S_ISSOCK)
1401
    if (S_ISSOCK(sock_mode)) {
3✔
1402
      return 0;
1403
    }
1404
#endif /* S_ISSOCK */
1405
  }
1406

1407
  errno = ENOSYS;
2✔
1408
  return -1;
2✔
1409
}
1410

1411
#if defined(SO_PEERCRED)
1412
static int ctrls_get_creds_peercred(int fd, uid_t *uid, gid_t *gid,
×
1413
    pid_t *pid) {
1414
# if defined(HAVE_STRUCT_SOCKPEERCRED)
1415
  struct sockpeercred cred;
1416
# else
1417
  struct ucred cred;
1418
# endif /* HAVE_STRUCT_SOCKPEERCRED */
1419
  socklen_t cred_len;
1420

1421
  cred_len = sizeof(cred);
×
1422
  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) {
×
1423
    int xerrno = errno;
×
1424

1425
    pr_trace_msg(trace_channel, 2,
×
1426
      "error obtaining peer credentials using SO_PEERCRED: %s",
1427
      strerror(xerrno));
1428

1429
    errno = EPERM;
×
1430
    return -1;
×
1431
  }
1432

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

1437
  if (gid != NULL) {
×
1438
    *gid = cred.gid;
×
1439
  }
1440

1441
  if (pid != NULL) {
×
1442
    *pid = cred.pid;
×
1443
  }
1444

1445
  return 0;
1446
}
1447
#endif /* SO_PEERCRED */
1448

1449
#if !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1450
static int ctrls_get_creds_peereid(int fd, uid_t *uid, gid_t *gid) {
1451
  if (getpeereid(fd, uid, gid) < 0) {
1452
    int xerrno = errno;
1453

1454
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1455
      "getpeereid(2) on fd %d: %s", fd, strerror(xerrno));
1456

1457
    errno = xerrno;
1458
    return -1;
1459
  }
1460

1461
  return 0;
1462
}
1463
#endif /* !HAVE_GETPEEREID */
1464

1465
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1466
    defined(HAVE_GETPEERUCRED)
1467
static int ctrls_get_creds_peerucred(int fd, uid_t *uid, gid_t *gid) {
1468
  ucred_t *cred = NULL;
1469

1470
  if (getpeerucred(fd, &cred) < 0) {
1471
    int xerrno = errno;
1472

1473
    pr_trace_msg(trace_channel, 7, "error obtaining credentials using "
1474
      "getpeerucred(3) on fd %d: %s", fd, strerror(xerrno));
1475

1476
    errno = xerrno;
1477
    return -1;
1478
  }
1479

1480
  if (uid != NULL) {
1481
    *uid = ucred_getruid(cred);
1482
  }
1483

1484
  if (gid != NULL) {
1485
    *gid = ucred_getrgid(cred);
1486
  }
1487

1488
  ucred_free(cred);
1489
  return 0;
1490
}
1491
#endif /* !HAVE_GETPEERUCRED */
1492

1493
#if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1494
    !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1495
static int ctrls_get_creds_local(int fd, uid_t *uid, gid_t *gid,
1496
    pid_t *pid) {
1497
  int res;
1498
  char buf[1];
1499
  struct iovec iov;
1500
  struct msghdr msg;
1501

1502
# if defined(SOCKCREDSIZE)
1503
#  define MINCREDSIZE                (sizeof(struct cmsghdr) + SOCKCREDSIZE(0))
1504
# else
1505
#  if defined(HAVE_STRUCT_CMSGCRED)
1506
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
1507
#  elif defined(HAVE_STRUCT_SOCKCRED)
1508
#   define MINCREDSIZE                (sizeof(struct cmsghdr) + sizeof(struct sockcred))
1509
#  endif
1510
# endif /* !SOCKCREDSIZE */
1511

1512
  char control[MINCREDSIZE];
1513

1514
  iov.iov_base = buf;
1515
  iov.iov_len = 1;
1516

1517
  memset(&msg, 0, sizeof(msg));
1518
  msg.msg_iov = &iov;
1519
  msg.msg_iovlen = 1;
1520
  msg.msg_control = control;
1521
  msg.msg_controllen = sizeof(control);
1522
  msg.msg_flags = 0;
1523

1524
  res = recvmsg(fd, &msg, 0);
1525
  while (res < 0) {
1526
    int xerrno = errno;
1527

1528
    if (xerrno == EINTR) {
1529
      pr_signals_handle();
1530

1531
      res = recvmsg(fd, &msg, 0);
1532
      continue;
1533
    }
1534

1535
    pr_trace_msg(trace_channel, 6,
1536
      "error calling recvmsg() on fd %d: %s", fd, strerror(xerrno));
1537

1538
    errno = xerrno;
1539
    return -1;
1540
  }
1541

1542
  if (msg.msg_controllen > 0) {
1543
#if defined(HAVE_STRUCT_CMSGCRED)
1544
    struct cmsgcred cred;
1545
#elif defined(HAVE_STRUCT_SOCKCRED)
1546
    struct sockcred cred;
1547
#endif /* !CMSGCRED and !SOCKCRED */
1548

1549
    struct cmsghdr *hdr = (struct cmsghdr *) control;
1550

1551
    if (hdr->cmsg_level != SOL_SOCKET) {
1552
      pr_trace_msg(trace_channel, 5,
1553
        "message received via recvmsg() on fd %d was not a SOL_SOCKET message",
1554
        fd);
1555

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

1560
    if (hdr->cmsg_len < MINCREDSIZE) {
1561
      pr_trace_msg(trace_channel, 5,
1562
        "message received via recvmsg() on fd %d was not of proper "
1563
        "length (%u bytes)", fd, MINCREDSIZE);
1564

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

1569
    if (hdr->cmsg_type != SCM_CREDS) {
1570
      pr_trace_msg(trace_channel, 5,
1571
        "message received via recvmsg() on fd %d was not of type SCM_CREDS",
1572
        fd);
1573

1574
      errno = EINVAL;
1575
      return -1;
1576
    }
1577

1578
#if defined(HAVE_STRUCT_CMSGCRED)
1579
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct cmsgcred));
1580

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

1585
    if (gid != NULL) {
1586
      *gid = cred.cmcred_gid;
1587
    }
1588

1589
    if (pid != NULL) {
1590
      *pid = cred.cmcred_pid;
1591
    }
1592

1593
#elif defined(HAVE_STRUCT_SOCKCRED)
1594
    memcpy(&cred, CMSG_DATA(hdr), sizeof(struct sockcred));
1595

1596
    if (uid != NULL) {
1597
      *uid = cred.sc_uid;
1598
    }
1599

1600
    if (gid != NULL) {
1601
      *gid = cred.sc_gid;
1602
    }
1603
#endif
1604

1605
    return 0;
1606
  }
1607

1608
  return -1;
1609
}
1610
#endif /* !SCM_CREDS */
1611

1612
static int ctrls_get_creds_basic(struct sockaddr_un *sock, int cl_fd,
×
1613
    unsigned int max_age, uid_t *uid, gid_t *gid, pid_t *pid) {
1614
  pid_t cl_pid = 0;
×
1615
  char *tmp = NULL;
×
1616
  time_t stale_time;
1617
  struct stat st;
1618

1619
  /* Check the path -- hmmm... */
1620
  PRIVS_ROOT
×
1621
  while (stat(sock->sun_path, &st) < 0) {
×
1622
    int xerrno = errno;
×
1623

1624
    if (xerrno == EINTR) {
×
1625
      pr_signals_handle();
×
1626
      continue;
×
1627
    }
1628

1629
    PRIVS_RELINQUISH
×
1630
    pr_trace_msg(trace_channel, 2, "error: unable to stat %s: %s",
×
1631
      sock->sun_path, strerror(xerrno));
1632
    (void) close(cl_fd);
×
1633

1634
    errno = xerrno;
×
1635
    return -1;
×
1636
  }
1637
  PRIVS_RELINQUISH
×
1638

1639
  /* Is it a socket? */
1640
  if (pr_ctrls_issock_unix(st.st_mode) < 0) {
×
1641
    (void) close(cl_fd);
×
1642
    errno = ENOTSOCK;
×
1643
    return -1;
×
1644
  }
1645

1646
  /* Are the perms _not_ rwx------? */
1647
  if (st.st_mode & (S_IRWXG|S_IRWXO) ||
×
1648
      ((st.st_mode & S_IRWXU) != PR_CTRLS_CL_MODE)) {
1649
    pr_trace_msg(trace_channel, 3,
×
1650
      "error: unable to accept connection: incorrect mode");
1651
    (void) close(cl_fd);
×
1652
    errno = EPERM;
×
1653
    return -1;
×
1654
  }
1655

1656
  /* Is it new enough? */
1657
  stale_time = time(NULL) - max_age;
×
1658

1659
  if (st.st_atime < stale_time ||
×
1660
      st.st_ctime < stale_time ||
×
1661
      st.st_mtime < stale_time) {
×
1662
    pool *tmp_pool;
1663
    char *msg = "error: stale connection";
×
1664

1665
    pr_trace_msg(trace_channel, 3,
×
1666
      "unable to accept connection: stale connection");
1667

1668
    /* Log the times being compared, to aid in debugging this situation. */
1669
    if (st.st_atime < stale_time) {
×
1670
      time_t age = stale_time - st.st_atime;
×
1671

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

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

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

1685
    if (st.st_mtime < stale_time) {
×
1686
      time_t age = stale_time - st.st_mtime;
×
1687

1688
      pr_trace_msg(trace_channel, 3,
×
1689
        "last modified time of '%s' is %lu secs old (must be less than %u "
1690
        "secs)", sock->sun_path, (unsigned long) age, max_age);
1691
    }
1692

1693
    tmp_pool = make_sub_pool(permanent_pool);
×
1694

1695
    if (pr_ctrls_send_response(tmp_pool, cl_fd, -1, 1, &msg) < 0) {
×
1696
      pr_trace_msg(trace_channel, 2, "error sending message: %s",
×
1697
        strerror(errno));
×
1698
    }
1699

1700
    destroy_pool(tmp_pool);
×
1701
    (void) close(cl_fd);
×
1702

1703
    errno = ETIMEDOUT;
×
1704
    return -1;
1705
  }
1706

1707
  /* Parse the PID out of the path */
1708
  tmp = sock->sun_path;
×
1709
  tmp += strlen("/tmp/ftp.cl");
×
1710
  cl_pid = atol(tmp);
×
1711

1712
  /* Return the IDs of the caller */
1713
  if (uid != NULL) {
×
1714
    *uid = st.st_uid;
×
1715
  }
1716

1717
  if (gid != NULL) {
×
1718
    *gid = st.st_gid;
×
1719
  }
1720

1721
  if (pid != NULL) {
×
1722
    *pid = cl_pid;
×
1723
  }
1724

1725
  return 0;
1726
}
1727

1728
int pr_ctrls_accept(int fd, uid_t *uid, gid_t *gid, pid_t *pid,
2✔
1729
    unsigned int max_age) {
1730
  socklen_t len = 0;
1731
  struct sockaddr_un sock;
1732
  int cl_fd = -1, res = -1, xerrno;
2✔
1733

1734
  len = sizeof(sock);
2✔
1735

1736
  cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
2✔
1737
  xerrno = errno;
2✔
1738

1739
  while (cl_fd < 0) {
4✔
1740
    if (xerrno == EINTR) {
2✔
1741
      pr_signals_handle();
×
1742

1743
      cl_fd = accept(fd, (struct sockaddr *) &sock, &len);
×
1744
      xerrno = errno;
×
1745
      continue;
×
1746
    }
1747

1748
    pr_trace_msg(trace_channel, 3,
2✔
1749
      "error: unable to accept on local socket: %s", strerror(xerrno));
1750

1751
    errno = xerrno;
2✔
1752
    return -1;
2✔
1753
  }
1754

1755
  /* NULL terminate the name */
1756
  sock.sun_path[sizeof(sock.sun_path)-1] = '\0';
×
1757

1758
#if defined(SO_PEERCRED)
1759
  pr_trace_msg(trace_channel, 5,
×
1760
    "checking client credentials using SO_PEERCRED");
1761
  res = ctrls_get_creds_peercred(cl_fd, uid, gid, pid);
×
1762

1763
#elif !defined(SO_PEERCRED) && defined(HAVE_GETPEEREID)
1764
  pr_trace_msg(trace_channel, 5,
1765
    "checking client credentials using getpeereid(2)");
1766
  res = ctrls_get_creds_peereid(cl_fd, uid, gid);
1767

1768
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1769
      defined(HAVE_GETPEERUCRED)
1770
  pr_trace_msg(trace_channel, 5,
1771
    "checking client credentials using getpeerucred(3)");
1772
  res = ctrls_get_creds_peerucred(cl_fd, uid, gid);
1773

1774
#elif !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \
1775
      !defined(HAVE_GETPEERUCRED) && defined(LOCAL_CREDS)
1776
  pr_trace_msg(trace_channel, 5,
1777
    "checking client credentials using SCM_CREDS");
1778
  res = ctrls_get_creds_local(cl_fd, uid, gid, pid);
1779
#endif
1780

1781
  /* Fallback to the Stevens method of determining connection credentials,
1782
   * if the kernel-enforced methods did not pan out.
1783
   */
1784
  if (res < 0) {
×
1785
    pr_trace_msg(trace_channel, 5,
×
1786
      "checking client credentials using Stevens' method");
1787
    res = ctrls_get_creds_basic(&sock, cl_fd, max_age, uid, gid, pid);
×
1788
    if (res < 0) {
×
1789
      return res;
1790
    }
1791
  }
1792

1793
  /* Done with the path now */
1794
  PRIVS_ROOT
×
1795
  (void) unlink(sock.sun_path);
×
1796
  PRIVS_RELINQUISH
×
1797

1798
  return cl_fd;
×
1799
}
1800

1801
void pr_block_ctrls(void) {
3✔
1802
  ctrls_blocked = TRUE;
46✔
1803
}
3✔
1804

1805
void pr_unblock_ctrls(void) {
3✔
1806
  ctrls_blocked = FALSE;
46✔
1807
}
3✔
1808

1809
int pr_ctrls_check_actions(void) {
4✔
1810
  register ctrls_action_t *act = NULL;
4✔
1811

1812
  for (act = ctrls_action_list; act; act = act->next) {
7✔
1813
    if (act->flags & PR_CTRLS_ACT_SOLITARY) {
4✔
1814
      /* This is a territorial action -- only one instance allowed */
1815
      if (ctrls_lookup_action(NULL, act->action, FALSE)) {
2✔
1816
        pr_log_pri(PR_LOG_NOTICE,
1✔
1817
          "duplicate controls for '%s' action not allowed",
1818
          act->action);
1819
        errno = EEXIST;
1✔
1820
        return -1;
1✔
1821
      }
1822
    }
1823
  }
1824

1825
  return 0;
1826
}
1827

1828
int pr_run_ctrls(module *mod, const char *action) {
12✔
1829
  register pr_ctrls_t *ctrl = NULL;
12✔
1830
  time_t now;
1831

1832
  /* Are ctrls blocked? */
1833
  if (ctrls_blocked == TRUE) {
12✔
1834
    errno = EPERM;
1✔
1835
    return -1;
1✔
1836
  }
1837

1838
  now = time(NULL);
11✔
1839

1840
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
18✔
1841
    int res;
1842

1843
    if (mod != NULL &&
14✔
1844
        ctrl->ctrls_module != NULL &&
14✔
1845
        ctrl->ctrls_module != mod) {
1846
      pr_trace_msg(trace_channel, 19,
1✔
1847
        "skipping ctrl due to module mismatch: module = %p, ctrl module = %p",
1848
        mod, ctrl->ctrls_module);
1849
      continue;
1✔
1850
    }
1851

1852
    /* Be watchful of the various client-side flags.  Note: if
1853
     * ctrl->ctrls_cl is ever NULL, it means there's a bug in the code.
1854
     */
1855
    if (ctrl->ctrls_cl->cl_flags != PR_CTRLS_CL_HAVEREQ) {
6✔
1856
      pr_trace_msg(trace_channel, 19,
1✔
1857
        "skipping ctrl due to missing client HAVEREQ flag");
1858
      continue;
1✔
1859
    }
1860

1861
    /* Has this control been disabled? */
1862
    if (ctrl->ctrls_flags & PR_CTRLS_ACT_DISABLED) {
5✔
1863
      pr_trace_msg(trace_channel, 19,
1✔
1864
        "skipping ctrl due to ACT_DISABLED flag");
1865
      continue;
1✔
1866
    }
1867

1868
    /* Is it time to trigger this ctrl? */
1869
    if (!(ctrl->ctrls_flags & PR_CTRLS_FL_REQUESTED)) {
4✔
1870
      pr_trace_msg(trace_channel, 19,
1✔
1871
        "skipping ctrl due to missing CTRLS_REQUESTED flag");
1872
      continue;
1✔
1873
    }
1874

1875
    if (ctrl->ctrls_when > now) {
3✔
1876
      pr_trace_msg(trace_channel, 19,
1✔
1877
        "skipping ctrl because it is still pending: now = %lu, ctrl when = %lu",
1878
        (unsigned long) now, (unsigned long) ctrl->ctrls_when);
1879
      ctrl->ctrls_flags |= PR_CTRLS_FL_PENDING;
1✔
1880
      pr_ctrls_add_response(ctrl, "request pending");
1✔
1881
      continue;
1✔
1882
    }
1883

1884
    if (action == NULL ||
4✔
1885
        strcmp(ctrl->ctrls_action, action) == 0) {
2✔
1886
      pr_trace_msg(trace_channel, 7, "calling '%s' control handler",
1✔
1887
        ctrl->ctrls_action);
1888

1889
    } else {
1890
      continue;
1✔
1891
    }
1892

1893
    pr_unblock_ctrls();
1894
    res = ctrl->ctrls_cb(ctrl,
4✔
1895
      (ctrl->ctrls_cb_args ? ctrl->ctrls_cb_args->nelts : 0),
1✔
1896
      (ctrl->ctrls_cb_args ? (char **) ctrl->ctrls_cb_args->elts : NULL));
1✔
1897
    pr_block_ctrls();
1898

1899
    pr_trace_msg(trace_channel, 19,
1✔
1900
      "ran '%s' ctrl, callback value = %d", ctrl->ctrls_action, res);
1901

1902
    if (res >= PR_CTRLS_STATUS_PENDING) {
1✔
1903
      pr_trace_msg(trace_channel, 1, "'%s' ctrl returned inappropriate "
×
1904
        "value %d, treating as GENERIC_ERROR (%d)", ctrl->ctrls_action, res,
1905
        PR_CTRLS_STATUS_GENERIC_ERROR);
1906
      res = PR_CTRLS_STATUS_GENERIC_ERROR;
×
1907
    }
1908

1909
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_REQUESTED;
1✔
1910
    ctrl->ctrls_flags &= ~PR_CTRLS_FL_PENDING;
1✔
1911
    ctrl->ctrls_flags |= PR_CTRLS_FL_HANDLED;
1✔
1912

1913
    ctrl->ctrls_cb_retval = res;
1✔
1914
  }
1915

1916
  return 0;
1917
}
1918

1919
int pr_ctrls_reset(void) {
2✔
1920
  pr_ctrls_t *ctrl = NULL;
2✔
1921

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

1940
  for (ctrl = ctrls_active_list; ctrl; ctrl = ctrl->ctrls_next) {
2✔
1941
    if (ctrl->ctrls_cb_retval < PR_CTRLS_STATUS_PENDING) {
×
1942
      pr_ctrls_free(ctrl);
×
1943
    }
1944
  }
1945

1946
  return 0;
2✔
1947
}
1948

1949
/* From include/mod_ctrls.h */
1950

1951
/* Returns TRUE if the given cl_gid is allowed by the group ACL, FALSE
1952
 * otherwise. Note that the default is to deny everyone, unless an ACL has
1953
 * been configured.
1954
 */
1955
int pr_ctrls_check_group_acl(gid_t cl_gid, const ctrls_group_acl_t *group_acl) {
10✔
1956
  int res = FALSE;
10✔
1957

1958
  if (group_acl == NULL) {
10✔
1959
    errno = EINVAL;
1✔
1960
    return -1;
1✔
1961
  }
1962

1963
  /* Note: the special condition of ngids of 1 and gids of NULL signals
1964
   * that all groups are to be treated according to the allow member.
1965
   */
1966
  if (group_acl->gids != NULL) {
9✔
1967
    register unsigned int i = 0;
1968

1969
    for (i = 0; i < group_acl->ngids; i++) {
2✔
1970
      if ((group_acl->gids)[i] == cl_gid) {
2✔
1971
        res = TRUE;
1✔
1972
      }
1973
    }
1974

1975
  } else if (group_acl->ngids == 1) {
7✔
1976
    res = TRUE;
4✔
1977
  }
1978

1979
  if (!group_acl->allow) {
9✔
1980
    res = !res;
2✔
1981
  }
1982

1983
  return res;
1984
}
1985

1986
/* Returns TRUE if the given cl_uid is allowed by the user ACL, FALSE
1987
 * otherwise. Note that the default is to deny everyone, unless an ACL has
1988
 * been configured.
1989
 */
1990
int pr_ctrls_check_user_acl(uid_t cl_uid, const ctrls_user_acl_t *user_acl) {
10✔
1991
  int res = FALSE;
10✔
1992

1993
  if (user_acl == NULL) {
10✔
1994
    errno = EINVAL;
1✔
1995
    return -1;
1✔
1996
  }
1997

1998
  /* Note: the special condition of nuids of 1 and uids of NULL signals
1999
   * that all users are to be treated according to the allow member.
2000
   */
2001
  if (user_acl->uids != NULL) {
9✔
2002
    register unsigned int i = 0;
2003

2004
    for (i = 0; i < user_acl->nuids; i++) {
2✔
2005
      if ((user_acl->uids)[i] == cl_uid) {
2✔
2006
        res = TRUE;
1✔
2007
      }
2008
    }
2009

2010
  } else if (user_acl->nuids == 1) {
7✔
2011
    res = TRUE;
3✔
2012
  }
2013

2014
  if (!user_acl->allow) {
9✔
2015
    res = !res;
2✔
2016
  }
2017

2018
  return res;
2019
}
2020

2021
/* Returns TRUE for allowed, FALSE for denied. */
2022
int pr_ctrls_check_acl(const pr_ctrls_t *ctrl,
9✔
2023
    const ctrls_acttab_t *acttab, const char *action) {
2024
  register unsigned int i = 0;
9✔
2025

2026
  if (ctrl == NULL ||
17✔
2027
      ctrl->ctrls_cl == NULL ||
8✔
2028
      acttab == NULL ||
14✔
2029
      action == NULL) {
7✔
2030
    errno = EINVAL;
4✔
2031
    return -1;
4✔
2032
  }
2033

2034
  for (i = 0; acttab[i].act_action; i++) {
3✔
2035
    if (strcmp(acttab[i].act_action, action) == 0) {
5✔
2036
      int user_check = FALSE, group_check = FALSE;
4✔
2037

2038
      if (acttab[i].act_acl != NULL) {
4✔
2039
        user_check = pr_ctrls_check_user_acl(ctrl->ctrls_cl->cl_uid,
3✔
2040
          &(acttab[i].act_acl->acl_users));
3✔
2041
        group_check = pr_ctrls_check_group_acl(ctrl->ctrls_cl->cl_gid,
3✔
2042
          &(acttab[i].act_acl->acl_groups));
3✔
2043
      }
2044

2045
      if (user_check != TRUE &&
8✔
2046
          group_check != TRUE) {
4✔
2047
        return FALSE;
2048
      }
2049
    }
2050
  }
2051

2052
  return TRUE;
2053
}
2054

2055
int pr_ctrls_init_acl(ctrls_acl_t *acl) {
7✔
2056
  if (acl == NULL) {
7✔
2057
    errno = EINVAL;
1✔
2058
    return -1;
1✔
2059
  }
2060

2061
  memset(acl, 0, sizeof(ctrls_acl_t));
6✔
2062
  acl->acl_users.allow = acl->acl_groups.allow = TRUE;
6✔
2063

2064
  return 0;
6✔
2065
}
2066

2067
static char *ctrls_argsep(char **arg) {
46✔
2068
  char *ret = NULL, *dst = NULL;
46✔
2069
  char quote_mode = 0;
46✔
2070

2071
  if (arg == NULL ||
92✔
2072
      !*arg ||
92✔
2073
      !**arg) {
46✔
2074
    errno = EINVAL;
12✔
2075
    return NULL;
12✔
2076
  }
2077

2078
  while (**arg &&
34✔
2079
         PR_ISSPACE(**arg)) {
34✔
2080
    (*arg)++;
×
2081
  }
2082

2083
  if (!**arg) {
34✔
2084
    return NULL;
2085
  }
2086

2087
  ret = dst = *arg;
34✔
2088

2089
  if (**arg == '\"') {
34✔
2090
    quote_mode++;
×
2091
    (*arg)++;
×
2092
  }
2093

2094
  while (**arg && **arg != ',' &&
240✔
2095
      (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
103✔
2096

2097
    if (**arg == '\\' && quote_mode) {
103✔
2098
      /* escaped char */
2099
      if (*((*arg) + 1)) {
×
2100
        *dst = *(++(*arg));
×
2101
      }
2102
    }
2103

2104
    *dst++ = **arg;
103✔
2105
    ++(*arg);
103✔
2106
  }
2107

2108
  if (**arg) {
34✔
2109
    (*arg)++;
22✔
2110
  }
2111

2112
  *dst = '\0';
34✔
2113
  return ret;
34✔
2114
}
2115

2116
char **pr_ctrls_parse_acl(pool *acl_pool, const char *acl_text) {
14✔
2117
  char *name = NULL, *acl_text_dup = NULL, **acl_list = NULL;
14✔
2118
  array_header *acl_arr = NULL;
14✔
2119
  pool *tmp_pool = NULL;
14✔
2120

2121
  if (acl_pool == NULL ||
28✔
2122
      acl_text == NULL) {
14✔
2123
    errno = EINVAL;
2✔
2124
    return NULL;
2✔
2125
  }
2126

2127
  tmp_pool = make_sub_pool(acl_pool);
12✔
2128
  acl_text_dup = pstrdup(tmp_pool, acl_text);
12✔
2129

2130
  /* Allocate an array */
2131
  acl_arr = make_array(acl_pool, 0, sizeof(char **));
12✔
2132

2133
  /* Add each name to the array */
2134
  while ((name = ctrls_argsep(&acl_text_dup)) != NULL) {
58✔
2135
    char *text;
2136

2137
    text = pstrdup(acl_pool, name);
34✔
2138

2139
    /* Push the name into the ACL array */
2140
    *((char **) push_array(acl_arr)) = text;
34✔
2141
  }
2142

2143
  /* Terminate the temp array with a NULL, as is proper. */
2144
  *((char **) push_array(acl_arr)) = NULL;
12✔
2145

2146
  acl_list = (char **) acl_arr->elts;
12✔
2147
  destroy_pool(tmp_pool);
12✔
2148

2149
  /* return the array of names */
2150
  return acl_list;
12✔
2151
}
2152

2153
int pr_ctrls_set_group_acl(pool *group_acl_pool, ctrls_group_acl_t *group_acl,
8✔
2154
    const char *allow, char *grouplist) {
2155
  char *group = NULL, **groups = NULL;
8✔
2156
  array_header *gid_list = NULL;
8✔
2157
  gid_t gid = 0;
8✔
2158
  pool *tmp_pool = NULL;
8✔
2159

2160
  if (group_acl_pool == NULL ||
16✔
2161
      group_acl == NULL ||
8✔
2162
      allow == NULL ||
12✔
2163
      grouplist == NULL) {
6✔
2164
    errno = EINVAL;
4✔
2165
    return -1;
4✔
2166
  }
2167

2168
  tmp_pool = make_sub_pool(group_acl_pool);
4✔
2169

2170
  if (strcasecmp(allow, "allow") == 0) {
4✔
2171
    group_acl->allow = TRUE;
3✔
2172

2173
  } else {
2174
    group_acl->allow = FALSE;
1✔
2175
  }
2176

2177
  /* Parse the given expression into an array, then retrieve the GID
2178
   * for each given name.
2179
   */
2180
  groups = pr_ctrls_parse_acl(group_acl_pool, grouplist);
4✔
2181

2182
  /* Allocate an array of gid_t's */
2183
  gid_list = make_array(group_acl_pool, 0, sizeof(gid_t));
4✔
2184

2185
  for (group = *groups; group != NULL; group = *++groups) {
15✔
2186

2187
    /* Handle a group name of "*" differently. */
2188
    if (strcmp(group, "*") == 0) {
12✔
2189
      group_acl->ngids = 1;
1✔
2190
      group_acl->gids = NULL;
1✔
2191
      destroy_pool(tmp_pool);
1✔
2192
      return 0;
1✔
2193
    }
2194

2195
    gid = pr_auth_name2gid(tmp_pool, group);
11✔
2196
    if (gid == (gid_t) -1) {
11✔
2197
      continue;
11✔
2198
    }
2199

2200
    *((gid_t *) push_array(gid_list)) = gid;
×
2201
  }
2202

2203
  group_acl->ngids = gid_list->nelts;
3✔
2204
  group_acl->gids = (gid_t *) gid_list->elts;
3✔
2205

2206
  destroy_pool(tmp_pool);
3✔
2207
  return 0;
3✔
2208
}
2209

2210
int pr_ctrls_set_user_acl(pool *user_acl_pool, ctrls_user_acl_t *user_acl,
10✔
2211
    const char *allow, char *userlist) {
2212
  char *user = NULL, **users = NULL;
10✔
2213
  array_header *uid_list = NULL;
10✔
2214
  uid_t uid = 0;
10✔
2215
  pool *tmp_pool = NULL;
10✔
2216

2217
  /* Sanity checks */
2218
  if (user_acl_pool == NULL ||
20✔
2219
      user_acl == NULL ||
10✔
2220
      allow == NULL ||
16✔
2221
      userlist == NULL) {
8✔
2222
    errno = EINVAL;
4✔
2223
    return -1;
4✔
2224
  }
2225

2226
  tmp_pool = make_sub_pool(user_acl_pool);
6✔
2227

2228
  if (strcasecmp(allow, "allow") == 0) {
6✔
2229
    user_acl->allow = TRUE;
5✔
2230

2231
  } else {
2232
    user_acl->allow = FALSE;
1✔
2233
  }
2234

2235
  /* Parse the given expression into an array, then retrieve the UID
2236
   * for each given name.
2237
   */
2238
  users = pr_ctrls_parse_acl(user_acl_pool, userlist);
6✔
2239

2240
  /* Allocate an array of uid_t's */
2241
  uid_list = make_array(user_acl_pool, 0, sizeof(uid_t));
6✔
2242

2243
  for (user = *users; user != NULL; user = *++users) {
23✔
2244

2245
    /* Handle a user name of "*" differently. */
2246
    if (strcmp(user, "*") == 0) {
18✔
2247
      user_acl->nuids = 1;
1✔
2248
      user_acl->uids = NULL;
1✔
2249
      destroy_pool(tmp_pool);
1✔
2250
      return 0;
1✔
2251
    }
2252

2253
    uid = pr_auth_name2uid(tmp_pool, user);
17✔
2254
    if (uid == (uid_t) -1) {
17✔
2255
      continue;
17✔
2256
    }
2257

2258
    *((uid_t *) push_array(uid_list)) = uid;
×
2259
  }
2260

2261
  user_acl->nuids = uid_list->nelts;
5✔
2262
  user_acl->uids = (uid_t *) uid_list->elts;
5✔
2263

2264
  destroy_pool(tmp_pool);
5✔
2265
  return 0;
5✔
2266
}
2267

2268
int pr_ctrls_set_module_acls2(ctrls_acttab_t *acttab, pool *acl_pool,
23✔
2269
    char **actions, const char *allow, const char *type, char *list,
2270
    const char **bad_action) {
2271
  register unsigned int i = 0;
23✔
2272
  int all_actions = FALSE;
23✔
2273

2274
  if (acttab == NULL ||
46✔
2275
      acl_pool == NULL ||
23✔
2276
      actions == NULL ||
38✔
2277
      type == NULL ||
32✔
2278
      bad_action == NULL) {
2279
    errno = EINVAL;
12✔
2280
    return -1;
12✔
2281
  }
2282

2283
  if (strcasecmp(type, "user") != 0 &&
16✔
2284
      strcasecmp(type, "group") != 0) {
5✔
2285
    errno = EINVAL;
3✔
2286
    return -1;
3✔
2287
  }
2288

2289
  /* First, sanity check the given list of actions against the actions
2290
   * in the given table.
2291
   */
2292
  for (i = 0; actions[i]; i++) {
6✔
2293
    register unsigned int j = 0;
8✔
2294
    int valid_action = FALSE;
8✔
2295

2296
    if (strcasecmp(actions[i], "all") == 0) {
8✔
2297
      continue;
2✔
2298
    }
2299

2300
    for (j = 0; acttab[j].act_action; j++) {
2✔
2301
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
6✔
2302
        valid_action = TRUE;
2303
        break;
2304
      }
2305
    }
2306

2307
    if (valid_action == FALSE) {
6✔
2308
      *bad_action = actions[i];
2✔
2309
      errno = EPERM;
2✔
2310
      return -1;
2✔
2311
    }
2312
  }
2313

2314
  for (i = 0; actions[i]; i++) {
6✔
2315
    register unsigned int j = 0;
6✔
2316

2317
    if (all_actions == FALSE &&
12✔
2318
        strcasecmp(actions[i], "all") == 0) {
6✔
2319
      all_actions = TRUE;
2✔
2320
    }
2321

2322
    for (j = 0; acttab[j].act_action; j++) {
12✔
2323
      int res = 0;
6✔
2324

2325
      if (all_actions == TRUE ||
10✔
2326
          strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2327

2328
        /* Use the type parameter to determine whether the list is of users or
2329
         * of groups.
2330
         */
2331
        if (strcasecmp(type, "user") == 0) {
6✔
2332
          res = pr_ctrls_set_user_acl(acl_pool,
4✔
2333
            &(acttab[j].act_acl->acl_users), allow, list);
4✔
2334

2335
        } else if (strcasecmp(type, "group") == 0) {
2✔
2336
          res = pr_ctrls_set_group_acl(acl_pool,
2✔
2337
            &(acttab[j].act_acl->acl_groups), allow, list);
2✔
2338
        }
2339

2340
        if (res < 0) {
6✔
2341
          *bad_action = actions[i];
×
2342
          return -1;
×
2343
        }
2344
      }
2345
    }
2346
  }
2347

2348
  return 0;
2349
}
2350

2351
char *pr_ctrls_set_module_acls(ctrls_acttab_t *acttab, pool *acl_pool,
11✔
2352
    char **actions, const char *allow, const char *type, char *list) {
2353
  int res;
2354
  char *bad_action = NULL;
11✔
2355

2356
  res = pr_ctrls_set_module_acls2(acttab, acl_pool, actions, allow, type, list,
11✔
2357
    (const char **) &bad_action);
2358
  if (res < 0) {
11✔
2359
    return bad_action;
8✔
2360
  }
2361

2362
  return 0;
2363
}
2364

2365
int pr_ctrls_unregister_module_actions2(ctrls_acttab_t *acttab,
11✔
2366
    char **actions, module *mod, const char **bad_action) {
2367
  register unsigned int i = 0;
11✔
2368

2369
  if (acttab == NULL ||
22✔
2370
      actions == NULL ||
11✔
2371
      mod == NULL ||
14✔
2372
      bad_action == NULL) {
7✔
2373
    errno = EINVAL;
7✔
2374
    return -1;
7✔
2375
  }
2376

2377
  /* First, sanity check the given actions against the actions supported by
2378
   * this module.
2379
   */
2380
  for (i = 0; actions[i]; i++) {
2✔
2381
    register unsigned int j = 0;
2382
    int valid_action = FALSE;
2383

2384
    for (j = 0; acttab[j].act_action; j++) {
2✔
2385
      if (strcmp(actions[i], acttab[j].act_action) == 0) {
4✔
2386
        valid_action = TRUE;
2387
        break;
2388
      }
2389
    }
2390

2391
    if (valid_action == FALSE) {
4✔
2392
      *bad_action = actions[i];
2✔
2393
      errno = EPERM;
2✔
2394
      return -1;
2✔
2395
    }
2396
  }
2397

2398
  /* Next, iterate through both lists again, looking for actions of the
2399
   * module _not_ in the given list.
2400
   */
2401
  for (i = 0; acttab[i].act_action; i++) {
2✔
2402
    register unsigned int j = 0;
2403
    int have_action = FALSE;
2404

2405
    for (j = 0; actions[j]; j++) {
×
2406
      if (strcmp(acttab[i].act_action, actions[j]) == 0) {
2✔
2407
        have_action = TRUE;
2408
        break;
2409
      }
2410
    }
2411

2412
    if (have_action == TRUE) {
2✔
2413
      pr_trace_msg(trace_channel, 4, "mod_%s.c: removing '%s' control",
2✔
2414
        mod->name, acttab[i].act_action);
2415
      pr_ctrls_unregister(mod, acttab[i].act_action);
2✔
2416
      destroy_pool(acttab[i].act_acl->acl_pool);
2✔
2417
    }
2418
  }
2419

2420
  return 0;
2421
}
2422

2423
char *pr_ctrls_unregister_module_actions(ctrls_acttab_t *acttab,
5✔
2424
    char **actions, module *mod) {
2425
  int res;
2426
  char *bad_action = NULL;
5✔
2427

2428
  res = pr_ctrls_unregister_module_actions2(acttab, actions, mod,
5✔
2429
    (const char **) &bad_action);
2430
  if (res < 0) {
5✔
2431
    return bad_action;
4✔
2432
  }
2433

2434
  return 0;
2435
}
2436

2437
int pr_ctrls_set_logfd(int fd) {
4✔
2438

2439
  /* Close any existing log fd. */
2440
  if (ctrls_logfd >= 0) {
4✔
2441
    (void) close(ctrls_logfd);
1✔
2442
  }
2443

2444
  ctrls_logfd = fd;
4✔
2445
  return 0;
4✔
2446
}
2447

2448
int pr_ctrls_log(const char *module_version, const char *fmt, ...) {
4✔
2449
  va_list msg;
2450
  int res;
2451

2452
  if (ctrls_logfd < 0) {
4✔
2453
    return 0;
2454
  }
2455

2456
  if (fmt == NULL) {
3✔
2457
    return 0;
2458
  }
2459

2460
  va_start(msg, fmt);
1✔
2461
  res = pr_log_vwritefile(ctrls_logfd, module_version, fmt, msg);
1✔
2462
  va_end(msg);
1✔
2463

2464
  return res;
1✔
2465
}
2466

2467
static void ctrls_cleanup_cb(void *user_data) {
36✔
2468
  ctrls_pool = NULL;
36✔
2469
  ctrls_action_list = NULL;
36✔
2470
  ctrls_active_list = NULL;
36✔
2471
  ctrls_free_list = NULL;
36✔
2472

2473
  action_lookup_next = NULL;
36✔
2474
  action_lookup_action = NULL;
36✔
2475
  action_lookup_module = NULL;
36✔
2476
}
36✔
2477

2478
/* Initialize the Controls API. */
2479
int init_ctrls2(const char *socket_path) {
36✔
2480
  struct stat st;
2481
  int fd, xerrno;
2482
  struct sockaddr_un sockun;
2483
  size_t socklen;
2484

2485
  if (ctrls_pool != NULL) {
36✔
2486
    destroy_pool(ctrls_pool);
×
2487
  }
2488

2489
  ctrls_pool = make_sub_pool(permanent_pool);
36✔
2490
  pr_pool_tag(ctrls_pool, "Controls Pool");
36✔
2491
  register_cleanup2(ctrls_pool, NULL, ctrls_cleanup_cb);
36✔
2492

2493
  /* Make sure all of the lists are zero'd out. */
2494
  ctrls_action_list = NULL;
36✔
2495
  ctrls_active_list = NULL;
36✔
2496
  ctrls_free_list = NULL;
36✔
2497

2498
   /* And that the lookup indices are (re)set as well... */
2499
  action_lookup_next = NULL;
36✔
2500
  action_lookup_action = NULL;
36✔
2501
  action_lookup_module = NULL;
36✔
2502

2503
  /* Run-time check to find out whether this platform identifies a
2504
   * Unix domain socket file descriptor via the S_ISFIFO macro, or
2505
   * the S_ISSOCK macro.
2506
   */
2507

2508
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
36✔
2509
  xerrno = errno;
36✔
2510

2511
  if (fd < 0) {
36✔
2512
    pr_log_debug(DEBUG10, "unable to create Unix domain socket: %s",
×
2513
      strerror(xerrno));
2514

2515
    errno = xerrno;
×
2516
    return -1;
×
2517
  }
2518

2519
  memset(&sockun, 0, sizeof(sockun));
36✔
2520
  sockun.sun_family = AF_UNIX;
36✔
2521
  sstrncpy(sockun.sun_path, socket_path, sizeof(sockun.sun_path));
36✔
2522
  socklen = sizeof(struct sockaddr_un);
36✔
2523

2524
  if (bind(fd, (struct sockaddr *) &sockun, socklen) < 0) {
36✔
2525
    xerrno = errno;
×
2526

2527
    pr_log_debug(DEBUG10, "unable to bind to Unix domain socket at '%s': %s",
×
2528
      socket_path, strerror(xerrno));
2529
    (void) close(fd);
×
2530
    (void) unlink(socket_path);
×
2531

2532
    errno = xerrno;
×
2533
    return -1;
×
2534
  }
2535

2536
  if (fstat(fd, &st) < 0) {
36✔
2537
    xerrno = errno;
×
2538

2539
    pr_log_debug(DEBUG10, "unable to stat Unix domain socket at '%s': %s",
×
2540
      socket_path, strerror(xerrno));
2541
    (void) close(fd);
×
2542
    (void) unlink(socket_path);
×
2543

2544
    errno = xerrno;
×
2545
    return -1;
×
2546
  }
2547

2548
#if defined(S_ISFIFO)
2549
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISFIFO");
36✔
2550
  if (S_ISFIFO(st.st_mode)) {
36✔
2551
    ctrls_use_isfifo = TRUE;
×
2552
  }
2553
#else
2554
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISFIFO: "
2555
    "macro undefined");
2556
#endif /* S_ISFIFO */
2557

2558
#if defined(S_ISSOCK)
2559
  pr_trace_msg(trace_channel, 9, "testing Unix domain socket using S_ISSOCK");
36✔
2560
  if (S_ISSOCK(st.st_mode)) {
36✔
2561
    ctrls_use_isfifo = FALSE;
36✔
2562
  }
2563
#else
2564
  pr_log_debug(DEBUG10, "cannot test Unix domain socket using S_ISSOCK: "
2565
    "macro undefined");
2566
#endif /* S_ISSOCK */
2567

2568
  pr_trace_msg(trace_channel, 9,
36✔
2569
    "using %s macro for Unix domain socket detection",
2570
    ctrls_use_isfifo ? "S_ISFIFO" : "S_ISSOCK");
36✔
2571

2572
  (void) close(fd);
36✔
2573
  (void) unlink(socket_path);
36✔
2574
  return 0;
36✔
2575
}
2576

2577
void init_ctrls(void) {
×
2578
  const char *socket_path = PR_RUN_DIR "/test.sock";
×
2579

2580
  (void) init_ctrls2(socket_path);
×
2581
}
×
2582
#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