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

proftpd / proftpd / 26694586415

30 May 2026 08:48PM UTC coverage: 92.637% (-0.4%) from 93.024%
26694586415

push

github

web-flow
Implement some sanity checks on the length of extended attributes (xattrs) that can be requested via custom SFTP extensions.

Bankde Eakasit posited that this could be another vector to triggering excessive memory allocations.

47319 of 51080 relevant lines covered (92.64%)

235.33 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