1
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019 Teddy Hogeborn
6
* Copyright © 2019 Björn Påhlsson
8
* This file is part of Mandos.
10
* Mandos is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* Mandos is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
* Contact the authors at <mandos@recompile.se>.
27
#include <inttypes.h> /* uintmax_t, PRIuMAX, PRIdMAX,
28
intmax_t, uint32_t, SCNx32,
30
#include <stddef.h> /* size_t */
31
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
33
#include <stdbool.h> /* bool, true, false */
34
#include <signal.h> /* struct sigaction, sigset_t,
35
sigemptyset(), sigaddset(),
36
SIGCHLD, pthread_sigmask(),
37
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
SA_NOCLDSTOP, sigfillset(), kill(),
39
SIGTERM, sigdelset(), SIGKILL,
40
NSIG, sigismember(), SA_ONSTACK,
41
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
44
malloc(), free(), strtoumax(),
45
realloc(), setenv(), calloc(),
46
mkdtemp(), mkostemp() */
47
#include <iso646.h> /* not, or, and, xor */
48
#include <error.h> /* error() */
49
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
#include <errno.h> /* errno, error_t, EACCES,
51
ENAMETOOLONG, ENOENT, ENOTDIR,
52
ENOMEM, EEXIST, ECHILD, EPERM,
53
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <stdint.h> /* SIZE_MAX */
77
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
78
fork(), _exit(), dup2(),
79
STDOUT_FILENO, setresgid(),
80
setresuid(), execv(), ssize_t,
81
read(), dup3(), getuid(), dup(),
82
STDERR_FILENO, pause(), write(),
83
rmdir(), unlink(), getpid() */
84
#include <sys/mman.h> /* munlock(), mlock() */
85
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
86
F_GETFD, F_GETFL, FD_CLOEXEC,
87
open(), O_WRONLY, O_NOCTTY,
88
O_RDONLY, O_NOFOLLOW */
89
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
91
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
92
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
93
IN_CLOEXEC, inotify_add_watch(),
94
IN_CLOSE_WRITE, IN_MOVED_TO,
95
IN_MOVED_FROM, IN_DELETE,
96
IN_EXCL_UNLINK, IN_ONLYDIR,
97
struct inotify_event */
98
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
99
#include <stdio.h> /* asprintf(), FILE, stderr, fopen(),
100
fclose(), getline(), sscanf(),
101
feof(), ferror(), rename(),
102
fdopen(), fprintf(), fscanf() */
103
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
GError, g_key_file_load_from_file(),
105
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
106
g_key_file_get_string(), guint64,
107
g_key_file_get_uint64(),
108
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
109
g_assert_true(), g_assert_nonnull(),
110
g_assert_null(), g_assert_false(),
111
g_assert_cmpint(), g_assert_cmpuint(),
112
g_test_skip(), g_assert_cmpstr(),
113
g_test_init(), g_test_add(), g_test_run(),
114
GOptionContext, g_option_context_new(),
115
g_option_context_set_help_enabled(), FALSE,
116
g_option_context_set_ignore_unknown_options(),
117
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
118
g_option_context_add_main_entries(),
119
g_option_context_parse(),
120
g_option_context_free(), g_error() */
121
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
122
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
123
SOCK_DGRAM, SOCK_NONBLOCK,
124
SOCK_CLOEXEC, connect(),
125
struct sockaddr, socklen_t,
126
shutdown(), SHUT_RD, send(),
127
MSG_NOSIGNAL, bind(), recv(),
129
#include <glob.h> /* globfree(), glob_t, glob(),
130
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
131
GLOB_ABORTED, GLOB_NOMATCH,
134
/* End of includes */
136
/* Start of declarations of private types and functions */
138
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
139
typedef uintmax_t mono_microsecs;
141
/* "task_queue" - A queue of tasks to be run */
143
struct task_struct *tasks; /* Tasks in this queue */
144
size_t length; /* Number of tasks */
145
/* Memory allocated for "tasks", in bytes */
147
/* Time when this queue should be run, at the latest */
148
mono_microsecs next_run;
149
} __attribute__((designated_init)) task_queue;
151
/* "task_func" - A function type for task functions
153
I.e. functions for the code which runs when a task is run, all have
155
typedef void (task_func) (const struct task_struct,
157
__attribute__((nonnull));
159
/* "buffer" - A data buffer for a growing array of bytes
161
Used for the "password" variable */
166
} __attribute__((designated_init)) buffer;
168
/* "string_set" - A set type which can contain strings
170
Used by the "cancelled_filenames" variable */
172
char *argz; /* Do not access these except in */
173
size_t argz_len; /* the string_set_* functions */
174
} __attribute__((designated_init)) string_set;
176
/* "task_context" - local variables for tasks
178
This data structure distinguishes between different tasks which are
179
using the same function. This data structure is passed to every
180
task function when each task is run.
182
Note that not every task uses every struct member. */
183
typedef struct task_struct {
184
task_func *const func; /* The function run by this task */
185
char *const question_filename; /* The question file */
186
const pid_t pid; /* Mandos client process ID */
187
const int epoll_fd; /* The epoll set file descriptor */
188
bool *const quit_now; /* Set to true on fatal errors */
189
const int fd; /* General purpose file descriptor */
190
bool *const mandos_client_exited; /* Set true when client exits */
191
buffer *const password; /* As read from client process */
192
bool *const password_is_read; /* "password" is done growing */
193
char *filename; /* General purpose file name */
194
/* A set of strings of all the file names of questions which have
195
been cancelled for any reason; tasks pertaining to these question
196
files should not be run */
197
string_set *const cancelled_filenames;
198
const mono_microsecs notafter; /* "NotAfter" from question file */
199
/* Updated before each queue run; is compared with queue.next_run */
200
const mono_microsecs *const current_time;
201
} __attribute__((designated_init)) task_context;
203
/* Declare all our functions here so we can define them in any order
204
below. Note: test functions are *not* declared here, they are
205
declared in the test section. */
206
__attribute__((warn_unused_result))
207
static bool should_only_run_tests(int *, char **[]);
208
__attribute__((warn_unused_result, cold))
209
static bool run_tests(int, char *[]);
210
static void handle_sigchld(__attribute__((unused)) int sig){}
211
__attribute__((warn_unused_result, malloc))
212
task_queue *create_queue(void);
213
__attribute__((nonnull, warn_unused_result))
214
bool add_to_queue(task_queue *const, const task_context);
215
__attribute__((nonnull))
216
void cleanup_task(const task_context *const);
217
__attribute__((nonnull))
218
void cleanup_queue(task_queue *const *const);
219
__attribute__((pure, nonnull, warn_unused_result))
220
bool queue_has_question(const task_queue *const);
221
__attribute__((nonnull))
222
void cleanup_close(const int *const);
223
__attribute__((nonnull))
224
void cleanup_string(char *const *const);
225
__attribute__((nonnull))
226
void cleanup_buffer(buffer *const);
227
__attribute__((pure, nonnull, warn_unused_result))
228
bool string_set_contains(const string_set, const char *const);
229
__attribute__((nonnull, warn_unused_result))
230
bool string_set_add(string_set *const, const char *const);
231
__attribute__((nonnull))
232
void string_set_clear(string_set *);
233
void string_set_swap(string_set *const, string_set *const);
234
__attribute__((nonnull, warn_unused_result))
235
bool start_mandos_client(task_queue *const, const int, bool *const,
236
bool *const, buffer *const, bool *const,
237
const struct sigaction *const,
238
const sigset_t, const char *const,
239
const uid_t, const gid_t,
240
const char *const *const);
241
__attribute__((nonnull))
242
task_func wait_for_mandos_client_exit;
243
__attribute__((nonnull))
244
task_func read_mandos_client_output;
245
__attribute__((warn_unused_result))
246
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
247
buffer *const, const char *const,
248
string_set *, const mono_microsecs *const,
249
bool *const, bool *const);
250
__attribute__((nonnull))
251
task_func read_inotify_event;
252
__attribute__((nonnull))
253
task_func open_and_parse_question;
254
__attribute__((nonnull))
255
task_func cancel_old_question;
256
__attribute__((nonnull))
257
task_func connect_question_socket;
258
__attribute__((nonnull))
259
task_func send_password_to_socket;
260
__attribute__((warn_unused_result))
261
bool add_existing_questions(task_queue *const, const int,
262
buffer *const, string_set *,
263
const mono_microsecs *const,
264
bool *const, bool *const,
266
__attribute__((nonnull, warn_unused_result))
267
bool wait_for_event(const int, const mono_microsecs,
268
const mono_microsecs);
269
bool run_queue(task_queue **const, string_set *const, bool *const);
270
bool clear_all_fds_from_epoll_set(const int);
271
mono_microsecs get_current_time(void);
272
__attribute__((nonnull, warn_unused_result))
273
bool setup_signal_handler(struct sigaction *const);
274
__attribute__((nonnull))
275
bool restore_signal_handler(const struct sigaction *const);
276
__attribute__((nonnull, warn_unused_result))
277
bool block_sigchld(sigset_t *const);
278
__attribute__((nonnull))
279
bool restore_sigmask(const sigset_t *const);
280
__attribute__((nonnull))
281
bool parse_arguments(int, char *[], const bool, char **, char **,
282
uid_t *const , gid_t *const, char **, size_t *);
284
/* End of declarations of private types and functions */
286
/* Start of "main" section; this section LACKS TESTS!
288
Code here should be as simple as possible. */
290
/* These are required to be global by Argp */
291
const char *argp_program_version = "password-agent " VERSION;
292
const char *argp_program_bug_address = "<mandos@recompile.se>";
294
int main(int argc, char *argv[]){
296
/* If the --test option is passed, skip all normal operations and
297
instead only run the run_tests() function, which also does all
298
its own option parsing, so we don't have to do anything here. */
299
if(should_only_run_tests(&argc, &argv)){
300
if(run_tests(argc, argv)){
301
return EXIT_SUCCESS; /* All tests successful */
303
return EXIT_FAILURE; /* Some test(s) failed */
306
__attribute__((cleanup(cleanup_string)))
307
char *agent_directory = NULL;
309
__attribute__((cleanup(cleanup_string)))
310
char *helper_directory = NULL;
315
__attribute__((cleanup(cleanup_string)))
316
char *mandos_argz = NULL;
317
size_t mandos_argz_length = 0;
319
if(not parse_arguments(argc, argv, true, &agent_directory,
320
&helper_directory, &user, &group,
321
&mandos_argz, &mandos_argz_length)){
322
/* This should never happen, since "true" is passed as the third
323
argument to parse_arguments() above, which should make
324
argp_parse() call exit() if any parsing error occurs. */
325
error(EX_USAGE, errno, "Failed to parse arguments");
328
const char default_agent_directory[] = "/run/systemd/ask-password";
329
const char default_helper_directory[]
330
= "/lib/mandos/plugin-helpers";
331
const char *const default_argv[]
332
= {"/lib/mandos/plugins.d/mandos-client", NULL };
334
/* Set variables to default values if unset */
335
if(agent_directory == NULL){
336
agent_directory = strdup(default_agent_directory);
337
if(agent_directory == NULL){
338
error(EX_OSERR, errno, "Failed strdup()");
341
if(helper_directory == NULL){
342
helper_directory = strdup(default_helper_directory);
343
if(helper_directory == NULL){
344
error(EX_OSERR, errno, "Failed strdup()");
348
user = 65534; /* nobody */
351
group = 65534; /* nogroup */
353
/* If parse_opt did not create an argz vector, create one with
355
if(mandos_argz == NULL){
357
#pragma GCC diagnostic push
358
/* argz_create() takes a non-const argv for some unknown reason -
359
argz_create() isn't modifying the strings, just copying them.
360
Therefore, this cast to non-const should be safe. */
361
#pragma GCC diagnostic ignored "-Wcast-qual"
363
errno = argz_create((char *const *)default_argv, &mandos_argz,
364
&mandos_argz_length);
366
#pragma GCC diagnostic pop
369
error(EX_OSERR, errno, "Failed argz_create()");
372
/* Use argz vector to create a normal argv, usable by execv() */
374
char **mandos_argv = malloc((argz_count(mandos_argz,
376
+ 1) * sizeof(char *));
377
if(mandos_argv == NULL){
378
error_t saved_errno = errno;
380
error(EX_OSERR, saved_errno, "Failed malloc()");
382
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
384
sigset_t orig_sigmask;
385
if(not block_sigchld(&orig_sigmask)){
389
struct sigaction old_sigchld_action;
390
if(not setup_signal_handler(&old_sigchld_action)){
394
mono_microsecs current_time = 0;
396
bool mandos_client_exited = false;
397
bool quit_now = false;
398
__attribute__((cleanup(cleanup_close)))
399
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
401
error(EX_OSERR, errno, "Failed to create epoll set fd");
403
__attribute__((cleanup(cleanup_queue)))
404
task_queue *queue = create_queue();
406
error(EX_OSERR, errno, "Failed to create task queue");
409
__attribute__((cleanup(cleanup_buffer)))
410
buffer password = {};
411
bool password_is_read = false;
413
__attribute__((cleanup(string_set_clear)))
414
string_set cancelled_filenames = {};
416
/* Add tasks to queue */
417
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
418
&quit_now, &password, &password_is_read,
419
&old_sigchld_action, orig_sigmask,
420
helper_directory, user, group,
421
(const char *const *)mandos_argv)){
422
return EX_OSERR; /* Error has already been printed */
424
/* These variables were only for start_mandos_client() and are not
429
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
430
agent_directory, &cancelled_filenames,
431
¤t_time, &mandos_client_exited,
433
switch(errno){ /* Error has already been printed */
443
if(not add_existing_questions(queue, epoll_fd, &password,
444
&cancelled_filenames, ¤t_time,
445
&mandos_client_exited,
446
&password_is_read, agent_directory)){
447
return EXIT_FAILURE; /* Error has already been printed */
452
current_time = get_current_time();
453
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
454
const error_t saved_errno = errno;
455
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
459
current_time = get_current_time();
460
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
461
const error_t saved_errno = errno;
462
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
465
/* When no tasks about questions are left in the queue, break out
466
of the loop (and implicitly exit the program) */
467
} while(queue_has_question(queue));
469
restore_signal_handler(&old_sigchld_action);
470
restore_sigmask(&orig_sigmask);
475
__attribute__((warn_unused_result))
476
mono_microsecs get_current_time(void){
477
struct timespec currtime;
478
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
479
error(0, errno, "Failed to get current time");
482
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
483
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
486
/* End of "main" section */
488
/* Start of regular code section; ALL this code has tests */
490
__attribute__((nonnull))
491
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
492
char **agent_directory, char **helper_directory,
493
uid_t *const user, gid_t *const group,
494
char **mandos_argz, size_t *mandos_argz_length){
496
const struct argp_option options[] = {
497
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
498
.doc="Systemd password agent directory" },
499
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
500
.doc="Mandos Client password helper directory" },
501
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
502
.flags=OPTION_HIDDEN | OPTION_ALIAS },
503
{ .name="user", .key='u', .arg="USERID",
504
.doc="User ID the Mandos Client will use as its unprivileged"
506
{ .name="userid", .key=130, /* From plugin--runner */
507
.flags=OPTION_HIDDEN | OPTION_ALIAS },
508
{ .name="group", .key='g', .arg="GROUPID",
509
.doc="Group ID the Mandos Client will use as its unprivileged"
511
{ .name="groupid", .key=131, /* From plugin--runner */
512
.flags=OPTION_HIDDEN | OPTION_ALIAS },
513
{ .name="test", .key=255, /* See should_only_run_tests() */
514
.doc="Skip normal operation, and only run self-tests. See"
515
" --test --help.", .group=10, },
519
__attribute__((nonnull(3)))
520
error_t parse_opt(int key, char *arg, struct argp_state *state){
523
case 'd': /* --agent-directory */
524
*agent_directory = strdup(arg);
526
case 128: /* --helper-directory */
527
case 129: /* --plugin-helper-dir */
528
*helper_directory = strdup(arg);
530
case 'u': /* --user */
531
case 130: /* --userid */
534
uintmax_t tmp_id = 0;
536
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
537
if(errno != 0 or tmp == arg or *tmp != '\0'
538
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
539
return ARGP_ERR_UNKNOWN;
541
*user = (uid_t)tmp_id;
545
case 'g': /* --group */
546
case 131: /* --groupid */
549
uintmax_t tmp_id = 0;
551
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
552
if(errno != 0 or tmp == arg or *tmp != '\0'
553
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
554
return ARGP_ERR_UNKNOWN;
556
*group = (gid_t)tmp_id;
561
/* Copy arguments into argz vector */
562
return argz_create(state->argv + state->next, mandos_argz,
565
return ARGP_ERR_UNKNOWN;
570
const struct argp argp = {
573
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
574
.doc = "Mandos password agent -- runs Mandos client as a"
575
" systemd password agent",
578
errno = argp_parse(&argp, argc, argv,
579
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
584
__attribute__((nonnull, warn_unused_result))
585
bool block_sigchld(sigset_t *const orig_sigmask){
586
sigset_t sigchld_sigmask;
587
if(sigemptyset(&sigchld_sigmask) < 0){
588
error(0, errno, "Failed to empty signal set");
591
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
592
error(0, errno, "Failed to add SIGCHLD to signal set");
595
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
596
error(0, errno, "Failed to block SIGCHLD signal");
602
__attribute__((nonnull, warn_unused_result, const))
603
bool restore_sigmask(const sigset_t *const orig_sigmask){
604
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
605
error(0, errno, "Failed to restore blocked signals");
611
__attribute__((nonnull, warn_unused_result))
612
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
613
struct sigaction sigchld_action = {
614
.sa_handler=handle_sigchld,
615
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
617
/* Set all signals in "sa_mask" struct member; this makes all
618
signals automatically blocked during signal handler */
619
if(sigfillset(&sigchld_action.sa_mask) != 0){
620
error(0, errno, "Failed to do sigfillset()");
623
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
624
error(0, errno, "Failed to set SIGCHLD signal handler");
630
__attribute__((nonnull, warn_unused_result))
631
bool restore_signal_handler(const struct sigaction *const
633
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
634
error(0, errno, "Failed to restore signal handler");
640
__attribute__((warn_unused_result, malloc))
641
task_queue *create_queue(void){
642
task_queue *queue = malloc(sizeof(task_queue));
646
queue->allocated = 0;
652
__attribute__((nonnull, warn_unused_result))
653
bool add_to_queue(task_queue *const queue, const task_context task){
654
if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
656
error(0, ENOMEM, "Failed to allocate %" PRIuMAX
657
" tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
662
if(needed_size > (queue->allocated)){
663
task_context *const new_tasks = realloc(queue->tasks,
665
if(new_tasks == NULL){
666
error(0, errno, "Failed to allocate %" PRIuMAX
667
" bytes for queue->tasks", (uintmax_t)needed_size);
670
queue->tasks = new_tasks;
671
queue->allocated = needed_size;
673
/* Using memcpy here is necessary because doing */
674
/* queue->tasks[queue->length++] = task; */
675
/* would violate const-ness of task members */
676
memcpy(&(queue->tasks[queue->length++]), &task,
677
sizeof(task_context));
681
__attribute__((nonnull))
682
void cleanup_task(const task_context *const task){
683
const error_t saved_errno = errno;
684
/* free and close all task data */
685
free(task->question_filename);
686
if(task->filename != task->question_filename){
687
free(task->filename);
690
kill(task->pid, SIGTERM);
698
__attribute__((nonnull))
699
void free_queue(task_queue *const queue){
704
__attribute__((nonnull))
705
void cleanup_queue(task_queue *const *const queue){
709
for(size_t i = 0; i < (*queue)->length; i++){
710
const task_context *const task = ((*queue)->tasks)+i;
716
__attribute__((pure, nonnull, warn_unused_result))
717
bool queue_has_question(const task_queue *const queue){
718
for(size_t i=0; i < queue->length; i++){
719
if(queue->tasks[i].question_filename != NULL){
726
__attribute__((nonnull))
727
void cleanup_close(const int *const fd){
728
const error_t saved_errno = errno;
733
__attribute__((nonnull))
734
void cleanup_string(char *const *const ptr){
738
__attribute__((nonnull))
739
void cleanup_buffer(buffer *buf){
740
if(buf->allocated > 0){
741
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
742
explicit_bzero(buf->data, buf->allocated);
744
memset(buf->data, '\0', buf->allocated);
747
if(buf->data != NULL){
748
if(munlock(buf->data, buf->allocated) != 0){
749
error(0, errno, "Failed to unlock memory of old buffer");
758
__attribute__((pure, nonnull, warn_unused_result))
759
bool string_set_contains(const string_set set, const char *const str){
760
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
761
s = argz_next(set.argz, set.argz_len, s)){
762
if(strcmp(s, str) == 0){
769
__attribute__((nonnull, warn_unused_result))
770
bool string_set_add(string_set *const set, const char *const str){
771
if(string_set_contains(*set, str)){
774
error_t error = argz_add(&set->argz, &set->argz_len, str);
782
__attribute__((nonnull))
783
void string_set_clear(string_set *set){
789
__attribute__((nonnull))
790
void string_set_swap(string_set *const set1, string_set *const set2){
791
/* Swap contents of two string sets */
793
char *const tmp_argz = set1->argz;
794
set1->argz = set2->argz;
795
set2->argz = tmp_argz;
798
const size_t tmp_argz_len = set1->argz_len;
799
set1->argz_len = set2->argz_len;
800
set2->argz_len = tmp_argz_len;
804
__attribute__((nonnull, warn_unused_result))
805
bool start_mandos_client(task_queue *const queue,
807
bool *const mandos_client_exited,
808
bool *const quit_now, buffer *const password,
809
bool *const password_is_read,
810
const struct sigaction *const
811
old_sigchld_action, const sigset_t sigmask,
812
const char *const helper_directory,
813
const uid_t user, const gid_t group,
814
const char *const *const argv){
816
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
817
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
821
const pid_t pid = fork();
823
if(not restore_signal_handler(old_sigchld_action)){
826
if(not restore_sigmask(&sigmask)){
829
if(close(pipefds[0]) != 0){
830
error(0, errno, "Failed to close() parent pipe fd");
833
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
834
error(0, errno, "Failed to dup2() pipe fd to stdout");
837
if(close(pipefds[1]) != 0){
838
error(0, errno, "Failed to close() old child pipe fd");
841
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
842
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
843
" \"%s\", 1)", helper_directory);
846
if(group != 0 and setresgid(group, 0, 0) == -1){
847
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
848
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
851
if(user != 0 and setresuid(user, 0, 0) == -1){
852
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
853
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
857
#pragma GCC diagnostic push
858
/* For historical reasons, the "argv" argument to execv() is not
859
const, but it is safe to override this. */
860
#pragma GCC diagnostic ignored "-Wcast-qual"
862
execv(argv[0], (char **)argv);
864
#pragma GCC diagnostic pop
866
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
871
if(not add_to_queue(queue, (task_context){
872
.func=wait_for_mandos_client_exit,
874
.mandos_client_exited=mandos_client_exited,
877
error(0, errno, "Failed to add wait_for_mandos_client to queue");
882
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
883
&(struct epoll_event)
884
{ .events=EPOLLIN | EPOLLRDHUP });
885
if(ret != 0 and errno != EEXIST){
886
error(0, errno, "Failed to add file descriptor to epoll set");
891
return add_to_queue(queue, (task_context){
892
.func=read_mandos_client_output,
897
.password_is_read=password_is_read,
901
__attribute__((nonnull))
902
void wait_for_mandos_client_exit(const task_context task,
903
task_queue *const queue){
904
const pid_t pid = task.pid;
905
bool *const mandos_client_exited = task.mandos_client_exited;
906
bool *const quit_now = task.quit_now;
909
switch(waitpid(pid, &status, WNOHANG)){
910
case 0: /* Not exited yet */
911
if(not add_to_queue(queue, task)){
912
error(0, errno, "Failed to add myself to queue");
917
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
923
default: /* Has exited */
924
*mandos_client_exited = true;
925
if((not WIFEXITED(status))
926
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
927
error(0, 0, "Mandos client failed or was killed");
933
__attribute__((nonnull))
934
void read_mandos_client_output(const task_context task,
935
task_queue *const queue){
936
buffer *const password = task.password;
937
bool *const quit_now = task.quit_now;
938
bool *const password_is_read = task.password_is_read;
939
const int fd = task.fd;
940
const int epoll_fd = task.epoll_fd;
942
const size_t new_potential_size = (password->length + PIPE_BUF);
943
if(password->allocated < new_potential_size){
944
char *const new_buffer = calloc(new_potential_size, 1);
945
if(new_buffer == NULL){
946
error(0, errno, "Failed to allocate %" PRIuMAX
947
" bytes for password", (uintmax_t)new_potential_size);
952
if(mlock(new_buffer, new_potential_size) != 0){
953
/* Warn but do not treat as fatal error */
954
if(errno != EPERM and errno != ENOMEM){
955
error(0, errno, "Failed to lock memory for password");
958
if(password->length > 0){
959
memcpy(new_buffer, password->data, password->length);
960
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
961
explicit_bzero(password->data, password->allocated);
963
memset(password->data, '\0', password->allocated);
966
if(password->data != NULL){
967
if(munlock(password->data, password->allocated) != 0){
968
error(0, errno, "Failed to unlock memory of old buffer");
970
free(password->data);
972
password->data = new_buffer;
973
password->allocated = new_potential_size;
976
const ssize_t read_length = read(fd, password->data
977
+ password->length, PIPE_BUF);
979
if(read_length == 0){ /* EOF */
980
*password_is_read = true;
984
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
985
error(0, errno, "Failed to read password from Mandos client");
990
if(read_length > 0){ /* Data has been read */
991
password->length += (size_t)read_length;
994
/* Either data was read, or EAGAIN was indicated, meaning no data
997
/* Re-add the fd to the epoll set */
998
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
999
&(struct epoll_event)
1000
{ .events=EPOLLIN | EPOLLRDHUP });
1001
if(ret != 0 and errno != EEXIST){
1002
error(0, errno, "Failed to re-add file descriptor to epoll set");
1008
/* Re-add myself to the queue */
1009
if(not add_to_queue(queue, task)){
1010
error(0, errno, "Failed to add myself to queue");
1016
__attribute__((nonnull, warn_unused_result))
1017
bool add_inotify_dir_watch(task_queue *const queue,
1018
const int epoll_fd, bool *const quit_now,
1019
buffer *const password,
1020
const char *const dir,
1021
string_set *cancelled_filenames,
1022
const mono_microsecs *const current_time,
1023
bool *const mandos_client_exited,
1024
bool *const password_is_read){
1025
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1027
error(0, errno, "Failed to create inotify instance");
1031
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1032
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1035
error(0, errno, "Failed to create inotify watch on %s", dir);
1039
/* Add the inotify fd to the epoll set */
1040
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1041
&(struct epoll_event)
1042
{ .events=EPOLLIN | EPOLLRDHUP });
1043
if(ret != 0 and errno != EEXIST){
1044
error(0, errno, "Failed to add file descriptor to epoll set");
1049
const task_context read_inotify_event_task = {
1050
.func=read_inotify_event,
1055
.filename=strdup(dir),
1056
.cancelled_filenames=cancelled_filenames,
1057
.current_time=current_time,
1058
.mandos_client_exited=mandos_client_exited,
1059
.password_is_read=password_is_read,
1061
if(read_inotify_event_task.filename == NULL){
1062
error(0, errno, "Failed to strdup(\"%s\")", dir);
1067
return add_to_queue(queue, read_inotify_event_task);
1070
__attribute__((nonnull))
1071
void read_inotify_event(const task_context task,
1072
task_queue *const queue){
1073
const int fd = task.fd;
1074
const int epoll_fd = task.epoll_fd;
1075
char *const filename = task.filename;
1076
bool *quit_now = task.quit_now;
1077
buffer *const password = task.password;
1078
string_set *const cancelled_filenames = task.cancelled_filenames;
1079
const mono_microsecs *const current_time = task.current_time;
1080
bool *const mandos_client_exited = task.mandos_client_exited;
1081
bool *const password_is_read = task.password_is_read;
1083
/* "sufficient to read at least one event." - inotify(7) */
1084
const size_t ievent_size = (sizeof(struct inotify_event)
1087
struct inotify_event event;
1088
char name_buffer[NAME_MAX + 1];
1090
struct inotify_event *const ievent = &ievent_buffer.event;
1092
const ssize_t read_length = read(fd, ievent, ievent_size);
1093
if(read_length == 0){ /* EOF */
1094
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1096
cleanup_task(&task);
1099
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1100
error(0, errno, "Failed to read from inotify fd for directory %s",
1103
cleanup_task(&task);
1106
if(read_length > 0 /* Data has been read */
1107
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1108
char *question_filename = NULL;
1109
const ssize_t question_filename_length
1110
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1111
if(question_filename_length < 0){
1112
error(0, errno, "Failed to create file name from directory name"
1113
" %s and file name %s", filename, ievent->name);
1115
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1116
if(not add_to_queue(queue, (task_context){
1117
.func=open_and_parse_question,
1119
.question_filename=question_filename,
1120
.filename=question_filename,
1122
.cancelled_filenames=cancelled_filenames,
1123
.current_time=current_time,
1124
.mandos_client_exited=mandos_client_exited,
1125
.password_is_read=password_is_read,
1127
error(0, errno, "Failed to add open_and_parse_question task"
1128
" for file name %s to queue", filename);
1130
/* Force the added task (open_and_parse_question) to run
1132
queue->next_run = 1;
1134
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1135
if(not string_set_add(cancelled_filenames,
1136
question_filename)){
1137
error(0, errno, "Could not add question %s to"
1138
" cancelled_questions", question_filename);
1140
free(question_filename);
1141
cleanup_task(&task);
1144
free(question_filename);
1149
/* Either data was read, or EAGAIN was indicated, meaning no data
1152
/* Re-add myself to the queue */
1153
if(not add_to_queue(queue, task)){
1154
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1155
" queue", filename);
1157
cleanup_task(&task);
1161
/* Re-add the fd to the epoll set */
1162
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1163
&(struct epoll_event)
1164
{ .events=EPOLLIN | EPOLLRDHUP });
1165
if(ret != 0 and errno != EEXIST){
1166
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1167
" directory %s to epoll set", fd, filename);
1168
/* Force the added task (read_inotify_event) to run again, at most
1169
one second from now */
1170
if((queue->next_run == 0)
1171
or (queue->next_run > (*current_time + 1000000))){
1172
queue->next_run = *current_time + 1000000;
1177
__attribute__((nonnull))
1178
void open_and_parse_question(const task_context task,
1179
task_queue *const queue){
1180
__attribute__((cleanup(cleanup_string)))
1181
char *question_filename = task.question_filename;
1182
const int epoll_fd = task.epoll_fd;
1183
buffer *const password = task.password;
1184
string_set *const cancelled_filenames = task.cancelled_filenames;
1185
const mono_microsecs *const current_time = task.current_time;
1186
bool *const mandos_client_exited = task.mandos_client_exited;
1187
bool *const password_is_read = task.password_is_read;
1189
/* We use the GLib "Key-value file parser" functions to parse the
1190
question file. See <https://www.freedesktop.org/wiki/Software
1191
/systemd/PasswordAgents/> for specification of contents */
1192
__attribute__((nonnull))
1193
void cleanup_g_key_file(GKeyFile **key_file){
1194
if(*key_file != NULL){
1195
g_key_file_free(*key_file);
1199
__attribute__((cleanup(cleanup_g_key_file)))
1200
GKeyFile *key_file = g_key_file_new();
1201
if(key_file == NULL){
1202
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1206
GError *glib_error = NULL;
1207
if(g_key_file_load_from_file(key_file, question_filename,
1208
G_KEY_FILE_NONE, &glib_error) != TRUE){
1209
/* If a file was removed, we should ignore it, so */
1210
/* only show error message if file actually existed */
1211
if(glib_error->code != G_FILE_ERROR_NOENT){
1212
error(0, 0, "Failed to load question data from file \"%s\": %s",
1213
question_filename, glib_error->message);
1218
__attribute__((cleanup(cleanup_string)))
1219
char *socket_name = g_key_file_get_string(key_file, "Ask",
1222
if(socket_name == NULL){
1223
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1224
question_filename, glib_error->message);
1228
if(strlen(socket_name) == 0){
1229
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1234
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1236
if(glib_error != NULL){
1237
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1238
question_filename, glib_error->message);
1242
if((pid != (guint64)((pid_t)pid))
1243
or (kill((pid_t)pid, 0) != 0)){
1244
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1245
" does not exist", (uintmax_t)pid, question_filename);
1249
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1250
"NotAfter", &glib_error);
1251
if(glib_error != NULL){
1252
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1253
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1254
" %s", question_filename, glib_error->message);
1259
if(queue->next_run == 0 or (queue->next_run > notafter)){
1260
queue->next_run = notafter;
1262
if(*current_time >= notafter){
1267
const task_context connect_question_socket_task = {
1268
.func=connect_question_socket,
1269
.question_filename=strdup(question_filename),
1272
.filename=strdup(socket_name),
1273
.cancelled_filenames=task.cancelled_filenames,
1274
.mandos_client_exited=mandos_client_exited,
1275
.password_is_read=password_is_read,
1276
.current_time=current_time,
1278
if(connect_question_socket_task.question_filename == NULL
1279
or connect_question_socket_task.filename == NULL
1280
or not add_to_queue(queue, connect_question_socket_task)){
1281
error(0, errno, "Failed to add connect_question_socket for socket"
1282
" %s (from \"%s\") to queue", socket_name,
1284
cleanup_task(&connect_question_socket_task);
1287
/* Force the added task (connect_question_socket) to run
1289
queue->next_run = 1;
1292
char *const dup_filename = strdup(question_filename);
1293
const task_context cancel_old_question_task = {
1294
.func=cancel_old_question,
1295
.question_filename=dup_filename,
1297
.filename=dup_filename,
1298
.cancelled_filenames=cancelled_filenames,
1299
.current_time=current_time,
1301
if(cancel_old_question_task.question_filename == NULL
1302
or not add_to_queue(queue, cancel_old_question_task)){
1303
error(0, errno, "Failed to add cancel_old_question for file "
1304
"\"%s\" to queue", question_filename);
1305
cleanup_task(&cancel_old_question_task);
1311
__attribute__((nonnull))
1312
void cancel_old_question(const task_context task,
1313
task_queue *const queue){
1314
char *const question_filename = task.question_filename;
1315
string_set *const cancelled_filenames = task.cancelled_filenames;
1316
const mono_microsecs notafter = task.notafter;
1317
const mono_microsecs *const current_time = task.current_time;
1319
if(*current_time >= notafter){
1320
if(not string_set_add(cancelled_filenames, question_filename)){
1321
error(0, errno, "Failed to cancel question for file %s",
1324
cleanup_task(&task);
1328
if(not add_to_queue(queue, task)){
1329
error(0, errno, "Failed to add cancel_old_question for file "
1330
"%s to queue", question_filename);
1331
cleanup_task(&task);
1335
if((queue->next_run == 0) or (queue->next_run > notafter)){
1336
queue->next_run = notafter;
1340
__attribute__((nonnull))
1341
void connect_question_socket(const task_context task,
1342
task_queue *const queue){
1343
char *const question_filename = task.question_filename;
1344
char *const filename = task.filename;
1345
const int epoll_fd = task.epoll_fd;
1346
buffer *const password = task.password;
1347
string_set *const cancelled_filenames = task.cancelled_filenames;
1348
bool *const mandos_client_exited = task.mandos_client_exited;
1349
bool *const password_is_read = task.password_is_read;
1350
const mono_microsecs *const current_time = task.current_time;
1352
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1354
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1355
error(0, 0, "Socket filename is larger than"
1356
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1357
(uintmax_t)sizeof(sock_name.sun_path), filename);
1358
if(not string_set_add(cancelled_filenames, question_filename)){
1359
error(0, errno, "Failed to cancel question for file %s",
1362
cleanup_task(&task);
1366
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1367
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1370
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1371
if(not add_to_queue(queue, task)){
1372
error(0, errno, "Failed to add connect_question_socket for file"
1373
" \"%s\" and socket \"%s\" to queue", question_filename,
1375
cleanup_task(&task);
1377
/* Force the added task (connect_question_socket) to run
1379
queue->next_run = 1;
1384
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1385
if(connect(fd, (struct sockaddr *)&sock_name,
1386
(socklen_t)SUN_LEN(&sock_name)) != 0){
1387
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1388
if(not add_to_queue(queue, task)){
1389
error(0, errno, "Failed to add connect_question_socket for file"
1390
" \"%s\" and socket \"%s\" to queue", question_filename,
1392
cleanup_task(&task);
1394
/* Force the added task (connect_question_socket) to run again,
1395
at most one second from now */
1396
if((queue->next_run == 0)
1397
or (queue->next_run > (*current_time + 1000000))){
1398
queue->next_run = *current_time + 1000000;
1404
/* Not necessary, but we can try, and merely warn on failure */
1405
if(shutdown(fd, SHUT_RD) != 0){
1406
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1410
/* Add the fd to the epoll set */
1411
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1412
&(struct epoll_event){ .events=EPOLLOUT })
1414
error(0, errno, "Failed to add inotify file descriptor %d for"
1415
" socket %s to epoll set", fd, filename);
1416
if(not add_to_queue(queue, task)){
1417
error(0, errno, "Failed to add connect_question_socket for file"
1418
" \"%s\" and socket \"%s\" to queue", question_filename,
1420
cleanup_task(&task);
1422
/* Force the added task (connect_question_socket) to run again,
1423
at most one second from now */
1424
if((queue->next_run == 0)
1425
or (queue->next_run > (*current_time + 1000000))){
1426
queue->next_run = *current_time + 1000000;
1432
/* add task send_password_to_socket to queue */
1433
const task_context send_password_to_socket_task = {
1434
.func=send_password_to_socket,
1435
.question_filename=question_filename,
1440
.cancelled_filenames=cancelled_filenames,
1441
.mandos_client_exited=mandos_client_exited,
1442
.password_is_read=password_is_read,
1443
.current_time=current_time,
1446
if(not add_to_queue(queue, send_password_to_socket_task)){
1447
error(0, errno, "Failed to add send_password_to_socket for"
1448
" file \"%s\" and socket \"%s\" to queue",
1449
question_filename, filename);
1450
cleanup_task(&send_password_to_socket_task);
1454
__attribute__((nonnull))
1455
void send_password_to_socket(const task_context task,
1456
task_queue *const queue){
1457
char *const question_filename=task.question_filename;
1458
char *const filename=task.filename;
1459
const int epoll_fd=task.epoll_fd;
1460
const int fd=task.fd;
1461
buffer *const password=task.password;
1462
string_set *const cancelled_filenames=task.cancelled_filenames;
1463
bool *const mandos_client_exited = task.mandos_client_exited;
1464
bool *const password_is_read = task.password_is_read;
1465
const mono_microsecs *const current_time = task.current_time;
1467
if(*mandos_client_exited and *password_is_read){
1469
const size_t send_buffer_length = password->length + 2;
1470
char *send_buffer = malloc(send_buffer_length);
1471
if(send_buffer == NULL){
1472
error(0, errno, "Failed to allocate send_buffer");
1474
if(mlock(send_buffer, send_buffer_length) != 0){
1475
/* Warn but do not treat as fatal error */
1476
if(errno != EPERM and errno != ENOMEM){
1477
error(0, errno, "Failed to lock memory for password"
1481
/* “[…] send a single datagram to the socket consisting of the
1482
password string either prefixed with "+" or with "-"
1483
depending on whether the password entry was successful or
1484
not. You may but don't have to include a final NUL byte in
1487
— <https://www.freedesktop.org/wiki/Software/systemd/
1488
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1490
send_buffer[0] = '+'; /* Prefix with "+" */
1491
/* Always add an extra NUL */
1492
send_buffer[password->length + 1] = '\0';
1493
if(password->length > 0){
1494
memcpy(send_buffer + 1, password->data, password->length);
1497
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1499
const error_t saved_errno = errno;
1500
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1501
explicit_bzero(send_buffer, send_buffer_length);
1503
memset(send_buffer, '\0', send_buffer_length);
1505
if(munlock(send_buffer, send_buffer_length) != 0){
1506
error(0, errno, "Failed to unlock memory of send buffer");
1509
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1510
switch(saved_errno){
1523
error(0, 0, "Password of size %" PRIuMAX " is too big",
1524
(uintmax_t)password->length);
1528
__attribute__((fallthrough));
1531
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1532
error(0, 0, "Password only partially sent to socket");
1537
__attribute__((fallthrough));
1540
error(0, saved_errno, "Failed to send() to socket %s",
1542
if(not string_set_add(cancelled_filenames,
1543
question_filename)){
1544
error(0, errno, "Failed to cancel question for file %s",
1547
cleanup_task(&task);
1552
cleanup_task(&task);
1558
/* We failed or are not ready yet; retry later */
1560
if(not add_to_queue(queue, task)){
1561
error(0, errno, "Failed to add send_password_to_socket for"
1562
" file %s and socket %s to queue", question_filename,
1564
cleanup_task(&task);
1567
/* Add the fd to the epoll set */
1568
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1569
&(struct epoll_event){ .events=EPOLLOUT })
1571
error(0, errno, "Failed to add socket file descriptor %d for"
1572
" socket %s to epoll set", fd, filename);
1573
/* Force the added task (send_password_to_socket) to run again, at
1574
most one second from now */
1575
if((queue->next_run == 0)
1576
or (queue->next_run > (*current_time + 1000000))){
1577
queue->next_run = *current_time + 1000000;
1582
__attribute__((warn_unused_result))
1583
bool add_existing_questions(task_queue *const queue,
1585
buffer *const password,
1586
string_set *cancelled_filenames,
1587
const mono_microsecs *const current_time,
1588
bool *const mandos_client_exited,
1589
bool *const password_is_read,
1590
const char *const dirname){
1591
__attribute__((cleanup(cleanup_string)))
1592
char *dir_pattern = NULL;
1593
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1594
if(ret < 0 or dir_pattern == NULL){
1595
error(0, errno, "Could not create glob pattern for directory %s",
1599
__attribute__((cleanup(globfree)))
1600
glob_t question_filenames = {};
1601
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1602
NULL, &question_filenames)){
1605
error(0, errno, "Failed to open directory %s", dirname);
1608
error(0, errno, "There are no question files in %s", dirname);
1611
error(0, errno, "Could not allocate memory for question file"
1612
" names in %s", dirname);
1616
__attribute__((fallthrough));
1619
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1620
char *const question_filename = strdup(question_filenames
1622
const task_context task = {
1623
.func=open_and_parse_question,
1625
.question_filename=question_filename,
1626
.filename=question_filename,
1628
.cancelled_filenames=cancelled_filenames,
1629
.current_time=current_time,
1630
.mandos_client_exited=mandos_client_exited,
1631
.password_is_read=password_is_read,
1634
if(question_filename == NULL
1635
or not add_to_queue(queue, task)){
1636
error(0, errno, "Failed to add open_and_parse_question for"
1637
" file %s to queue",
1638
question_filenames.gl_pathv[i]);
1639
free(question_filename);
1641
queue->next_run = 1;
1648
__attribute__((nonnull, warn_unused_result))
1649
bool wait_for_event(const int epoll_fd,
1650
const mono_microsecs queue_next_run,
1651
const mono_microsecs current_time){
1652
__attribute__((const))
1653
int milliseconds_to_wait(const mono_microsecs currtime,
1654
const mono_microsecs nextrun){
1655
if(currtime >= nextrun){
1658
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1659
if(wait_time_ms > (uintmax_t)INT_MAX){
1662
return (int)wait_time_ms;
1665
const int wait_time_ms = milliseconds_to_wait(current_time,
1668
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1669
sigset_t temporary_unblocked_sigmask;
1670
/* Get current signal mask */
1671
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1674
/* Remove SIGCHLD from the signal mask */
1675
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1678
struct epoll_event events[8]; /* Ignored */
1679
int ret = epoll_pwait(epoll_fd, events,
1680
sizeof(events) / sizeof(struct epoll_event),
1681
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1682
&temporary_unblocked_sigmask);
1683
if(ret < 0 and errno != EINTR){
1684
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1686
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1689
return clear_all_fds_from_epoll_set(epoll_fd);
1692
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1693
/* Create a new empty epoll set */
1694
__attribute__((cleanup(cleanup_close)))
1695
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1696
if(new_epoll_fd < 0){
1699
/* dup3() the new epoll set fd over the old one, replacing it */
1700
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1706
__attribute__((nonnull, warn_unused_result))
1707
bool run_queue(task_queue **const queue,
1708
string_set *const cancelled_filenames,
1709
bool *const quit_now){
1711
task_queue *new_queue = create_queue();
1712
if(new_queue == NULL){
1716
__attribute__((cleanup(string_set_clear)))
1717
string_set old_cancelled_filenames = {};
1718
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1720
/* Declare i outside the for loop, since we might need i after the
1721
loop in case we aborted in the middle */
1723
for(i=0; i < (*queue)->length and not *quit_now; i++){
1724
task_context *const task = &((*queue)->tasks[i]);
1725
const char *const question_filename = task->question_filename;
1726
/* Skip any task referencing a cancelled question filename */
1727
if(question_filename != NULL
1728
and string_set_contains(old_cancelled_filenames,
1729
question_filename)){
1733
task->func(*task, new_queue);
1737
/* we might be in the middle of the queue, so clean up any
1738
remaining tasks in the current queue */
1739
for(; i < (*queue)->length; i++){
1740
cleanup_task(&((*queue)->tasks[i]));
1754
/* End of regular code section */
1756
/* Start of tests section; here are the tests for the above code */
1758
/* This "fixture" data structure is used by the test setup and
1759
teardown functions */
1761
struct sigaction orig_sigaction;
1762
sigset_t orig_sigmask;
1765
static void test_setup(test_fixture *fixture,
1766
__attribute__((unused))
1767
gconstpointer user_data){
1768
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1769
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1772
static void test_teardown(test_fixture *fixture,
1773
__attribute__((unused))
1774
gconstpointer user_data){
1775
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1776
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1779
/* Utility function used by tests to search queue for matching task */
1780
__attribute__((pure, nonnull, warn_unused_result))
1781
static task_context *find_matching_task(const task_queue *const queue,
1782
const task_context task){
1783
/* The argument "task" structure is a pattern to match; 0 in any
1784
member means any value matches, otherwise the value must match.
1785
The filename strings are compared by strcmp(), not by pointer. */
1786
for(size_t i = 0; i < queue->length; i++){
1787
task_context *const current_task = queue->tasks+i;
1788
/* Check all members of task_context, if set to a non-zero value.
1789
If a member does not match, continue to next task in queue */
1791
/* task_func *const func */
1792
if(task.func != NULL and current_task->func != task.func){
1795
/* char *const question_filename; */
1796
if(task.question_filename != NULL
1797
and (current_task->question_filename == NULL
1798
or strcmp(current_task->question_filename,
1799
task.question_filename) != 0)){
1802
/* const pid_t pid; */
1803
if(task.pid != 0 and current_task->pid != task.pid){
1806
/* const int epoll_fd; */
1807
if(task.epoll_fd != 0
1808
and current_task->epoll_fd != task.epoll_fd){
1811
/* bool *const quit_now; */
1812
if(task.quit_now != NULL
1813
and current_task->quit_now != task.quit_now){
1817
if(task.fd != 0 and current_task->fd != task.fd){
1820
/* bool *const mandos_client_exited; */
1821
if(task.mandos_client_exited != NULL
1822
and current_task->mandos_client_exited
1823
!= task.mandos_client_exited){
1826
/* buffer *const password; */
1827
if(task.password != NULL
1828
and current_task->password != task.password){
1831
/* bool *const password_is_read; */
1832
if(task.password_is_read != NULL
1833
and current_task->password_is_read != task.password_is_read){
1836
/* char *filename; */
1837
if(task.filename != NULL
1838
and (current_task->filename == NULL
1839
or strcmp(current_task->filename, task.filename) != 0)){
1842
/* string_set *const cancelled_filenames; */
1843
if(task.cancelled_filenames != NULL
1844
and current_task->cancelled_filenames
1845
!= task.cancelled_filenames){
1848
/* const mono_microsecs notafter; */
1849
if(task.notafter != 0
1850
and current_task->notafter != task.notafter){
1853
/* const mono_microsecs *const current_time; */
1854
if(task.current_time != NULL
1855
and current_task->current_time != task.current_time){
1858
/* Current task matches all members; return it */
1859
return current_task;
1861
/* No task in queue matches passed pattern task */
1865
static void test_create_queue(__attribute__((unused))
1866
test_fixture *fixture,
1867
__attribute__((unused))
1868
gconstpointer user_data){
1869
__attribute__((cleanup(cleanup_queue)))
1870
task_queue *const queue = create_queue();
1871
g_assert_nonnull(queue);
1872
g_assert_null(queue->tasks);
1873
g_assert_true(queue->length == 0);
1874
g_assert_true(queue->next_run == 0);
1877
static task_func dummy_func;
1879
static void test_add_to_queue(__attribute__((unused))
1880
test_fixture *fixture,
1881
__attribute__((unused))
1882
gconstpointer user_data){
1883
__attribute__((cleanup(cleanup_queue)))
1884
task_queue *queue = create_queue();
1885
g_assert_nonnull(queue);
1887
g_assert_true(add_to_queue(queue,
1888
(task_context){ .func=dummy_func }));
1889
g_assert_true(queue->length == 1);
1890
g_assert_nonnull(queue->tasks);
1891
g_assert_true(queue->tasks[0].func == dummy_func);
1894
static void test_add_to_queue_overflow(__attribute__((unused))
1895
test_fixture *fixture,
1896
__attribute__((unused))
1897
gconstpointer user_data){
1898
__attribute__((cleanup(cleanup_queue)))
1899
task_queue *queue = create_queue();
1900
g_assert_nonnull(queue);
1901
g_assert_true(queue->length == 0);
1902
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1904
FILE *real_stderr = stderr;
1905
FILE *devnull = fopen("/dev/null", "we");
1906
g_assert_nonnull(devnull);
1908
const bool ret = add_to_queue(queue,
1909
(task_context){ .func=dummy_func });
1910
g_assert_true(errno == ENOMEM);
1911
g_assert_false(ret);
1912
stderr = real_stderr;
1913
g_assert_cmpint(fclose(devnull), ==, 0);
1914
queue->length = 0; /* Restore real size */
1917
static void dummy_func(__attribute__((unused))
1918
const task_context task,
1919
__attribute__((unused))
1920
task_queue *const queue){
1923
static void test_queue_has_question_empty(__attribute__((unused))
1924
test_fixture *fixture,
1925
__attribute__((unused))
1926
gconstpointer user_data){
1927
__attribute__((cleanup(cleanup_queue)))
1928
task_queue *queue = create_queue();
1929
g_assert_nonnull(queue);
1930
g_assert_false(queue_has_question(queue));
1933
static void test_queue_has_question_false(__attribute__((unused))
1934
test_fixture *fixture,
1935
__attribute__((unused))
1936
gconstpointer user_data){
1937
__attribute__((cleanup(cleanup_queue)))
1938
task_queue *queue = create_queue();
1939
g_assert_nonnull(queue);
1940
g_assert_true(add_to_queue(queue,
1941
(task_context){ .func=dummy_func }));
1942
g_assert_false(queue_has_question(queue));
1945
static void test_queue_has_question_true(__attribute__((unused))
1946
test_fixture *fixture,
1947
__attribute__((unused))
1948
gconstpointer user_data){
1949
__attribute__((cleanup(cleanup_queue)))
1950
task_queue *queue = create_queue();
1951
g_assert_nonnull(queue);
1952
char *const question_filename
1953
= strdup("/nonexistent/question_filename");
1954
g_assert_nonnull(question_filename);
1955
task_context task = {
1957
.question_filename=question_filename,
1959
g_assert_true(add_to_queue(queue, task));
1960
g_assert_true(queue_has_question(queue));
1963
static void test_queue_has_question_false2(__attribute__((unused))
1964
test_fixture *fixture,
1965
__attribute__((unused))
1966
gconstpointer user_data){
1967
__attribute__((cleanup(cleanup_queue)))
1968
task_queue *queue = create_queue();
1969
g_assert_nonnull(queue);
1970
task_context task = { .func=dummy_func };
1971
g_assert_true(add_to_queue(queue, task));
1972
g_assert_true(add_to_queue(queue, task));
1973
g_assert_cmpint((int)queue->length, ==, 2);
1974
g_assert_false(queue_has_question(queue));
1977
static void test_queue_has_question_true2(__attribute__((unused))
1978
test_fixture *fixture,
1979
__attribute__((unused))
1980
gconstpointer user_data){
1981
__attribute__((cleanup(cleanup_queue)))
1982
task_queue *queue = create_queue();
1983
g_assert_nonnull(queue);
1984
task_context task1 = { .func=dummy_func };
1985
g_assert_true(add_to_queue(queue, task1));
1986
char *const question_filename
1987
= strdup("/nonexistent/question_filename");
1988
g_assert_nonnull(question_filename);
1989
task_context task2 = {
1991
.question_filename=question_filename,
1993
g_assert_true(add_to_queue(queue, task2));
1994
g_assert_cmpint((int)queue->length, ==, 2);
1995
g_assert_true(queue_has_question(queue));
1998
static void test_cleanup_buffer(__attribute__((unused))
1999
test_fixture *fixture,
2000
__attribute__((unused))
2001
gconstpointer user_data){
2004
const size_t buffersize = 10;
2006
buf.data = malloc(buffersize);
2007
g_assert_nonnull(buf.data);
2008
if(mlock(buf.data, buffersize) != 0){
2009
g_assert_true(errno == EPERM or errno == ENOMEM);
2012
cleanup_buffer(&buf);
2013
g_assert_null(buf.data);
2017
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2018
test_fixture *fixture,
2019
__attribute__((unused))
2022
__attribute__((cleanup(string_set_clear)))
2023
string_set set = {};
2024
g_assert_false(string_set_contains(set, "")); /* Empty string */
2025
g_assert_false(string_set_contains(set, "test_string"));
2029
test_string_set_with_added_string_contains_it(__attribute__((unused))
2030
test_fixture *fixture,
2031
__attribute__((unused))
2034
__attribute__((cleanup(string_set_clear)))
2035
string_set set = {};
2036
g_assert_true(string_set_add(&set, "test_string"));
2037
g_assert_true(string_set_contains(set, "test_string"));
2041
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2042
test_fixture *fixture,
2043
__attribute__((unused))
2044
gconstpointer user_data){
2045
__attribute__((cleanup(string_set_clear)))
2046
string_set set = {};
2047
g_assert_true(string_set_add(&set, "test_string"));
2048
string_set_clear(&set);
2049
g_assert_false(string_set_contains(set, "test_string"));
2053
void test_string_set_swap_one_with_empty(__attribute__((unused))
2054
test_fixture *fixture,
2055
__attribute__((unused))
2056
gconstpointer user_data){
2057
__attribute__((cleanup(string_set_clear)))
2058
string_set set1 = {};
2059
__attribute__((cleanup(string_set_clear)))
2060
string_set set2 = {};
2061
g_assert_true(string_set_add(&set1, "test_string1"));
2062
string_set_swap(&set1, &set2);
2063
g_assert_false(string_set_contains(set1, "test_string1"));
2064
g_assert_true(string_set_contains(set2, "test_string1"));
2068
void test_string_set_swap_empty_with_one(__attribute__((unused))
2069
test_fixture *fixture,
2070
__attribute__((unused))
2071
gconstpointer user_data){
2072
__attribute__((cleanup(string_set_clear)))
2073
string_set set1 = {};
2074
__attribute__((cleanup(string_set_clear)))
2075
string_set set2 = {};
2076
g_assert_true(string_set_add(&set2, "test_string2"));
2077
string_set_swap(&set1, &set2);
2078
g_assert_true(string_set_contains(set1, "test_string2"));
2079
g_assert_false(string_set_contains(set2, "test_string2"));
2082
static void test_string_set_swap_one_with_one(__attribute__((unused))
2083
test_fixture *fixture,
2084
__attribute__((unused))
2087
__attribute__((cleanup(string_set_clear)))
2088
string_set set1 = {};
2089
__attribute__((cleanup(string_set_clear)))
2090
string_set set2 = {};
2091
g_assert_true(string_set_add(&set1, "test_string1"));
2092
g_assert_true(string_set_add(&set2, "test_string2"));
2093
string_set_swap(&set1, &set2);
2094
g_assert_false(string_set_contains(set1, "test_string1"));
2095
g_assert_true(string_set_contains(set1, "test_string2"));
2096
g_assert_false(string_set_contains(set2, "test_string2"));
2097
g_assert_true(string_set_contains(set2, "test_string1"));
2100
static bool fd_has_cloexec_and_nonblock(const int);
2102
static bool epoll_set_contains(int, int, uint32_t);
2104
static void test_start_mandos_client(test_fixture *fixture,
2105
__attribute__((unused))
2106
gconstpointer user_data){
2108
bool mandos_client_exited = false;
2109
bool quit_now = false;
2110
__attribute__((cleanup(cleanup_close)))
2111
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2112
g_assert_cmpint(epoll_fd, >=, 0);
2113
__attribute__((cleanup(cleanup_queue)))
2114
task_queue *queue = create_queue();
2115
g_assert_nonnull(queue);
2116
buffer password = {};
2117
bool password_is_read = false;
2118
const char helper_directory[] = "/nonexistent";
2119
const char *const argv[] = { "/bin/true", NULL };
2121
g_assert_true(start_mandos_client(queue, epoll_fd,
2122
&mandos_client_exited, &quit_now,
2123
&password, &password_is_read,
2124
&fixture->orig_sigaction,
2125
fixture->orig_sigmask,
2126
helper_directory, 0, 0, argv));
2128
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2130
const task_context *const added_wait_task
2131
= find_matching_task(queue, (task_context){
2132
.func=wait_for_mandos_client_exit,
2133
.mandos_client_exited=&mandos_client_exited,
2134
.quit_now=&quit_now,
2136
g_assert_nonnull(added_wait_task);
2137
g_assert_cmpint(added_wait_task->pid, >, 0);
2138
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2139
waitpid(added_wait_task->pid, NULL, 0);
2141
const task_context *const added_read_task
2142
= find_matching_task(queue, (task_context){
2143
.func=read_mandos_client_output,
2145
.password=&password,
2146
.password_is_read=&password_is_read,
2147
.quit_now=&quit_now,
2149
g_assert_nonnull(added_read_task);
2150
g_assert_cmpint(added_read_task->fd, >, 2);
2151
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2152
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2153
EPOLLIN | EPOLLRDHUP));
2156
static bool fd_has_cloexec_and_nonblock(const int fd){
2157
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2158
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2159
return ((socket_fd_flags >= 0)
2160
and (socket_fd_flags & FD_CLOEXEC)
2161
and (socket_file_flags >= 0)
2162
and (socket_file_flags & O_NONBLOCK));
2165
__attribute__((const))
2166
bool is_privileged(void){
2167
uid_t user = getuid() + 1;
2168
if(user == 0){ /* Overflow check */
2171
gid_t group = getuid() + 1;
2172
if(group == 0){ /* Overflow check */
2175
const pid_t pid = fork();
2176
if(pid == 0){ /* Child */
2177
if(setresgid((uid_t)-1, group, group) == -1){
2179
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2180
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2184
if(setresuid((uid_t)-1, user, user) == -1){
2186
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2187
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2194
waitpid(pid, &status, 0);
2195
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2201
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2202
/* Only scan for events in this eventmask */
2203
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2204
__attribute__((cleanup(cleanup_string)))
2205
char *fdinfo_name = NULL;
2206
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2207
g_assert_cmpint(ret, >, 0);
2208
g_assert_nonnull(fdinfo_name);
2210
FILE *fdinfo = fopen(fdinfo_name, "r");
2211
g_assert_nonnull(fdinfo);
2212
uint32_t reported_events;
2217
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2220
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2221
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2222
&found_fd, &reported_events) == 2){
2227
} while(not feof(fdinfo) and not ferror(fdinfo));
2228
g_assert_cmpint(fclose(fdinfo), ==, 0);
2235
/* Don't check events if none are given */
2238
return (reported_events & eventmask) == (events & eventmask);
2241
static void test_start_mandos_client_execv(test_fixture *fixture,
2242
__attribute__((unused))
2243
gconstpointer user_data){
2244
bool mandos_client_exited = false;
2245
bool quit_now = false;
2246
__attribute__((cleanup(cleanup_close)))
2247
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2248
g_assert_cmpint(epoll_fd, >=, 0);
2249
__attribute__((cleanup(cleanup_queue)))
2250
task_queue *queue = create_queue();
2251
g_assert_nonnull(queue);
2252
__attribute__((cleanup(cleanup_buffer)))
2253
buffer password = {};
2254
const char helper_directory[] = "/nonexistent";
2255
/* Can't execv("/", ...), so this should fail */
2256
const char *const argv[] = { "/", NULL };
2259
__attribute__((cleanup(cleanup_close)))
2260
const int devnull_fd = open("/dev/null",
2261
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2262
g_assert_cmpint(devnull_fd, >=, 0);
2263
__attribute__((cleanup(cleanup_close)))
2264
const int real_stderr_fd = dup(STDERR_FILENO);
2265
g_assert_cmpint(real_stderr_fd, >=, 0);
2266
dup2(devnull_fd, STDERR_FILENO);
2268
const bool success = start_mandos_client(queue, epoll_fd,
2269
&mandos_client_exited,
2273
&fixture->orig_sigaction,
2274
fixture->orig_sigmask,
2275
helper_directory, 0, 0,
2277
dup2(real_stderr_fd, STDERR_FILENO);
2278
g_assert_true(success);
2280
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2282
struct timespec starttime, currtime;
2283
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2285
queue->next_run = 0;
2286
string_set cancelled_filenames = {};
2289
__attribute__((cleanup(cleanup_close)))
2290
const int devnull_fd = open("/dev/null",
2291
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2292
g_assert_cmpint(devnull_fd, >=, 0);
2293
__attribute__((cleanup(cleanup_close)))
2294
const int real_stderr_fd = dup(STDERR_FILENO);
2295
g_assert_cmpint(real_stderr_fd, >=, 0);
2296
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2297
dup2(devnull_fd, STDERR_FILENO);
2298
const bool success = run_queue(&queue, &cancelled_filenames,
2300
dup2(real_stderr_fd, STDERR_FILENO);
2305
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2306
} while(((queue->length) > 0)
2308
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2310
g_assert_true(quit_now);
2311
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2312
g_assert_true(mandos_client_exited);
2315
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2316
__attribute__((unused))
2319
if(not is_privileged()){
2320
g_test_skip("Not privileged");
2324
bool mandos_client_exited = false;
2325
bool quit_now = false;
2326
__attribute__((cleanup(cleanup_close)))
2327
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2328
g_assert_cmpint(epoll_fd, >=, 0);
2329
__attribute__((cleanup(cleanup_queue)))
2330
task_queue *queue = create_queue();
2331
g_assert_nonnull(queue);
2332
__attribute__((cleanup(cleanup_buffer)))
2333
buffer password = {};
2334
bool password_is_read = false;
2335
const char helper_directory[] = "/nonexistent";
2336
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2340
const bool success = start_mandos_client(queue, epoll_fd,
2341
&mandos_client_exited,
2342
&quit_now, &password,
2344
&fixture->orig_sigaction,
2345
fixture->orig_sigmask,
2346
helper_directory, user,
2348
g_assert_true(success);
2349
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2351
struct timespec starttime, currtime;
2352
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2354
queue->next_run = 0;
2355
string_set cancelled_filenames = {};
2356
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2357
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2358
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2359
} while(((queue->length) > 0)
2361
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2363
g_assert_false(quit_now);
2364
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2365
g_assert_true(mandos_client_exited);
2367
g_assert_true(password_is_read);
2368
g_assert_nonnull(password.data);
2371
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2373
g_assert_true((uid_t)id == id);
2375
g_assert_cmpuint((unsigned int)id, ==, 0);
2378
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2379
__attribute__((unused))
2382
if(not is_privileged()){
2383
g_test_skip("Not privileged");
2387
bool mandos_client_exited = false;
2388
bool quit_now = false;
2389
__attribute__((cleanup(cleanup_close)))
2390
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2391
g_assert_cmpint(epoll_fd, >=, 0);
2392
__attribute__((cleanup(cleanup_queue)))
2393
task_queue *queue = create_queue();
2394
g_assert_nonnull(queue);
2395
__attribute__((cleanup(cleanup_buffer)))
2396
buffer password = {};
2397
bool password_is_read = false;
2398
const char helper_directory[] = "/nonexistent";
2399
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2403
const bool success = start_mandos_client(queue, epoll_fd,
2404
&mandos_client_exited,
2405
&quit_now, &password,
2407
&fixture->orig_sigaction,
2408
fixture->orig_sigmask,
2409
helper_directory, user,
2411
g_assert_true(success);
2412
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2414
struct timespec starttime, currtime;
2415
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2417
queue->next_run = 0;
2418
string_set cancelled_filenames = {};
2419
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2420
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2421
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2422
} while(((queue->length) > 0)
2424
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2426
g_assert_false(quit_now);
2427
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2428
g_assert_true(mandos_client_exited);
2430
g_assert_true(password_is_read);
2431
g_assert_nonnull(password.data);
2434
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2436
g_assert_true((gid_t)id == id);
2438
g_assert_cmpuint((unsigned int)id, ==, 0);
2441
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2442
__attribute__((unused))
2445
if(not is_privileged()){
2446
g_test_skip("Not privileged");
2450
bool mandos_client_exited = false;
2451
bool quit_now = false;
2452
__attribute__((cleanup(cleanup_close)))
2453
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2454
g_assert_cmpint(epoll_fd, >=, 0);
2455
__attribute__((cleanup(cleanup_queue)))
2456
task_queue *queue = create_queue();
2457
g_assert_nonnull(queue);
2458
__attribute__((cleanup(cleanup_buffer)))
2459
buffer password = {};
2460
bool password_is_read = false;
2461
const char helper_directory[] = "/nonexistent";
2462
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2467
const bool success = start_mandos_client(queue, epoll_fd,
2468
&mandos_client_exited,
2469
&quit_now, &password,
2471
&fixture->orig_sigaction,
2472
fixture->orig_sigmask,
2473
helper_directory, user,
2475
g_assert_true(success);
2476
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2478
struct timespec starttime, currtime;
2479
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2481
queue->next_run = 0;
2482
string_set cancelled_filenames = {};
2483
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2484
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2485
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2486
} while(((queue->length) > 0)
2488
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2490
g_assert_false(quit_now);
2491
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2492
g_assert_true(mandos_client_exited);
2494
g_assert_true(password_is_read);
2495
g_assert_nonnull(password.data);
2498
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2500
g_assert_true((uid_t)id == id);
2502
g_assert_cmpuint((unsigned int)id, ==, user);
2505
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2506
__attribute__((unused))
2509
if(not is_privileged()){
2510
g_test_skip("Not privileged");
2514
bool mandos_client_exited = false;
2515
bool quit_now = false;
2516
__attribute__((cleanup(cleanup_close)))
2517
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2518
g_assert_cmpint(epoll_fd, >=, 0);
2519
__attribute__((cleanup(cleanup_queue)))
2520
task_queue *queue = create_queue();
2521
g_assert_nonnull(queue);
2522
__attribute__((cleanup(cleanup_buffer)))
2523
buffer password = {};
2524
bool password_is_read = false;
2525
const char helper_directory[] = "/nonexistent";
2526
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2531
const bool success = start_mandos_client(queue, epoll_fd,
2532
&mandos_client_exited,
2533
&quit_now, &password,
2535
&fixture->orig_sigaction,
2536
fixture->orig_sigmask,
2537
helper_directory, user,
2539
g_assert_true(success);
2540
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2542
struct timespec starttime, currtime;
2543
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2545
queue->next_run = 0;
2546
string_set cancelled_filenames = {};
2547
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2548
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2549
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2550
} while(((queue->length) > 0)
2552
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2554
g_assert_false(quit_now);
2555
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2556
g_assert_true(mandos_client_exited);
2558
g_assert_true(password_is_read);
2559
g_assert_nonnull(password.data);
2562
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2564
g_assert_true((gid_t)id == id);
2566
g_assert_cmpuint((unsigned int)id, ==, group);
2569
static void test_start_mandos_client_read(test_fixture *fixture,
2570
__attribute__((unused))
2571
gconstpointer user_data){
2572
bool mandos_client_exited = false;
2573
bool quit_now = false;
2574
__attribute__((cleanup(cleanup_close)))
2575
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2576
g_assert_cmpint(epoll_fd, >=, 0);
2577
__attribute__((cleanup(cleanup_queue)))
2578
task_queue *queue = create_queue();
2579
g_assert_nonnull(queue);
2580
__attribute__((cleanup(cleanup_buffer)))
2581
buffer password = {};
2582
bool password_is_read = false;
2583
const char dummy_test_password[] = "dummy test password";
2584
const char helper_directory[] = "/nonexistent";
2585
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2588
const bool success = start_mandos_client(queue, epoll_fd,
2589
&mandos_client_exited,
2590
&quit_now, &password,
2592
&fixture->orig_sigaction,
2593
fixture->orig_sigmask,
2594
helper_directory, 0, 0,
2596
g_assert_true(success);
2597
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2599
struct timespec starttime, currtime;
2600
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2602
queue->next_run = 0;
2603
string_set cancelled_filenames = {};
2604
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2605
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2606
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2607
} while(((queue->length) > 0)
2609
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2611
g_assert_false(quit_now);
2612
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2613
g_assert_true(mandos_client_exited);
2615
g_assert_true(password_is_read);
2616
g_assert_cmpint((int)password.length, ==,
2617
sizeof(dummy_test_password)-1);
2618
g_assert_nonnull(password.data);
2619
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2620
sizeof(dummy_test_password)-1), ==, 0);
2624
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2625
__attribute__((unused))
2628
bool mandos_client_exited = false;
2629
bool quit_now = false;
2630
__attribute__((cleanup(cleanup_close)))
2631
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2632
g_assert_cmpint(epoll_fd, >=, 0);
2633
__attribute__((cleanup(cleanup_queue)))
2634
task_queue *queue = create_queue();
2635
g_assert_nonnull(queue);
2636
__attribute__((cleanup(cleanup_buffer)))
2637
buffer password = {};
2638
bool password_is_read = false;
2639
const char helper_directory[] = "/nonexistent";
2640
const char *const argv[] = { "/bin/sh", "-c",
2641
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2643
const bool success = start_mandos_client(queue, epoll_fd,
2644
&mandos_client_exited,
2645
&quit_now, &password,
2647
&fixture->orig_sigaction,
2648
fixture->orig_sigmask,
2649
helper_directory, 0, 0,
2651
g_assert_true(success);
2652
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2654
struct timespec starttime, currtime;
2655
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2657
queue->next_run = 0;
2658
string_set cancelled_filenames = {};
2659
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2660
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2661
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2662
} while(((queue->length) > 0)
2664
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2666
g_assert_false(quit_now);
2667
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2668
g_assert_true(mandos_client_exited);
2670
g_assert_true(password_is_read);
2671
g_assert_cmpint((int)password.length, ==,
2672
sizeof(helper_directory)-1);
2673
g_assert_nonnull(password.data);
2674
g_assert_cmpint(memcmp(helper_directory, password.data,
2675
sizeof(helper_directory)-1), ==, 0);
2678
__attribute__((nonnull, warn_unused_result))
2679
static bool proc_status_sigblk_to_sigset(const char *const,
2682
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2683
__attribute__((unused))
2684
gconstpointer user_data){
2685
bool mandos_client_exited = false;
2686
bool quit_now = false;
2687
__attribute__((cleanup(cleanup_close)))
2688
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2689
g_assert_cmpint(epoll_fd, >=, 0);
2690
__attribute__((cleanup(cleanup_queue)))
2691
task_queue *queue = create_queue();
2692
g_assert_nonnull(queue);
2693
__attribute__((cleanup(cleanup_buffer)))
2694
buffer password = {};
2695
bool password_is_read = false;
2696
const char helper_directory[] = "/nonexistent";
2697
/* see proc(5) for format of /proc/self/status */
2698
const char *const argv[] = { "/usr/bin/awk",
2699
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2701
g_assert_true(start_mandos_client(queue, epoll_fd,
2702
&mandos_client_exited, &quit_now,
2703
&password, &password_is_read,
2704
&fixture->orig_sigaction,
2705
fixture->orig_sigmask,
2706
helper_directory, 0, 0, argv));
2708
struct timespec starttime, currtime;
2709
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2711
queue->next_run = 0;
2712
string_set cancelled_filenames = {};
2713
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2714
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2715
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2716
} while((not (mandos_client_exited and password_is_read))
2718
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2719
g_assert_true(mandos_client_exited);
2720
g_assert_true(password_is_read);
2722
sigset_t parsed_sigmask;
2723
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2726
for(int signum = 1; signum < NSIG; signum++){
2727
const bool has_signal = sigismember(&parsed_sigmask, signum);
2728
if(sigismember(&fixture->orig_sigmask, signum)){
2729
g_assert_true(has_signal);
2731
g_assert_false(has_signal);
2736
__attribute__((nonnull, warn_unused_result))
2737
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2738
sigset_t *const sigmask){
2739
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2740
uintmax_t scanned_sigmask;
2741
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2744
if(sigemptyset(sigmask) != 0){
2747
for(int signum = 1; signum < NSIG; signum++){
2748
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2749
if(sigaddset(sigmask, signum) != 0){
2757
static void run_task_with_stderr_to_dev_null(const task_context task,
2758
task_queue *const queue);
2761
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2762
test_fixture *fixture,
2763
__attribute__((unused))
2764
gconstpointer user_data){
2766
bool mandos_client_exited = false;
2767
bool quit_now = false;
2769
__attribute__((cleanup(cleanup_queue)))
2770
task_queue *queue = create_queue();
2771
g_assert_nonnull(queue);
2772
const task_context task = {
2773
.func=wait_for_mandos_client_exit,
2775
.mandos_client_exited=&mandos_client_exited,
2776
.quit_now=&quit_now,
2778
run_task_with_stderr_to_dev_null(task, queue);
2780
g_assert_false(mandos_client_exited);
2781
g_assert_true(quit_now);
2782
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2785
static void run_task_with_stderr_to_dev_null(const task_context task,
2786
task_queue *const queue){
2787
FILE *real_stderr = stderr;
2788
FILE *devnull = fopen("/dev/null", "we");
2789
g_assert_nonnull(devnull);
2792
task.func(task, queue);
2793
stderr = real_stderr;
2795
g_assert_cmpint(fclose(devnull), ==, 0);
2799
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2800
__attribute__((unused))
2801
gconstpointer user_data){
2802
bool mandos_client_exited = false;
2803
bool quit_now = false;
2805
pid_t create_eternal_process(void){
2806
const pid_t pid = fork();
2807
if(pid == 0){ /* Child */
2808
if(not restore_signal_handler(&fixture->orig_sigaction)){
2809
_exit(EXIT_FAILURE);
2811
if(not restore_sigmask(&fixture->orig_sigmask)){
2812
_exit(EXIT_FAILURE);
2820
pid_t pid = create_eternal_process();
2821
g_assert_true(pid != -1);
2823
__attribute__((cleanup(cleanup_queue)))
2824
task_queue *queue = create_queue();
2825
g_assert_nonnull(queue);
2826
const task_context task = {
2827
.func=wait_for_mandos_client_exit,
2829
.mandos_client_exited=&mandos_client_exited,
2830
.quit_now=&quit_now,
2832
task.func(task, queue);
2834
g_assert_false(mandos_client_exited);
2835
g_assert_false(quit_now);
2836
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2838
g_assert_nonnull(find_matching_task(queue, (task_context){
2839
.func=wait_for_mandos_client_exit,
2841
.mandos_client_exited=&mandos_client_exited,
2842
.quit_now=&quit_now,
2847
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2848
__attribute__((unused))
2851
bool mandos_client_exited = false;
2852
bool quit_now = false;
2854
pid_t create_successful_process(void){
2855
const pid_t pid = fork();
2856
if(pid == 0){ /* Child */
2857
if(not restore_signal_handler(&fixture->orig_sigaction)){
2858
_exit(EXIT_FAILURE);
2860
if(not restore_sigmask(&fixture->orig_sigmask)){
2861
_exit(EXIT_FAILURE);
2867
const pid_t pid = create_successful_process();
2868
g_assert_true(pid != -1);
2870
__attribute__((cleanup(cleanup_queue)))
2871
task_queue *queue = create_queue();
2872
g_assert_nonnull(queue);
2873
const task_context initial_task = {
2874
.func=wait_for_mandos_client_exit,
2876
.mandos_client_exited=&mandos_client_exited,
2877
.quit_now=&quit_now,
2879
g_assert_true(add_to_queue(queue, initial_task));
2881
struct timespec starttime, currtime;
2882
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2883
__attribute__((cleanup(cleanup_close)))
2884
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2886
queue->next_run = 0;
2887
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2888
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2889
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2890
} while((not mandos_client_exited)
2892
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2894
g_assert_true(mandos_client_exited);
2895
g_assert_false(quit_now);
2896
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2900
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2901
__attribute__((unused))
2904
bool mandos_client_exited = false;
2905
bool quit_now = false;
2907
pid_t create_failing_process(void){
2908
const pid_t pid = fork();
2909
if(pid == 0){ /* Child */
2910
if(not restore_signal_handler(&fixture->orig_sigaction)){
2911
_exit(EXIT_FAILURE);
2913
if(not restore_sigmask(&fixture->orig_sigmask)){
2914
_exit(EXIT_FAILURE);
2920
const pid_t pid = create_failing_process();
2921
g_assert_true(pid != -1);
2923
__attribute__((cleanup(string_set_clear)))
2924
string_set cancelled_filenames = {};
2925
__attribute__((cleanup(cleanup_close)))
2926
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2927
g_assert_cmpint(epoll_fd, >=, 0);
2928
__attribute__((cleanup(cleanup_queue)))
2929
task_queue *queue = create_queue();
2930
g_assert_nonnull(queue);
2931
g_assert_true(add_to_queue(queue, (task_context){
2932
.func=wait_for_mandos_client_exit,
2934
.mandos_client_exited=&mandos_client_exited,
2935
.quit_now=&quit_now,
2938
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2940
__attribute__((cleanup(cleanup_close)))
2941
const int devnull_fd = open("/dev/null",
2942
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2943
g_assert_cmpint(devnull_fd, >=, 0);
2944
__attribute__((cleanup(cleanup_close)))
2945
const int real_stderr_fd = dup(STDERR_FILENO);
2946
g_assert_cmpint(real_stderr_fd, >=, 0);
2948
struct timespec starttime, currtime;
2949
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2951
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2952
dup2(devnull_fd, STDERR_FILENO);
2953
const bool success = run_queue(&queue, &cancelled_filenames,
2955
dup2(real_stderr_fd, STDERR_FILENO);
2960
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2961
} while((not mandos_client_exited)
2963
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2965
g_assert_true(quit_now);
2966
g_assert_true(mandos_client_exited);
2967
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2971
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2972
__attribute__((unused))
2973
gconstpointer user_data){
2974
bool mandos_client_exited = false;
2975
bool quit_now = false;
2977
pid_t create_killed_process(void){
2978
const pid_t pid = fork();
2979
if(pid == 0){ /* Child */
2980
if(not restore_signal_handler(&fixture->orig_sigaction)){
2981
_exit(EXIT_FAILURE);
2983
if(not restore_sigmask(&fixture->orig_sigmask)){
2984
_exit(EXIT_FAILURE);
2993
const pid_t pid = create_killed_process();
2994
g_assert_true(pid != -1);
2996
__attribute__((cleanup(string_set_clear)))
2997
string_set cancelled_filenames = {};
2998
__attribute__((cleanup(cleanup_close)))
2999
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3000
g_assert_cmpint(epoll_fd, >=, 0);
3001
__attribute__((cleanup(cleanup_queue)))
3002
task_queue *queue = create_queue();
3003
g_assert_nonnull(queue);
3004
g_assert_true(add_to_queue(queue, (task_context){
3005
.func=wait_for_mandos_client_exit,
3007
.mandos_client_exited=&mandos_client_exited,
3008
.quit_now=&quit_now,
3011
__attribute__((cleanup(cleanup_close)))
3012
const int devnull_fd = open("/dev/null",
3013
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3014
g_assert_cmpint(devnull_fd, >=, 0);
3015
__attribute__((cleanup(cleanup_close)))
3016
const int real_stderr_fd = dup(STDERR_FILENO);
3017
g_assert_cmpint(real_stderr_fd, >=, 0);
3019
struct timespec starttime, currtime;
3020
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3022
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3023
dup2(devnull_fd, STDERR_FILENO);
3024
const bool success = run_queue(&queue, &cancelled_filenames,
3026
dup2(real_stderr_fd, STDERR_FILENO);
3031
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3032
} while((not mandos_client_exited)
3034
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3036
g_assert_true(mandos_client_exited);
3037
g_assert_true(quit_now);
3038
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3041
static bool epoll_set_does_not_contain(int, int);
3044
void test_read_mandos_client_output_readerror(__attribute__((unused))
3045
test_fixture *fixture,
3046
__attribute__((unused))
3049
__attribute__((cleanup(cleanup_close)))
3050
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3051
g_assert_cmpint(epoll_fd, >=, 0);
3053
__attribute__((cleanup(cleanup_buffer)))
3054
buffer password = {};
3056
/* Reading /proc/self/mem from offset 0 will always give EIO */
3057
const int fd = open("/proc/self/mem",
3058
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3060
bool password_is_read = false;
3061
bool quit_now = false;
3062
__attribute__((cleanup(cleanup_queue)))
3063
task_queue *queue = create_queue();
3064
g_assert_nonnull(queue);
3066
task_context task = {
3067
.func=read_mandos_client_output,
3070
.password=&password,
3071
.password_is_read=&password_is_read,
3072
.quit_now=&quit_now,
3074
run_task_with_stderr_to_dev_null(task, queue);
3075
g_assert_false(password_is_read);
3076
g_assert_cmpint((int)password.length, ==, 0);
3077
g_assert_true(quit_now);
3078
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3080
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3082
g_assert_cmpint(close(fd), ==, -1);
3085
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3086
return not epoll_set_contains(epoll_fd, fd, 0);
3090
void test_read_mandos_client_output_nodata(__attribute__((unused))
3091
test_fixture *fixture,
3092
__attribute__((unused))
3093
gconstpointer user_data){
3094
__attribute__((cleanup(cleanup_close)))
3095
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3096
g_assert_cmpint(epoll_fd, >=, 0);
3099
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3101
__attribute__((cleanup(cleanup_buffer)))
3102
buffer password = {};
3104
bool password_is_read = false;
3105
bool quit_now = false;
3106
__attribute__((cleanup(cleanup_queue)))
3107
task_queue *queue = create_queue();
3108
g_assert_nonnull(queue);
3110
task_context task = {
3111
.func=read_mandos_client_output,
3114
.password=&password,
3115
.password_is_read=&password_is_read,
3116
.quit_now=&quit_now,
3118
task.func(task, queue);
3119
g_assert_false(password_is_read);
3120
g_assert_cmpint((int)password.length, ==, 0);
3121
g_assert_false(quit_now);
3122
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3124
g_assert_nonnull(find_matching_task(queue, (task_context){
3125
.func=read_mandos_client_output,
3128
.password=&password,
3129
.password_is_read=&password_is_read,
3130
.quit_now=&quit_now,
3133
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3134
EPOLLIN | EPOLLRDHUP));
3136
g_assert_cmpint(close(pipefds[1]), ==, 0);
3139
static void test_read_mandos_client_output_eof(__attribute__((unused))
3140
test_fixture *fixture,
3141
__attribute__((unused))
3144
__attribute__((cleanup(cleanup_close)))
3145
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3146
g_assert_cmpint(epoll_fd, >=, 0);
3149
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3150
g_assert_cmpint(close(pipefds[1]), ==, 0);
3152
__attribute__((cleanup(cleanup_buffer)))
3153
buffer password = {};
3155
bool password_is_read = false;
3156
bool quit_now = false;
3157
__attribute__((cleanup(cleanup_queue)))
3158
task_queue *queue = create_queue();
3159
g_assert_nonnull(queue);
3161
task_context task = {
3162
.func=read_mandos_client_output,
3165
.password=&password,
3166
.password_is_read=&password_is_read,
3167
.quit_now=&quit_now,
3169
task.func(task, queue);
3170
g_assert_true(password_is_read);
3171
g_assert_cmpint((int)password.length, ==, 0);
3172
g_assert_false(quit_now);
3173
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3175
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3177
g_assert_cmpint(close(pipefds[0]), ==, -1);
3181
void test_read_mandos_client_output_once(__attribute__((unused))
3182
test_fixture *fixture,
3183
__attribute__((unused))
3184
gconstpointer user_data){
3185
__attribute__((cleanup(cleanup_close)))
3186
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3187
g_assert_cmpint(epoll_fd, >=, 0);
3190
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3192
const char dummy_test_password[] = "dummy test password";
3193
/* Start with a pre-allocated buffer */
3194
__attribute__((cleanup(cleanup_buffer)))
3196
.data=malloc(sizeof(dummy_test_password)),
3198
.allocated=sizeof(dummy_test_password),
3200
g_assert_nonnull(password.data);
3201
if(mlock(password.data, password.allocated) != 0){
3202
g_assert_true(errno == EPERM or errno == ENOMEM);
3205
bool password_is_read = false;
3206
bool quit_now = false;
3207
__attribute__((cleanup(cleanup_queue)))
3208
task_queue *queue = create_queue();
3209
g_assert_nonnull(queue);
3211
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3212
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3213
sizeof(dummy_test_password)),
3214
==, (int)sizeof(dummy_test_password));
3216
task_context task = {
3217
.func=read_mandos_client_output,
3220
.password=&password,
3221
.password_is_read=&password_is_read,
3222
.quit_now=&quit_now,
3224
task.func(task, queue);
3226
g_assert_false(password_is_read);
3227
g_assert_cmpint((int)password.length, ==,
3228
(int)sizeof(dummy_test_password));
3229
g_assert_nonnull(password.data);
3230
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3231
sizeof(dummy_test_password)), ==, 0);
3233
g_assert_false(quit_now);
3234
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3236
g_assert_nonnull(find_matching_task(queue, (task_context){
3237
.func=read_mandos_client_output,
3240
.password=&password,
3241
.password_is_read=&password_is_read,
3242
.quit_now=&quit_now,
3245
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3246
EPOLLIN | EPOLLRDHUP));
3248
g_assert_cmpint(close(pipefds[1]), ==, 0);
3252
void test_read_mandos_client_output_malloc(__attribute__((unused))
3253
test_fixture *fixture,
3254
__attribute__((unused))
3255
gconstpointer user_data){
3256
__attribute__((cleanup(cleanup_close)))
3257
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3258
g_assert_cmpint(epoll_fd, >=, 0);
3261
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3263
const char dummy_test_password[] = "dummy test password";
3264
/* Start with an empty buffer */
3265
__attribute__((cleanup(cleanup_buffer)))
3266
buffer password = {};
3268
bool password_is_read = false;
3269
bool quit_now = false;
3270
__attribute__((cleanup(cleanup_queue)))
3271
task_queue *queue = create_queue();
3272
g_assert_nonnull(queue);
3274
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3275
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3276
sizeof(dummy_test_password)),
3277
==, (int)sizeof(dummy_test_password));
3279
task_context task = {
3280
.func=read_mandos_client_output,
3283
.password=&password,
3284
.password_is_read=&password_is_read,
3285
.quit_now=&quit_now,
3287
task.func(task, queue);
3289
g_assert_false(password_is_read);
3290
g_assert_cmpint((int)password.length, ==,
3291
(int)sizeof(dummy_test_password));
3292
g_assert_nonnull(password.data);
3293
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3294
sizeof(dummy_test_password)), ==, 0);
3296
g_assert_false(quit_now);
3297
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3299
g_assert_nonnull(find_matching_task(queue, (task_context){
3300
.func=read_mandos_client_output,
3303
.password=&password,
3304
.password_is_read=&password_is_read,
3305
.quit_now=&quit_now,
3308
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3309
EPOLLIN | EPOLLRDHUP));
3311
g_assert_cmpint(close(pipefds[1]), ==, 0);
3315
void test_read_mandos_client_output_append(__attribute__((unused))
3316
test_fixture *fixture,
3317
__attribute__((unused))
3318
gconstpointer user_data){
3319
__attribute__((cleanup(cleanup_close)))
3320
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3321
g_assert_cmpint(epoll_fd, >=, 0);
3324
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3326
const char dummy_test_password[] = "dummy test password";
3327
__attribute__((cleanup(cleanup_buffer)))
3329
.data=malloc(PIPE_BUF),
3331
.allocated=PIPE_BUF,
3333
g_assert_nonnull(password.data);
3334
if(mlock(password.data, password.allocated) != 0){
3335
g_assert_true(errno == EPERM or errno == ENOMEM);
3338
memset(password.data, 'x', PIPE_BUF);
3339
char password_expected[PIPE_BUF];
3340
memcpy(password_expected, password.data, PIPE_BUF);
3342
bool password_is_read = false;
3343
bool quit_now = false;
3344
__attribute__((cleanup(cleanup_queue)))
3345
task_queue *queue = create_queue();
3346
g_assert_nonnull(queue);
3348
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3349
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3350
sizeof(dummy_test_password)),
3351
==, (int)sizeof(dummy_test_password));
3353
task_context task = {
3354
.func=read_mandos_client_output,
3357
.password=&password,
3358
.password_is_read=&password_is_read,
3359
.quit_now=&quit_now,
3361
task.func(task, queue);
3363
g_assert_false(password_is_read);
3364
g_assert_cmpint((int)password.length, ==,
3365
PIPE_BUF + sizeof(dummy_test_password));
3366
g_assert_nonnull(password.data);
3367
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3369
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3370
dummy_test_password,
3371
sizeof(dummy_test_password)), ==, 0);
3372
g_assert_false(quit_now);
3373
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3375
g_assert_nonnull(find_matching_task(queue, (task_context){
3376
.func=read_mandos_client_output,
3379
.password=&password,
3380
.password_is_read=&password_is_read,
3381
.quit_now=&quit_now,
3384
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3385
EPOLLIN | EPOLLRDHUP));
3388
static char *make_temporary_directory(void);
3390
static void test_add_inotify_dir_watch(__attribute__((unused))
3391
test_fixture *fixture,
3392
__attribute__((unused))
3393
gconstpointer user_data){
3394
__attribute__((cleanup(cleanup_close)))
3395
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3396
g_assert_cmpint(epoll_fd, >=, 0);
3397
__attribute__((cleanup(cleanup_queue)))
3398
task_queue *queue = create_queue();
3399
g_assert_nonnull(queue);
3400
__attribute__((cleanup(string_set_clear)))
3401
string_set cancelled_filenames = {};
3402
const mono_microsecs current_time = 0;
3404
bool quit_now = false;
3405
buffer password = {};
3406
bool mandos_client_exited = false;
3407
bool password_is_read = false;
3409
__attribute__((cleanup(cleanup_string)))
3410
char *tempdir = make_temporary_directory();
3411
g_assert_nonnull(tempdir);
3413
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3415
&cancelled_filenames,
3417
&mandos_client_exited,
3418
&password_is_read));
3420
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3422
const task_context *const added_read_task
3423
= find_matching_task(queue, (task_context){
3424
.func=read_inotify_event,
3426
.quit_now=&quit_now,
3427
.password=&password,
3429
.cancelled_filenames=&cancelled_filenames,
3430
.current_time=¤t_time,
3431
.mandos_client_exited=&mandos_client_exited,
3432
.password_is_read=&password_is_read,
3434
g_assert_nonnull(added_read_task);
3436
g_assert_cmpint(added_read_task->fd, >, 2);
3437
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3438
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3439
added_read_task->fd,
3440
EPOLLIN | EPOLLRDHUP));
3442
g_assert_cmpint(rmdir(tempdir), ==, 0);
3445
static char *make_temporary_directory(void){
3446
char *name = strdup("/tmp/mandosXXXXXX");
3447
g_assert_nonnull(name);
3448
char *result = mkdtemp(name);
3455
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3456
test_fixture *fixture,
3457
__attribute__((unused))
3458
gconstpointer user_data){
3459
__attribute__((cleanup(cleanup_close)))
3460
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3461
g_assert_cmpint(epoll_fd, >=, 0);
3462
__attribute__((cleanup(cleanup_queue)))
3463
task_queue *queue = create_queue();
3464
g_assert_nonnull(queue);
3465
__attribute__((cleanup(string_set_clear)))
3466
string_set cancelled_filenames = {};
3467
const mono_microsecs current_time = 0;
3469
bool quit_now = false;
3470
buffer password = {};
3471
bool mandos_client_exited = false;
3472
bool password_is_read = false;
3474
const char nonexistent_dir[] = "/nonexistent";
3476
FILE *real_stderr = stderr;
3477
FILE *devnull = fopen("/dev/null", "we");
3478
g_assert_nonnull(devnull);
3480
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3481
&password, nonexistent_dir,
3482
&cancelled_filenames,
3484
&mandos_client_exited,
3485
&password_is_read));
3486
stderr = real_stderr;
3487
g_assert_cmpint(fclose(devnull), ==, 0);
3489
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3492
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3493
test_fixture *fixture,
3494
__attribute__((unused))
3497
__attribute__((cleanup(cleanup_close)))
3498
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3499
g_assert_cmpint(epoll_fd, >=, 0);
3500
__attribute__((cleanup(cleanup_queue)))
3501
task_queue *queue = create_queue();
3502
g_assert_nonnull(queue);
3503
__attribute__((cleanup(string_set_clear)))
3504
string_set cancelled_filenames = {};
3505
const mono_microsecs current_time = 0;
3507
bool quit_now = false;
3508
buffer password = {};
3509
bool mandos_client_exited = false;
3510
bool password_is_read = false;
3512
const char not_a_directory[] = "/dev/tty";
3514
FILE *real_stderr = stderr;
3515
FILE *devnull = fopen("/dev/null", "we");
3516
g_assert_nonnull(devnull);
3518
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3519
&password, not_a_directory,
3520
&cancelled_filenames,
3522
&mandos_client_exited,
3523
&password_is_read));
3524
stderr = real_stderr;
3525
g_assert_cmpint(fclose(devnull), ==, 0);
3527
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3530
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3531
test_fixture *fixture,
3532
__attribute__((unused))
3535
__attribute__((cleanup(cleanup_close)))
3536
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3537
g_assert_cmpint(epoll_fd, >=, 0);
3538
__attribute__((cleanup(cleanup_queue)))
3539
task_queue *queue = create_queue();
3540
g_assert_nonnull(queue);
3541
__attribute__((cleanup(string_set_clear)))
3542
string_set cancelled_filenames = {};
3543
const mono_microsecs current_time = 0;
3545
bool quit_now = false;
3546
buffer password = {};
3547
bool mandos_client_exited = false;
3548
bool password_is_read = false;
3550
__attribute__((cleanup(cleanup_string)))
3551
char *tempdir = make_temporary_directory();
3552
g_assert_nonnull(tempdir);
3554
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3556
&cancelled_filenames,
3558
&mandos_client_exited,
3559
&password_is_read));
3561
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3563
const task_context *const added_read_task
3564
= find_matching_task(queue,
3565
(task_context){ .func=read_inotify_event });
3566
g_assert_nonnull(added_read_task);
3568
g_assert_cmpint(added_read_task->fd, >, 2);
3569
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3571
/* "sufficient to read at least one event." - inotify(7) */
3572
const size_t ievent_size = (sizeof(struct inotify_event)
3574
struct inotify_event *ievent = malloc(ievent_size);
3575
g_assert_nonnull(ievent);
3577
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3579
g_assert_cmpint(errno, ==, EAGAIN);
3583
g_assert_cmpint(rmdir(tempdir), ==, 0);
3586
static char *make_temporary_file_in_directory(const char
3590
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3591
test_fixture *fixture,
3592
__attribute__((unused))
3595
__attribute__((cleanup(cleanup_close)))
3596
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3597
g_assert_cmpint(epoll_fd, >=, 0);
3598
__attribute__((cleanup(cleanup_queue)))
3599
task_queue *queue = create_queue();
3600
g_assert_nonnull(queue);
3601
__attribute__((cleanup(string_set_clear)))
3602
string_set cancelled_filenames = {};
3603
const mono_microsecs current_time = 0;
3605
bool quit_now = false;
3606
buffer password = {};
3607
bool mandos_client_exited = false;
3608
bool password_is_read = false;
3610
__attribute__((cleanup(cleanup_string)))
3611
char *tempdir = make_temporary_directory();
3612
g_assert_nonnull(tempdir);
3614
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3616
&cancelled_filenames,
3618
&mandos_client_exited,
3619
&password_is_read));
3621
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3623
const task_context *const added_read_task
3624
= find_matching_task(queue,
3625
(task_context){ .func=read_inotify_event });
3626
g_assert_nonnull(added_read_task);
3628
g_assert_cmpint(added_read_task->fd, >, 2);
3629
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3631
__attribute__((cleanup(cleanup_string)))
3632
char *filename = make_temporary_file_in_directory(tempdir);
3633
g_assert_nonnull(filename);
3635
/* "sufficient to read at least one event." - inotify(7) */
3636
const size_t ievent_size = (sizeof(struct inotify_event)
3638
struct inotify_event *ievent = malloc(ievent_size);
3639
g_assert_nonnull(ievent);
3641
ssize_t read_size = 0;
3642
read_size = read(added_read_task->fd, ievent, ievent_size);
3644
g_assert_cmpint((int)read_size, >, 0);
3645
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3646
g_assert_cmpstr(ievent->name, ==, basename(filename));
3650
g_assert_cmpint(unlink(filename), ==, 0);
3651
g_assert_cmpint(rmdir(tempdir), ==, 0);
3654
static char *make_temporary_prefixed_file_in_directory(const char
3658
char *filename = NULL;
3659
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3661
g_assert_nonnull(filename);
3662
const int fd = mkostemp(filename, O_CLOEXEC);
3663
g_assert_cmpint(fd, >=, 0);
3664
g_assert_cmpint(close(fd), ==, 0);
3668
static char *make_temporary_file_in_directory(const char
3670
return make_temporary_prefixed_file_in_directory("temp", dir);
3674
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3675
test_fixture *fixture,
3676
__attribute__((unused))
3677
gconstpointer user_data){
3678
__attribute__((cleanup(cleanup_close)))
3679
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3680
g_assert_cmpint(epoll_fd, >=, 0);
3681
__attribute__((cleanup(cleanup_queue)))
3682
task_queue *queue = create_queue();
3683
g_assert_nonnull(queue);
3684
__attribute__((cleanup(string_set_clear)))
3685
string_set cancelled_filenames = {};
3686
const mono_microsecs current_time = 0;
3688
bool quit_now = false;
3689
buffer password = {};
3690
bool mandos_client_exited = false;
3691
bool password_is_read = false;
3693
__attribute__((cleanup(cleanup_string)))
3694
char *watchdir = make_temporary_directory();
3695
g_assert_nonnull(watchdir);
3697
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3698
&password, watchdir,
3699
&cancelled_filenames,
3701
&mandos_client_exited,
3702
&password_is_read));
3704
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3706
const task_context *const added_read_task
3707
= find_matching_task(queue,
3708
(task_context){ .func=read_inotify_event });
3709
g_assert_nonnull(added_read_task);
3711
g_assert_cmpint(added_read_task->fd, >, 2);
3712
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3714
char *sourcedir = make_temporary_directory();
3715
g_assert_nonnull(sourcedir);
3717
__attribute__((cleanup(cleanup_string)))
3718
char *filename = make_temporary_file_in_directory(sourcedir);
3719
g_assert_nonnull(filename);
3721
__attribute__((cleanup(cleanup_string)))
3722
char *targetfilename = NULL;
3723
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3724
basename(filename)), >, 0);
3725
g_assert_nonnull(targetfilename);
3727
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3728
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3731
/* "sufficient to read at least one event." - inotify(7) */
3732
const size_t ievent_size = (sizeof(struct inotify_event)
3734
struct inotify_event *ievent = malloc(ievent_size);
3735
g_assert_nonnull(ievent);
3737
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3739
g_assert_cmpint((int)read_size, >, 0);
3740
g_assert_true(ievent->mask & IN_MOVED_TO);
3741
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3745
g_assert_cmpint(unlink(targetfilename), ==, 0);
3746
g_assert_cmpint(rmdir(watchdir), ==, 0);
3750
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3751
test_fixture *fixture,
3752
__attribute__((unused))
3755
__attribute__((cleanup(cleanup_close)))
3756
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3757
g_assert_cmpint(epoll_fd, >=, 0);
3758
__attribute__((cleanup(cleanup_queue)))
3759
task_queue *queue = create_queue();
3760
g_assert_nonnull(queue);
3761
__attribute__((cleanup(string_set_clear)))
3762
string_set cancelled_filenames = {};
3763
const mono_microsecs current_time = 0;
3765
bool quit_now = false;
3766
buffer password = {};
3767
bool mandos_client_exited = false;
3768
bool password_is_read = false;
3770
__attribute__((cleanup(cleanup_string)))
3771
char *tempdir = make_temporary_directory();
3772
g_assert_nonnull(tempdir);
3774
__attribute__((cleanup(cleanup_string)))
3775
char *tempfilename = make_temporary_file_in_directory(tempdir);
3776
g_assert_nonnull(tempfilename);
3778
__attribute__((cleanup(cleanup_string)))
3779
char *targetdir = make_temporary_directory();
3780
g_assert_nonnull(targetdir);
3782
__attribute__((cleanup(cleanup_string)))
3783
char *targetfilename = NULL;
3784
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3785
basename(tempfilename)), >, 0);
3786
g_assert_nonnull(targetfilename);
3788
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3790
&cancelled_filenames,
3792
&mandos_client_exited,
3793
&password_is_read));
3795
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3797
const task_context *const added_read_task
3798
= find_matching_task(queue,
3799
(task_context){ .func=read_inotify_event });
3800
g_assert_nonnull(added_read_task);
3802
/* "sufficient to read at least one event." - inotify(7) */
3803
const size_t ievent_size = (sizeof(struct inotify_event)
3805
struct inotify_event *ievent = malloc(ievent_size);
3806
g_assert_nonnull(ievent);
3808
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3810
g_assert_cmpint((int)read_size, >, 0);
3811
g_assert_true(ievent->mask & IN_MOVED_FROM);
3812
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3816
g_assert_cmpint(unlink(targetfilename), ==, 0);
3817
g_assert_cmpint(rmdir(targetdir), ==, 0);
3818
g_assert_cmpint(rmdir(tempdir), ==, 0);
3822
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3823
test_fixture *fixture,
3824
__attribute__((unused))
3825
gconstpointer user_data){
3826
__attribute__((cleanup(cleanup_close)))
3827
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3828
g_assert_cmpint(epoll_fd, >=, 0);
3829
__attribute__((cleanup(cleanup_queue)))
3830
task_queue *queue = create_queue();
3831
g_assert_nonnull(queue);
3832
__attribute__((cleanup(string_set_clear)))
3833
string_set cancelled_filenames = {};
3834
const mono_microsecs current_time = 0;
3836
bool quit_now = false;
3837
buffer password = {};
3838
bool mandos_client_exited = false;
3839
bool password_is_read = false;
3841
__attribute__((cleanup(cleanup_string)))
3842
char *tempdir = make_temporary_directory();
3843
g_assert_nonnull(tempdir);
3845
__attribute__((cleanup(cleanup_string)))
3846
char *tempfile = make_temporary_file_in_directory(tempdir);
3847
g_assert_nonnull(tempfile);
3849
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3851
&cancelled_filenames,
3853
&mandos_client_exited,
3854
&password_is_read));
3855
g_assert_cmpint(unlink(tempfile), ==, 0);
3857
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3859
const task_context *const added_read_task
3860
= find_matching_task(queue,
3861
(task_context){ .func=read_inotify_event });
3862
g_assert_nonnull(added_read_task);
3864
g_assert_cmpint(added_read_task->fd, >, 2);
3865
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3867
/* "sufficient to read at least one event." - inotify(7) */
3868
const size_t ievent_size = (sizeof(struct inotify_event)
3870
struct inotify_event *ievent = malloc(ievent_size);
3871
g_assert_nonnull(ievent);
3873
ssize_t read_size = 0;
3874
read_size = read(added_read_task->fd, ievent, ievent_size);
3876
g_assert_cmpint((int)read_size, >, 0);
3877
g_assert_true(ievent->mask & IN_DELETE);
3878
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3882
g_assert_cmpint(rmdir(tempdir), ==, 0);
3886
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3887
test_fixture *fixture,
3888
__attribute__((unused))
3891
__attribute__((cleanup(cleanup_close)))
3892
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3893
g_assert_cmpint(epoll_fd, >=, 0);
3894
__attribute__((cleanup(cleanup_queue)))
3895
task_queue *queue = create_queue();
3896
g_assert_nonnull(queue);
3897
__attribute__((cleanup(string_set_clear)))
3898
string_set cancelled_filenames = {};
3899
const mono_microsecs current_time = 0;
3901
bool quit_now = false;
3902
buffer password = {};
3903
bool mandos_client_exited = false;
3904
bool password_is_read = false;
3906
__attribute__((cleanup(cleanup_string)))
3907
char *tempdir = make_temporary_directory();
3908
g_assert_nonnull(tempdir);
3910
__attribute__((cleanup(cleanup_string)))
3911
char *tempfile = make_temporary_file_in_directory(tempdir);
3912
g_assert_nonnull(tempfile);
3913
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3915
g_assert_cmpint(tempfile_fd, >, 2);
3917
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3919
&cancelled_filenames,
3921
&mandos_client_exited,
3922
&password_is_read));
3923
g_assert_cmpint(unlink(tempfile), ==, 0);
3925
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3927
const task_context *const added_read_task
3928
= find_matching_task(queue,
3929
(task_context){ .func=read_inotify_event });
3930
g_assert_nonnull(added_read_task);
3932
g_assert_cmpint(added_read_task->fd, >, 2);
3933
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3935
/* "sufficient to read at least one event." - inotify(7) */
3936
const size_t ievent_size = (sizeof(struct inotify_event)
3938
struct inotify_event *ievent = malloc(ievent_size);
3939
g_assert_nonnull(ievent);
3941
ssize_t read_size = 0;
3942
read_size = read(added_read_task->fd, ievent, ievent_size);
3944
g_assert_cmpint((int)read_size, >, 0);
3945
g_assert_true(ievent->mask & IN_DELETE);
3946
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3948
g_assert_cmpint(close(tempfile_fd), ==, 0);
3950
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3951
file not appear as an ievent, so we should not see it now. */
3952
read_size = read(added_read_task->fd, ievent, ievent_size);
3953
g_assert_cmpint((int)read_size, ==, -1);
3954
g_assert_true(errno == EAGAIN);
3958
g_assert_cmpint(rmdir(tempdir), ==, 0);
3961
static void test_read_inotify_event_readerror(__attribute__((unused))
3962
test_fixture *fixture,
3963
__attribute__((unused))
3966
__attribute__((cleanup(cleanup_close)))
3967
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3968
g_assert_cmpint(epoll_fd, >=, 0);
3969
const mono_microsecs current_time = 0;
3971
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3972
const int fd = open("/proc/self/mem",
3973
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3975
bool quit_now = false;
3976
__attribute__((cleanup(cleanup_queue)))
3977
task_queue *queue = create_queue();
3978
g_assert_nonnull(queue);
3980
task_context task = {
3981
.func=read_inotify_event,
3984
.quit_now=&quit_now,
3985
.filename=strdup("/nonexistent"),
3986
.cancelled_filenames = &(string_set){},
3988
.current_time=¤t_time,
3990
g_assert_nonnull(task.filename);
3991
run_task_with_stderr_to_dev_null(task, queue);
3992
g_assert_true(quit_now);
3993
g_assert_true(queue->next_run == 0);
3994
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3996
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3998
g_assert_cmpint(close(fd), ==, -1);
4001
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4002
test_fixture *fixture,
4003
__attribute__((unused))
4006
const mono_microsecs current_time = 17;
4009
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4010
const int epoll_fd = pipefds[0]; /* This will obviously fail */
4012
bool quit_now = false;
4013
buffer password = {};
4014
bool mandos_client_exited = false;
4015
bool password_is_read = false;
4016
__attribute__((cleanup(cleanup_queue)))
4017
task_queue *queue = create_queue();
4018
g_assert_nonnull(queue);
4020
task_context task = {
4021
.func=read_inotify_event,
4024
.quit_now=&quit_now,
4025
.password=&password,
4026
.filename=strdup("/nonexistent"),
4027
.cancelled_filenames = &(string_set){},
4029
.current_time=¤t_time,
4030
.mandos_client_exited=&mandos_client_exited,
4031
.password_is_read=&password_is_read,
4033
g_assert_nonnull(task.filename);
4034
run_task_with_stderr_to_dev_null(task, queue);
4036
g_assert_nonnull(find_matching_task(queue, task));
4037
g_assert_true(queue->next_run == 1000000 + current_time);
4039
g_assert_cmpint(close(pipefds[0]), ==, 0);
4040
g_assert_cmpint(close(pipefds[1]), ==, 0);
4043
static void test_read_inotify_event_nodata(__attribute__((unused))
4044
test_fixture *fixture,
4045
__attribute__((unused))
4046
gconstpointer user_data){
4047
__attribute__((cleanup(cleanup_close)))
4048
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4049
g_assert_cmpint(epoll_fd, >=, 0);
4050
const mono_microsecs current_time = 0;
4053
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4055
bool quit_now = false;
4056
buffer password = {};
4057
bool mandos_client_exited = false;
4058
bool password_is_read = false;
4059
__attribute__((cleanup(cleanup_queue)))
4060
task_queue *queue = create_queue();
4061
g_assert_nonnull(queue);
4063
task_context task = {
4064
.func=read_inotify_event,
4067
.quit_now=&quit_now,
4068
.password=&password,
4069
.filename=strdup("/nonexistent"),
4070
.cancelled_filenames = &(string_set){},
4072
.current_time=¤t_time,
4073
.mandos_client_exited=&mandos_client_exited,
4074
.password_is_read=&password_is_read,
4076
g_assert_nonnull(task.filename);
4077
task.func(task, queue);
4078
g_assert_false(quit_now);
4079
g_assert_true(queue->next_run == 0);
4080
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4082
g_assert_nonnull(find_matching_task(queue, (task_context){
4083
.func=read_inotify_event,
4086
.quit_now=&quit_now,
4087
.password=&password,
4088
.filename=task.filename,
4089
.cancelled_filenames=task.cancelled_filenames,
4090
.current_time=¤t_time,
4091
.mandos_client_exited=&mandos_client_exited,
4092
.password_is_read=&password_is_read,
4095
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4096
EPOLLIN | EPOLLRDHUP));
4098
g_assert_cmpint(close(pipefds[1]), ==, 0);
4101
static void test_read_inotify_event_eof(__attribute__((unused))
4102
test_fixture *fixture,
4103
__attribute__((unused))
4104
gconstpointer user_data){
4105
__attribute__((cleanup(cleanup_close)))
4106
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4107
g_assert_cmpint(epoll_fd, >=, 0);
4108
const mono_microsecs current_time = 0;
4111
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4112
g_assert_cmpint(close(pipefds[1]), ==, 0);
4114
bool quit_now = false;
4115
buffer password = {};
4116
__attribute__((cleanup(cleanup_queue)))
4117
task_queue *queue = create_queue();
4118
g_assert_nonnull(queue);
4120
task_context task = {
4121
.func=read_inotify_event,
4124
.quit_now=&quit_now,
4125
.password=&password,
4126
.filename=strdup("/nonexistent"),
4127
.cancelled_filenames = &(string_set){},
4129
.current_time=¤t_time,
4131
run_task_with_stderr_to_dev_null(task, queue);
4132
g_assert_true(quit_now);
4133
g_assert_true(queue->next_run == 0);
4134
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4136
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4138
g_assert_cmpint(close(pipefds[0]), ==, -1);
4142
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4143
test_fixture *fixture,
4144
__attribute__((unused))
4145
gconstpointer user_data){
4146
__attribute__((cleanup(cleanup_close)))
4147
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4148
g_assert_cmpint(epoll_fd, >=, 0);
4149
const mono_microsecs current_time = 0;
4152
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4154
/* "sufficient to read at least one event." - inotify(7) */
4155
const size_t ievent_max_size = (sizeof(struct inotify_event)
4157
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4159
struct inotify_event event;
4160
char name_buffer[NAME_MAX + 1];
4162
struct inotify_event *const ievent = &ievent_buffer.event;
4164
const char dummy_file_name[] = "ask.dummy_file_name";
4165
ievent->mask = IN_CLOSE_WRITE;
4166
ievent->len = sizeof(dummy_file_name);
4167
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4168
const size_t ievent_size = (sizeof(struct inotify_event)
4169
+ sizeof(dummy_file_name));
4170
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4172
g_assert_cmpint(close(pipefds[1]), ==, 0);
4174
bool quit_now = false;
4175
buffer password = {};
4176
bool mandos_client_exited = false;
4177
bool password_is_read = false;
4178
__attribute__((cleanup(cleanup_queue)))
4179
task_queue *queue = create_queue();
4180
g_assert_nonnull(queue);
4182
task_context task = {
4183
.func=read_inotify_event,
4186
.quit_now=&quit_now,
4187
.password=&password,
4188
.filename=strdup("/nonexistent"),
4189
.cancelled_filenames = &(string_set){},
4191
.current_time=¤t_time,
4192
.mandos_client_exited=&mandos_client_exited,
4193
.password_is_read=&password_is_read,
4195
task.func(task, queue);
4196
g_assert_false(quit_now);
4197
g_assert_true(queue->next_run != 0);
4198
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4200
g_assert_nonnull(find_matching_task(queue, (task_context){
4201
.func=read_inotify_event,
4204
.quit_now=&quit_now,
4205
.password=&password,
4206
.filename=task.filename,
4207
.cancelled_filenames=task.cancelled_filenames,
4208
.current_time=¤t_time,
4209
.mandos_client_exited=&mandos_client_exited,
4210
.password_is_read=&password_is_read,
4213
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4214
EPOLLIN | EPOLLRDHUP));
4216
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4218
__attribute__((cleanup(cleanup_string)))
4219
char *filename = NULL;
4220
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4221
dummy_file_name), >, 0);
4222
g_assert_nonnull(filename);
4223
g_assert_nonnull(find_matching_task(queue, (task_context){
4224
.func=open_and_parse_question,
4227
.question_filename=filename,
4228
.password=&password,
4229
.cancelled_filenames=task.cancelled_filenames,
4230
.current_time=¤t_time,
4231
.mandos_client_exited=&mandos_client_exited,
4232
.password_is_read=&password_is_read,
4237
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4238
test_fixture *fixture,
4239
__attribute__((unused))
4240
gconstpointer user_data){
4241
__attribute__((cleanup(cleanup_close)))
4242
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4243
g_assert_cmpint(epoll_fd, >=, 0);
4244
const mono_microsecs current_time = 0;
4247
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4249
/* "sufficient to read at least one event." - inotify(7) */
4250
const size_t ievent_max_size = (sizeof(struct inotify_event)
4252
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4254
struct inotify_event event;
4255
char name_buffer[NAME_MAX + 1];
4257
struct inotify_event *const ievent = &ievent_buffer.event;
4259
const char dummy_file_name[] = "ask.dummy_file_name";
4260
ievent->mask = IN_MOVED_TO;
4261
ievent->len = sizeof(dummy_file_name);
4262
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4263
const size_t ievent_size = (sizeof(struct inotify_event)
4264
+ sizeof(dummy_file_name));
4265
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4267
g_assert_cmpint(close(pipefds[1]), ==, 0);
4269
bool quit_now = false;
4270
buffer password = {};
4271
bool mandos_client_exited = false;
4272
bool password_is_read = false;
4273
__attribute__((cleanup(cleanup_queue)))
4274
task_queue *queue = create_queue();
4275
g_assert_nonnull(queue);
4277
task_context task = {
4278
.func=read_inotify_event,
4281
.quit_now=&quit_now,
4282
.password=&password,
4283
.filename=strdup("/nonexistent"),
4284
.cancelled_filenames = &(string_set){},
4286
.current_time=¤t_time,
4287
.mandos_client_exited=&mandos_client_exited,
4288
.password_is_read=&password_is_read,
4290
task.func(task, queue);
4291
g_assert_false(quit_now);
4292
g_assert_true(queue->next_run != 0);
4293
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4295
g_assert_nonnull(find_matching_task(queue, (task_context){
4296
.func=read_inotify_event,
4299
.quit_now=&quit_now,
4300
.password=&password,
4301
.filename=task.filename,
4302
.cancelled_filenames=task.cancelled_filenames,
4303
.current_time=¤t_time,
4304
.mandos_client_exited=&mandos_client_exited,
4305
.password_is_read=&password_is_read,
4308
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4309
EPOLLIN | EPOLLRDHUP));
4311
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4313
__attribute__((cleanup(cleanup_string)))
4314
char *filename = NULL;
4315
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4316
dummy_file_name), >, 0);
4317
g_assert_nonnull(filename);
4318
g_assert_nonnull(find_matching_task(queue, (task_context){
4319
.func=open_and_parse_question,
4322
.question_filename=filename,
4323
.password=&password,
4324
.cancelled_filenames=task.cancelled_filenames,
4325
.current_time=¤t_time,
4326
.mandos_client_exited=&mandos_client_exited,
4327
.password_is_read=&password_is_read,
4332
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4333
test_fixture *fixture,
4334
__attribute__((unused))
4335
gconstpointer user_data){
4336
__attribute__((cleanup(cleanup_close)))
4337
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4338
g_assert_cmpint(epoll_fd, >=, 0);
4339
__attribute__((cleanup(string_set_clear)))
4340
string_set cancelled_filenames = {};
4341
const mono_microsecs current_time = 0;
4344
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4346
/* "sufficient to read at least one event." - inotify(7) */
4347
const size_t ievent_max_size = (sizeof(struct inotify_event)
4349
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4351
struct inotify_event event;
4352
char name_buffer[NAME_MAX + 1];
4354
struct inotify_event *const ievent = &ievent_buffer.event;
4356
const char dummy_file_name[] = "ask.dummy_file_name";
4357
ievent->mask = IN_MOVED_FROM;
4358
ievent->len = sizeof(dummy_file_name);
4359
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4360
const size_t ievent_size = (sizeof(struct inotify_event)
4361
+ sizeof(dummy_file_name));
4362
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4364
g_assert_cmpint(close(pipefds[1]), ==, 0);
4366
bool quit_now = false;
4367
buffer password = {};
4368
bool mandos_client_exited = false;
4369
bool password_is_read = false;
4370
__attribute__((cleanup(cleanup_queue)))
4371
task_queue *queue = create_queue();
4372
g_assert_nonnull(queue);
4374
task_context task = {
4375
.func=read_inotify_event,
4378
.quit_now=&quit_now,
4379
.password=&password,
4380
.filename=strdup("/nonexistent"),
4381
.cancelled_filenames=&cancelled_filenames,
4382
.current_time=¤t_time,
4383
.mandos_client_exited=&mandos_client_exited,
4384
.password_is_read=&password_is_read,
4386
task.func(task, queue);
4387
g_assert_false(quit_now);
4388
g_assert_true(queue->next_run == 0);
4389
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4391
g_assert_nonnull(find_matching_task(queue, (task_context){
4392
.func=read_inotify_event,
4395
.quit_now=&quit_now,
4396
.password=&password,
4397
.filename=task.filename,
4398
.cancelled_filenames=&cancelled_filenames,
4399
.current_time=¤t_time,
4400
.mandos_client_exited=&mandos_client_exited,
4401
.password_is_read=&password_is_read,
4404
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4405
EPOLLIN | EPOLLRDHUP));
4407
__attribute__((cleanup(cleanup_string)))
4408
char *filename = NULL;
4409
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4410
dummy_file_name), >, 0);
4411
g_assert_nonnull(filename);
4412
g_assert_true(string_set_contains(*task.cancelled_filenames,
4416
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4417
test_fixture *fixture,
4418
__attribute__((unused))
4421
__attribute__((cleanup(cleanup_close)))
4422
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4423
g_assert_cmpint(epoll_fd, >=, 0);
4424
__attribute__((cleanup(string_set_clear)))
4425
string_set cancelled_filenames = {};
4426
const mono_microsecs current_time = 0;
4429
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4431
/* "sufficient to read at least one event." - inotify(7) */
4432
const size_t ievent_max_size = (sizeof(struct inotify_event)
4434
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4436
struct inotify_event event;
4437
char name_buffer[NAME_MAX + 1];
4439
struct inotify_event *const ievent = &ievent_buffer.event;
4441
const char dummy_file_name[] = "ask.dummy_file_name";
4442
ievent->mask = IN_DELETE;
4443
ievent->len = sizeof(dummy_file_name);
4444
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4445
const size_t ievent_size = (sizeof(struct inotify_event)
4446
+ sizeof(dummy_file_name));
4447
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4449
g_assert_cmpint(close(pipefds[1]), ==, 0);
4451
bool quit_now = false;
4452
buffer password = {};
4453
bool mandos_client_exited = false;
4454
bool password_is_read = false;
4455
__attribute__((cleanup(cleanup_queue)))
4456
task_queue *queue = create_queue();
4457
g_assert_nonnull(queue);
4459
task_context task = {
4460
.func=read_inotify_event,
4463
.quit_now=&quit_now,
4464
.password=&password,
4465
.filename=strdup("/nonexistent"),
4466
.cancelled_filenames=&cancelled_filenames,
4467
.current_time=¤t_time,
4468
.mandos_client_exited=&mandos_client_exited,
4469
.password_is_read=&password_is_read,
4471
task.func(task, queue);
4472
g_assert_false(quit_now);
4473
g_assert_true(queue->next_run == 0);
4474
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4476
g_assert_nonnull(find_matching_task(queue, (task_context){
4477
.func=read_inotify_event,
4480
.quit_now=&quit_now,
4481
.password=&password,
4482
.filename=task.filename,
4483
.cancelled_filenames=&cancelled_filenames,
4484
.current_time=¤t_time,
4485
.mandos_client_exited=&mandos_client_exited,
4486
.password_is_read=&password_is_read,
4489
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4490
EPOLLIN | EPOLLRDHUP));
4492
__attribute__((cleanup(cleanup_string)))
4493
char *filename = NULL;
4494
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4495
dummy_file_name), >, 0);
4496
g_assert_nonnull(filename);
4497
g_assert_true(string_set_contains(*task.cancelled_filenames,
4502
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4503
test_fixture *fixture,
4504
__attribute__((unused))
4507
__attribute__((cleanup(cleanup_close)))
4508
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4509
g_assert_cmpint(epoll_fd, >=, 0);
4510
const mono_microsecs current_time = 0;
4513
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4515
/* "sufficient to read at least one event." - inotify(7) */
4516
const size_t ievent_max_size = (sizeof(struct inotify_event)
4518
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4520
struct inotify_event event;
4521
char name_buffer[NAME_MAX + 1];
4523
struct inotify_event *const ievent = &ievent_buffer.event;
4525
const char dummy_file_name[] = "ignored.dummy_file_name";
4526
ievent->mask = IN_CLOSE_WRITE;
4527
ievent->len = sizeof(dummy_file_name);
4528
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4529
const size_t ievent_size = (sizeof(struct inotify_event)
4530
+ sizeof(dummy_file_name));
4531
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4533
g_assert_cmpint(close(pipefds[1]), ==, 0);
4535
bool quit_now = false;
4536
buffer password = {};
4537
bool mandos_client_exited = false;
4538
bool password_is_read = false;
4539
__attribute__((cleanup(cleanup_queue)))
4540
task_queue *queue = create_queue();
4541
g_assert_nonnull(queue);
4543
task_context task = {
4544
.func=read_inotify_event,
4547
.quit_now=&quit_now,
4548
.password=&password,
4549
.filename=strdup("/nonexistent"),
4550
.cancelled_filenames = &(string_set){},
4552
.current_time=¤t_time,
4553
.mandos_client_exited=&mandos_client_exited,
4554
.password_is_read=&password_is_read,
4556
task.func(task, queue);
4557
g_assert_false(quit_now);
4558
g_assert_true(queue->next_run == 0);
4559
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4561
g_assert_nonnull(find_matching_task(queue, (task_context){
4562
.func=read_inotify_event,
4565
.quit_now=&quit_now,
4566
.password=&password,
4567
.filename=task.filename,
4568
.cancelled_filenames=task.cancelled_filenames,
4569
.current_time=¤t_time,
4570
.mandos_client_exited=&mandos_client_exited,
4571
.password_is_read=&password_is_read,
4574
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4575
EPOLLIN | EPOLLRDHUP));
4579
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4580
test_fixture *fixture,
4581
__attribute__((unused))
4582
gconstpointer user_data){
4583
__attribute__((cleanup(cleanup_close)))
4584
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4585
g_assert_cmpint(epoll_fd, >=, 0);
4586
const mono_microsecs current_time = 0;
4589
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4591
/* "sufficient to read at least one event." - inotify(7) */
4592
const size_t ievent_max_size = (sizeof(struct inotify_event)
4594
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4596
struct inotify_event event;
4597
char name_buffer[NAME_MAX + 1];
4599
struct inotify_event *const ievent = &ievent_buffer.event;
4601
const char dummy_file_name[] = "ignored.dummy_file_name";
4602
ievent->mask = IN_MOVED_TO;
4603
ievent->len = sizeof(dummy_file_name);
4604
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4605
const size_t ievent_size = (sizeof(struct inotify_event)
4606
+ sizeof(dummy_file_name));
4607
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4609
g_assert_cmpint(close(pipefds[1]), ==, 0);
4611
bool quit_now = false;
4612
buffer password = {};
4613
bool mandos_client_exited = false;
4614
bool password_is_read = false;
4615
__attribute__((cleanup(cleanup_queue)))
4616
task_queue *queue = create_queue();
4617
g_assert_nonnull(queue);
4619
task_context task = {
4620
.func=read_inotify_event,
4623
.quit_now=&quit_now,
4624
.password=&password,
4625
.filename=strdup("/nonexistent"),
4626
.cancelled_filenames = &(string_set){},
4628
.current_time=¤t_time,
4629
.mandos_client_exited=&mandos_client_exited,
4630
.password_is_read=&password_is_read,
4632
task.func(task, queue);
4633
g_assert_false(quit_now);
4634
g_assert_true(queue->next_run == 0);
4635
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4637
g_assert_nonnull(find_matching_task(queue, (task_context){
4638
.func=read_inotify_event,
4641
.quit_now=&quit_now,
4642
.password=&password,
4643
.filename=task.filename,
4644
.cancelled_filenames=task.cancelled_filenames,
4645
.current_time=¤t_time,
4646
.mandos_client_exited=&mandos_client_exited,
4647
.password_is_read=&password_is_read,
4650
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4651
EPOLLIN | EPOLLRDHUP));
4655
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4656
test_fixture *fixture,
4657
__attribute__((unused))
4660
__attribute__((cleanup(cleanup_close)))
4661
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4662
g_assert_cmpint(epoll_fd, >=, 0);
4663
__attribute__((cleanup(string_set_clear)))
4664
string_set cancelled_filenames = {};
4665
const mono_microsecs current_time = 0;
4668
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4670
/* "sufficient to read at least one event." - inotify(7) */
4671
const size_t ievent_max_size = (sizeof(struct inotify_event)
4673
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4675
struct inotify_event event;
4676
char name_buffer[NAME_MAX + 1];
4678
struct inotify_event *const ievent = &ievent_buffer.event;
4680
const char dummy_file_name[] = "ignored.dummy_file_name";
4681
ievent->mask = IN_MOVED_FROM;
4682
ievent->len = sizeof(dummy_file_name);
4683
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4684
const size_t ievent_size = (sizeof(struct inotify_event)
4685
+ sizeof(dummy_file_name));
4686
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4688
g_assert_cmpint(close(pipefds[1]), ==, 0);
4690
bool quit_now = false;
4691
buffer password = {};
4692
bool mandos_client_exited = false;
4693
bool password_is_read = false;
4694
__attribute__((cleanup(cleanup_queue)))
4695
task_queue *queue = create_queue();
4696
g_assert_nonnull(queue);
4698
task_context task = {
4699
.func=read_inotify_event,
4702
.quit_now=&quit_now,
4703
.password=&password,
4704
.filename=strdup("/nonexistent"),
4705
.cancelled_filenames=&cancelled_filenames,
4706
.current_time=¤t_time,
4707
.mandos_client_exited=&mandos_client_exited,
4708
.password_is_read=&password_is_read,
4710
task.func(task, queue);
4711
g_assert_false(quit_now);
4712
g_assert_true(queue->next_run == 0);
4713
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4715
g_assert_nonnull(find_matching_task(queue, (task_context){
4716
.func=read_inotify_event,
4719
.quit_now=&quit_now,
4720
.password=&password,
4721
.filename=task.filename,
4722
.cancelled_filenames=&cancelled_filenames,
4723
.current_time=¤t_time,
4724
.mandos_client_exited=&mandos_client_exited,
4725
.password_is_read=&password_is_read,
4728
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4729
EPOLLIN | EPOLLRDHUP));
4731
__attribute__((cleanup(cleanup_string)))
4732
char *filename = NULL;
4733
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4734
dummy_file_name), >, 0);
4735
g_assert_nonnull(filename);
4736
g_assert_false(string_set_contains(cancelled_filenames, filename));
4740
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4741
test_fixture *fixture,
4742
__attribute__((unused))
4745
__attribute__((cleanup(cleanup_close)))
4746
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4747
g_assert_cmpint(epoll_fd, >=, 0);
4748
__attribute__((cleanup(string_set_clear)))
4749
string_set cancelled_filenames = {};
4750
const mono_microsecs current_time = 0;
4753
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4755
/* "sufficient to read at least one event." - inotify(7) */
4756
const size_t ievent_max_size = (sizeof(struct inotify_event)
4758
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4760
struct inotify_event event;
4761
char name_buffer[NAME_MAX + 1];
4763
struct inotify_event *const ievent = &ievent_buffer.event;
4765
const char dummy_file_name[] = "ignored.dummy_file_name";
4766
ievent->mask = IN_DELETE;
4767
ievent->len = sizeof(dummy_file_name);
4768
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4769
const size_t ievent_size = (sizeof(struct inotify_event)
4770
+ sizeof(dummy_file_name));
4771
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4773
g_assert_cmpint(close(pipefds[1]), ==, 0);
4775
bool quit_now = false;
4776
buffer password = {};
4777
bool mandos_client_exited = false;
4778
bool password_is_read = false;
4779
__attribute__((cleanup(cleanup_queue)))
4780
task_queue *queue = create_queue();
4781
g_assert_nonnull(queue);
4783
task_context task = {
4784
.func=read_inotify_event,
4787
.quit_now=&quit_now,
4788
.password=&password,
4789
.filename=strdup("/nonexistent"),
4790
.cancelled_filenames=&cancelled_filenames,
4791
.current_time=¤t_time,
4792
.mandos_client_exited=&mandos_client_exited,
4793
.password_is_read=&password_is_read,
4795
task.func(task, queue);
4796
g_assert_false(quit_now);
4797
g_assert_true(queue->next_run == 0);
4798
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4800
g_assert_nonnull(find_matching_task(queue, (task_context){
4801
.func=read_inotify_event,
4804
.quit_now=&quit_now,
4805
.password=&password,
4806
.filename=task.filename,
4807
.cancelled_filenames=&cancelled_filenames,
4808
.current_time=¤t_time,
4809
.mandos_client_exited=&mandos_client_exited,
4810
.password_is_read=&password_is_read,
4813
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4814
EPOLLIN | EPOLLRDHUP));
4816
__attribute__((cleanup(cleanup_string)))
4817
char *filename = NULL;
4818
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4819
dummy_file_name), >, 0);
4820
g_assert_nonnull(filename);
4821
g_assert_false(string_set_contains(cancelled_filenames, filename));
4825
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4826
test_fixture *fixture,
4827
__attribute__((unused))
4828
gconstpointer user_data){
4829
__attribute__((cleanup(cleanup_close)))
4830
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4831
g_assert_cmpint(epoll_fd, >=, 0);
4832
__attribute__((cleanup(string_set_clear)))
4833
string_set cancelled_filenames = {};
4834
bool mandos_client_exited = false;
4835
bool password_is_read = false;
4836
__attribute__((cleanup(cleanup_queue)))
4837
task_queue *queue = create_queue();
4838
g_assert_nonnull(queue);
4840
char *const filename = strdup("/nonexistent");
4841
g_assert_nonnull(filename);
4842
task_context task = {
4843
.func=open_and_parse_question,
4844
.question_filename=filename,
4846
.password=(buffer[]){{}},
4848
.cancelled_filenames=&cancelled_filenames,
4849
.current_time=(mono_microsecs[]){0},
4850
.mandos_client_exited=&mandos_client_exited,
4851
.password_is_read=&password_is_read,
4853
task.func(task, queue);
4854
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4857
static void test_open_and_parse_question_EIO(__attribute__((unused))
4858
test_fixture *fixture,
4859
__attribute__((unused))
4860
gconstpointer user_data){
4861
__attribute__((cleanup(cleanup_close)))
4862
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4863
g_assert_cmpint(epoll_fd, >=, 0);
4864
__attribute__((cleanup(string_set_clear)))
4865
string_set cancelled_filenames = {};
4866
buffer password = {};
4867
bool mandos_client_exited = false;
4868
bool password_is_read = false;
4869
__attribute__((cleanup(cleanup_queue)))
4870
task_queue *queue = create_queue();
4871
g_assert_nonnull(queue);
4872
const mono_microsecs current_time = 0;
4874
char *filename = strdup("/proc/self/mem");
4875
task_context task = {
4876
.func=open_and_parse_question,
4877
.question_filename=filename,
4879
.password=&password,
4881
.cancelled_filenames=&cancelled_filenames,
4882
.current_time=¤t_time,
4883
.mandos_client_exited=&mandos_client_exited,
4884
.password_is_read=&password_is_read,
4886
run_task_with_stderr_to_dev_null(task, queue);
4887
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4891
test_open_and_parse_question_parse_error(__attribute__((unused))
4892
test_fixture *fixture,
4893
__attribute__((unused))
4894
gconstpointer user_data){
4895
__attribute__((cleanup(cleanup_close)))
4896
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4897
g_assert_cmpint(epoll_fd, >=, 0);
4898
__attribute__((cleanup(string_set_clear)))
4899
string_set cancelled_filenames = {};
4900
__attribute__((cleanup(cleanup_queue)))
4901
task_queue *queue = create_queue();
4902
g_assert_nonnull(queue);
4904
__attribute__((cleanup(cleanup_string)))
4905
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4906
g_assert_nonnull(tempfilename);
4907
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4908
g_assert_cmpint(tempfile, >, 0);
4909
const char bad_data[] = "this is bad syntax\n";
4910
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4911
==, sizeof(bad_data));
4912
g_assert_cmpint(close(tempfile), ==, 0);
4914
char *const filename = strdup(tempfilename);
4915
g_assert_nonnull(filename);
4916
task_context task = {
4917
.func=open_and_parse_question,
4918
.question_filename=filename,
4920
.password=(buffer[]){{}},
4922
.cancelled_filenames=&cancelled_filenames,
4923
.current_time=(mono_microsecs[]){0},
4924
.mandos_client_exited=(bool[]){false},
4925
.password_is_read=(bool[]){false},
4927
run_task_with_stderr_to_dev_null(task, queue);
4929
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4931
g_assert_cmpint(unlink(tempfilename), ==, 0);
4935
void test_open_and_parse_question_nosocket(__attribute__((unused))
4936
test_fixture *fixture,
4937
__attribute__((unused))
4938
gconstpointer user_data){
4939
__attribute__((cleanup(cleanup_close)))
4940
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4941
g_assert_cmpint(epoll_fd, >=, 0);
4942
__attribute__((cleanup(string_set_clear)))
4943
string_set cancelled_filenames = {};
4944
__attribute__((cleanup(cleanup_queue)))
4945
task_queue *queue = create_queue();
4946
g_assert_nonnull(queue);
4948
__attribute__((cleanup(cleanup_string)))
4949
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4950
g_assert_nonnull(tempfilename);
4951
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4952
g_assert_cmpint(questionfile, >, 0);
4953
FILE *qf = fdopen(questionfile, "w");
4954
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4955
g_assert_cmpint(fclose(qf), ==, 0);
4957
char *const filename = strdup(tempfilename);
4958
g_assert_nonnull(filename);
4959
task_context task = {
4960
.func=open_and_parse_question,
4961
.question_filename=filename,
4963
.password=(buffer[]){{}},
4965
.cancelled_filenames=&cancelled_filenames,
4966
.current_time=(mono_microsecs[]){0},
4967
.mandos_client_exited=(bool[]){false},
4968
.password_is_read=(bool[]){false},
4970
run_task_with_stderr_to_dev_null(task, queue);
4971
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4973
g_assert_cmpint(unlink(tempfilename), ==, 0);
4977
void test_open_and_parse_question_badsocket(__attribute__((unused))
4978
test_fixture *fixture,
4979
__attribute__((unused))
4980
gconstpointer user_data){
4981
__attribute__((cleanup(cleanup_close)))
4982
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4983
g_assert_cmpint(epoll_fd, >=, 0);
4984
__attribute__((cleanup(string_set_clear)))
4985
string_set cancelled_filenames = {};
4986
__attribute__((cleanup(cleanup_queue)))
4987
task_queue *queue = create_queue();
4988
g_assert_nonnull(queue);
4990
__attribute__((cleanup(cleanup_string)))
4991
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4992
g_assert_nonnull(tempfilename);
4993
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4994
g_assert_cmpint(questionfile, >, 0);
4995
FILE *qf = fdopen(questionfile, "w");
4996
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4997
g_assert_cmpint(fclose(qf), ==, 0);
4999
char *const filename = strdup(tempfilename);
5000
g_assert_nonnull(filename);
5001
task_context task = {
5002
.func=open_and_parse_question,
5003
.question_filename=filename,
5005
.password=(buffer[]){{}},
5007
.cancelled_filenames=&cancelled_filenames,
5008
.current_time=(mono_microsecs[]){0},
5009
.mandos_client_exited=(bool[]){false},
5010
.password_is_read=(bool[]){false},
5012
run_task_with_stderr_to_dev_null(task, queue);
5013
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5015
g_assert_cmpint(unlink(tempfilename), ==, 0);
5019
void test_open_and_parse_question_nopid(__attribute__((unused))
5020
test_fixture *fixture,
5021
__attribute__((unused))
5022
gconstpointer user_data){
5023
__attribute__((cleanup(cleanup_close)))
5024
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5025
g_assert_cmpint(epoll_fd, >=, 0);
5026
__attribute__((cleanup(string_set_clear)))
5027
string_set cancelled_filenames = {};
5028
__attribute__((cleanup(cleanup_queue)))
5029
task_queue *queue = create_queue();
5030
g_assert_nonnull(queue);
5032
__attribute__((cleanup(cleanup_string)))
5033
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5034
g_assert_nonnull(tempfilename);
5035
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5036
g_assert_cmpint(questionfile, >, 0);
5037
FILE *qf = fdopen(questionfile, "w");
5038
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5039
g_assert_cmpint(fclose(qf), ==, 0);
5041
char *const filename = strdup(tempfilename);
5042
g_assert_nonnull(filename);
5043
task_context task = {
5044
.func=open_and_parse_question,
5045
.question_filename=filename,
5047
.password=(buffer[]){{}},
5049
.cancelled_filenames=&cancelled_filenames,
5050
.current_time=(mono_microsecs[]){0},
5051
.mandos_client_exited=(bool[]){false},
5052
.password_is_read=(bool[]){false},
5054
run_task_with_stderr_to_dev_null(task, queue);
5055
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5057
g_assert_cmpint(unlink(tempfilename), ==, 0);
5061
void test_open_and_parse_question_badpid(__attribute__((unused))
5062
test_fixture *fixture,
5063
__attribute__((unused))
5064
gconstpointer user_data){
5065
__attribute__((cleanup(cleanup_close)))
5066
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5067
g_assert_cmpint(epoll_fd, >=, 0);
5068
__attribute__((cleanup(string_set_clear)))
5069
string_set cancelled_filenames = {};
5070
__attribute__((cleanup(cleanup_queue)))
5071
task_queue *queue = create_queue();
5072
g_assert_nonnull(queue);
5074
__attribute__((cleanup(cleanup_string)))
5075
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5076
g_assert_nonnull(tempfilename);
5077
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5078
g_assert_cmpint(questionfile, >, 0);
5079
FILE *qf = fdopen(questionfile, "w");
5080
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5082
g_assert_cmpint(fclose(qf), ==, 0);
5084
char *const filename = strdup(tempfilename);
5085
g_assert_nonnull(filename);
5086
task_context task = {
5087
.func=open_and_parse_question,
5088
.question_filename=filename,
5090
.password=(buffer[]){{}},
5092
.cancelled_filenames=&cancelled_filenames,
5093
.current_time=(mono_microsecs[]){0},
5094
.mandos_client_exited=(bool[]){false},
5095
.password_is_read=(bool[]){false},
5097
run_task_with_stderr_to_dev_null(task, queue);
5098
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5100
g_assert_cmpint(unlink(tempfilename), ==, 0);
5104
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5105
test_fixture *fixture,
5106
__attribute__((unused))
5107
gconstpointer user_data){
5108
__attribute__((cleanup(cleanup_close)))
5109
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5110
g_assert_cmpint(epoll_fd, >=, 0);
5111
__attribute__((cleanup(string_set_clear)))
5112
string_set cancelled_filenames = {};
5113
buffer password = {};
5114
bool mandos_client_exited = false;
5115
bool password_is_read = false;
5116
__attribute__((cleanup(cleanup_queue)))
5117
task_queue *queue = create_queue();
5118
g_assert_nonnull(queue);
5119
const mono_microsecs current_time = 0;
5121
/* Find value of sysctl kernel.pid_max */
5122
uintmax_t pid_max = 0;
5123
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5124
g_assert_nonnull(sysctl_pid_max);
5125
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5127
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5129
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5130
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5132
__attribute__((cleanup(cleanup_string)))
5133
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5134
g_assert_nonnull(tempfilename);
5135
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5136
g_assert_cmpint(questionfile, >, 0);
5137
FILE *qf = fdopen(questionfile, "w");
5138
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5139
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5141
g_assert_cmpint(fclose(qf), ==, 0);
5143
char *const question_filename = strdup(tempfilename);
5144
g_assert_nonnull(question_filename);
5145
task_context task = {
5146
.func=open_and_parse_question,
5147
.question_filename=question_filename,
5149
.password=&password,
5150
.filename=question_filename,
5151
.cancelled_filenames=&cancelled_filenames,
5152
.current_time=¤t_time,
5153
.mandos_client_exited=&mandos_client_exited,
5154
.password_is_read=&password_is_read,
5156
run_task_with_stderr_to_dev_null(task, queue);
5157
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5159
g_assert_cmpint(unlink(tempfilename), ==, 0);
5163
test_open_and_parse_question_no_notafter(__attribute__((unused))
5164
test_fixture *fixture,
5165
__attribute__((unused))
5166
gconstpointer user_data){
5167
__attribute__((cleanup(cleanup_close)))
5168
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5169
g_assert_cmpint(epoll_fd, >=, 0);
5170
__attribute__((cleanup(string_set_clear)))
5171
string_set cancelled_filenames = {};
5172
buffer password = {};
5173
bool mandos_client_exited = false;
5174
bool password_is_read = false;
5175
__attribute__((cleanup(cleanup_queue)))
5176
task_queue *queue = create_queue();
5177
g_assert_nonnull(queue);
5178
const mono_microsecs current_time = 0;
5180
__attribute__((cleanup(cleanup_string)))
5181
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5182
g_assert_nonnull(tempfilename);
5183
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5184
g_assert_cmpint(questionfile, >, 0);
5185
FILE *qf = fdopen(questionfile, "w");
5186
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5187
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5188
g_assert_cmpint(fclose(qf), ==, 0);
5190
char *const filename = strdup(tempfilename);
5191
g_assert_nonnull(filename);
5192
task_context task = {
5193
.func=open_and_parse_question,
5194
.question_filename=filename,
5196
.password=&password,
5198
.cancelled_filenames=&cancelled_filenames,
5199
.current_time=¤t_time,
5200
.mandos_client_exited=&mandos_client_exited,
5201
.password_is_read=&password_is_read,
5203
task.func(task, queue);
5204
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5206
__attribute__((cleanup(cleanup_string)))
5207
char *socket_filename = strdup("/nonexistent");
5208
g_assert_nonnull(socket_filename);
5209
g_assert_nonnull(find_matching_task(queue, (task_context){
5210
.func=connect_question_socket,
5211
.question_filename=tempfilename,
5212
.filename=socket_filename,
5214
.password=&password,
5215
.current_time=¤t_time,
5216
.mandos_client_exited=&mandos_client_exited,
5217
.password_is_read=&password_is_read,
5220
g_assert_true(queue->next_run != 0);
5222
g_assert_cmpint(unlink(tempfilename), ==, 0);
5226
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5227
test_fixture *fixture,
5228
__attribute__((unused))
5229
gconstpointer user_data){
5230
__attribute__((cleanup(cleanup_close)))
5231
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5232
g_assert_cmpint(epoll_fd, >=, 0);
5233
__attribute__((cleanup(string_set_clear)))
5234
string_set cancelled_filenames = {};
5235
buffer password = {};
5236
bool mandos_client_exited = false;
5237
bool password_is_read = false;
5238
__attribute__((cleanup(cleanup_queue)))
5239
task_queue *queue = create_queue();
5240
g_assert_nonnull(queue);
5241
const mono_microsecs current_time = 0;
5243
__attribute__((cleanup(cleanup_string)))
5244
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5245
g_assert_nonnull(tempfilename);
5246
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5247
g_assert_cmpint(questionfile, >, 0);
5248
FILE *qf = fdopen(questionfile, "w");
5249
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5250
PRIuMAX "\nNotAfter=\n",
5251
(uintmax_t)getpid()), >, 0);
5252
g_assert_cmpint(fclose(qf), ==, 0);
5254
char *const filename = strdup(tempfilename);
5255
g_assert_nonnull(filename);
5256
task_context task = {
5257
.func=open_and_parse_question,
5258
.question_filename=filename,
5260
.password=&password,
5262
.cancelled_filenames=&cancelled_filenames,
5263
.current_time=¤t_time,
5264
.mandos_client_exited=&mandos_client_exited,
5265
.password_is_read=&password_is_read,
5267
run_task_with_stderr_to_dev_null(task, queue);
5268
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5270
__attribute__((cleanup(cleanup_string)))
5271
char *socket_filename = strdup("/nonexistent");
5272
g_assert_nonnull(find_matching_task(queue, (task_context){
5273
.func=connect_question_socket,
5274
.question_filename=tempfilename,
5275
.filename=socket_filename,
5277
.password=&password,
5278
.current_time=¤t_time,
5279
.mandos_client_exited=&mandos_client_exited,
5280
.password_is_read=&password_is_read,
5282
g_assert_true(queue->next_run != 0);
5284
g_assert_cmpint(unlink(tempfilename), ==, 0);
5288
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5290
const mono_microsecs
5292
const mono_microsecs
5294
__attribute__((cleanup(cleanup_close)))
5295
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5296
g_assert_cmpint(epoll_fd, >=, 0);
5297
__attribute__((cleanup(string_set_clear)))
5298
string_set cancelled_filenames = {};
5299
buffer password = {};
5300
bool mandos_client_exited = false;
5301
bool password_is_read = false;
5302
__attribute__((cleanup(cleanup_queue)))
5303
task_queue *queue = create_queue();
5304
g_assert_nonnull(queue);
5305
queue->next_run = next_queue_run;
5307
__attribute__((cleanup(cleanup_string)))
5308
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5309
g_assert_nonnull(tempfilename);
5310
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5311
g_assert_cmpint(questionfile, >, 0);
5312
FILE *qf = fdopen(questionfile, "w");
5313
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5314
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5315
(uintmax_t)getpid(), notafter), >, 0);
5316
g_assert_cmpint(fclose(qf), ==, 0);
5318
char *const filename = strdup(tempfilename);
5319
g_assert_nonnull(filename);
5320
task_context task = {
5321
.func=open_and_parse_question,
5322
.question_filename=filename,
5324
.password=&password,
5326
.cancelled_filenames=&cancelled_filenames,
5327
.current_time=¤t_time,
5328
.mandos_client_exited=&mandos_client_exited,
5329
.password_is_read=&password_is_read,
5331
task.func(task, queue);
5333
if(queue->length >= 1){
5334
__attribute__((cleanup(cleanup_string)))
5335
char *socket_filename = strdup("/nonexistent");
5336
g_assert_nonnull(find_matching_task(queue, (task_context){
5337
.func=connect_question_socket,
5338
.filename=socket_filename,
5340
.password=&password,
5341
.current_time=¤t_time,
5342
.cancelled_filenames=&cancelled_filenames,
5343
.mandos_client_exited=&mandos_client_exited,
5344
.password_is_read=&password_is_read,
5346
g_assert_true(queue->next_run != 0);
5350
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5351
} else if(current_time >= notafter) {
5352
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5354
g_assert_nonnull(find_matching_task(queue, (task_context){
5355
.func=cancel_old_question,
5356
.question_filename=tempfilename,
5357
.filename=tempfilename,
5359
.cancelled_filenames=&cancelled_filenames,
5360
.current_time=¤t_time,
5363
g_assert_true(queue->next_run == 1);
5365
g_assert_cmpint(unlink(tempfilename), ==, 0);
5369
test_open_and_parse_question_notafter_0(__attribute__((unused))
5370
test_fixture *fixture,
5371
__attribute__((unused))
5372
gconstpointer user_data){
5373
/* current_time, notafter, next_queue_run */
5374
assert_open_and_parse_question_with_notafter(0, 0, 0);
5378
test_open_and_parse_question_notafter_1(__attribute__((unused))
5379
test_fixture *fixture,
5380
__attribute__((unused))
5381
gconstpointer user_data){
5382
/* current_time, notafter, next_queue_run */
5383
assert_open_and_parse_question_with_notafter(0, 1, 0);
5387
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5388
test_fixture *fixture,
5389
__attribute__((unused))
5390
gconstpointer user_data){
5391
/* current_time, notafter, next_queue_run */
5392
assert_open_and_parse_question_with_notafter(0, 1, 1);
5396
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5397
test_fixture *fixture,
5398
__attribute__((unused))
5399
gconstpointer user_data){
5400
/* current_time, notafter, next_queue_run */
5401
assert_open_and_parse_question_with_notafter(0, 1, 2);
5405
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5406
test_fixture *fixture,
5407
__attribute__((unused))
5408
gconstpointer user_data){
5409
/* current_time, notafter, next_queue_run */
5410
assert_open_and_parse_question_with_notafter(1, 1, 0);
5414
test_open_and_parse_question_late_notafter(__attribute__((unused))
5415
test_fixture *fixture,
5416
__attribute__((unused))
5417
gconstpointer user_data){
5418
/* current_time, notafter, next_queue_run */
5419
assert_open_and_parse_question_with_notafter(2, 1, 0);
5422
static void assert_cancel_old_question_param(const mono_microsecs
5424
const mono_microsecs
5426
const mono_microsecs
5428
const mono_microsecs
5430
__attribute__((cleanup(string_set_clear)))
5431
string_set cancelled_filenames = {};
5432
__attribute__((cleanup(cleanup_queue)))
5433
task_queue *queue = create_queue();
5434
g_assert_nonnull(queue);
5435
queue->next_run = next_queue_run;
5437
char *const question_filename = strdup("/nonexistent");
5438
g_assert_nonnull(question_filename);
5439
task_context task = {
5440
.func=cancel_old_question,
5441
.question_filename=question_filename,
5442
.filename=question_filename,
5444
.cancelled_filenames=&cancelled_filenames,
5445
.current_time=¤t_time,
5447
task.func(task, queue);
5449
if(current_time >= notafter){
5450
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5451
g_assert_true(string_set_contains(cancelled_filenames,
5454
g_assert_nonnull(find_matching_task(queue, (task_context){
5455
.func=cancel_old_question,
5456
.question_filename=question_filename,
5457
.filename=question_filename,
5459
.cancelled_filenames=&cancelled_filenames,
5460
.current_time=¤t_time,
5463
g_assert_false(string_set_contains(cancelled_filenames,
5464
question_filename));
5466
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5467
(unsigned int)next_set_to);
5470
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5471
test_fixture *fixture,
5472
__attribute__((unused))
5473
gconstpointer user_data){
5474
/* next_queue_run unset,
5475
cancellation should happen because time has come,
5476
next_queue_run should be unchanged */
5477
/* next_queue_run, notafter, current_time, next_set_to */
5478
assert_cancel_old_question_param(0, 1, 2, 0);
5481
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5482
test_fixture *fixture,
5483
__attribute__((unused))
5484
gconstpointer user_data){
5485
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5486
and current_time is not yet notafter or greater,
5487
update value of next_queue_run to value of notafter */
5488
/* next_queue_run, notafter, current_time, next_set_to */
5489
assert_cancel_old_question_param(0, 2, 1, 2);
5492
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5493
test_fixture *fixture,
5494
__attribute__((unused))
5495
gconstpointer user_data){
5496
/* next_queue_run 1,
5497
cancellation should happen because time has come,
5498
next_queue_run should be unchanged */
5499
/* next_queue_run, notafter, current_time, next_set_to */
5500
assert_cancel_old_question_param(1, 2, 3, 1);
5503
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5504
test_fixture *fixture,
5505
__attribute__((unused))
5506
gconstpointer user_data){
5507
/* If next_queue_run is set,
5508
and current_time is not yet notafter or greater,
5509
and notafter is larger than next_queue_run
5510
next_queue_run should be unchanged */
5511
/* next_queue_run, notafter, current_time, next_set_to */
5512
assert_cancel_old_question_param(1, 3, 2, 1);
5515
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5516
test_fixture *fixture,
5517
__attribute__((unused))
5518
gconstpointer user_data){
5519
/* next_queue_run 2,
5520
cancellation should happen because time has come,
5521
next_queue_run should be unchanged */
5522
/* next_queue_run, notafter, current_time, next_set_to */
5523
assert_cancel_old_question_param(2, 1, 3, 2);
5526
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5527
test_fixture *fixture,
5528
__attribute__((unused))
5529
gconstpointer user_data){
5530
/* If next_queue_run is set,
5531
and current_time is not yet notafter or greater,
5532
and notafter is larger than next_queue_run
5533
next_queue_run should be unchanged */
5534
/* next_queue_run, notafter, current_time, next_set_to */
5535
assert_cancel_old_question_param(2, 3, 1, 2);
5538
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5539
test_fixture *fixture,
5540
__attribute__((unused))
5541
gconstpointer user_data){
5542
/* next_queue_run 3,
5543
cancellation should happen because time has come,
5544
next_queue_run should be unchanged */
5545
/* next_queue_run, notafter, current_time, next_set_to */
5546
assert_cancel_old_question_param(3, 1, 2, 3);
5549
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5550
test_fixture *fixture,
5551
__attribute__((unused))
5552
gconstpointer user_data){
5553
/* If next_queue_run is set,
5554
and current_time is not yet notafter or greater,
5555
and notafter is smaller than next_queue_run
5556
update value of next_queue_run to value of notafter */
5557
/* next_queue_run, notafter, current_time, next_set_to */
5558
assert_cancel_old_question_param(3, 2, 1, 2);
5562
test_connect_question_socket_name_too_long(__attribute__((unused))
5563
test_fixture *fixture,
5564
__attribute__((unused))
5565
gconstpointer user_data){
5566
__attribute__((cleanup(cleanup_close)))
5567
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5568
g_assert_cmpint(epoll_fd, >=, 0);
5569
const char question_filename[] = "/nonexistent/question";
5570
__attribute__((cleanup(string_set_clear)))
5571
string_set cancelled_filenames = {};
5572
__attribute__((cleanup(cleanup_queue)))
5573
task_queue *queue = create_queue();
5574
g_assert_nonnull(queue);
5575
__attribute__((cleanup(cleanup_string)))
5576
char *tempdir = make_temporary_directory();
5577
g_assert_nonnull(tempdir);
5578
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5579
char socket_name[sizeof(unix_socket.sun_path)];
5580
memset(socket_name, 'x', sizeof(socket_name));
5581
socket_name[sizeof(socket_name)-1] = '\0';
5582
char *filename = NULL;
5583
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5585
g_assert_nonnull(filename);
5587
task_context task = {
5588
.func=connect_question_socket,
5589
.question_filename=strdup(question_filename),
5591
.password=(buffer[]){{}},
5593
.cancelled_filenames=&cancelled_filenames,
5594
.mandos_client_exited=(bool[]){false},
5595
.password_is_read=(bool[]){false},
5596
.current_time=(mono_microsecs[]){0},
5598
g_assert_nonnull(task.question_filename);
5599
run_task_with_stderr_to_dev_null(task, queue);
5601
g_assert_true(string_set_contains(cancelled_filenames,
5602
question_filename));
5603
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5604
g_assert_true(queue->next_run == 0);
5606
g_assert_cmpint(rmdir(tempdir), ==, 0);
5610
void test_connect_question_socket_connect_fail(__attribute__((unused))
5611
test_fixture *fixture,
5612
__attribute__((unused))
5615
__attribute__((cleanup(cleanup_close)))
5616
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5617
g_assert_cmpint(epoll_fd, >=, 0);
5618
const char question_filename[] = "/nonexistent/question";
5619
__attribute__((cleanup(string_set_clear)))
5620
string_set cancelled_filenames = {};
5621
const mono_microsecs current_time = 3;
5622
__attribute__((cleanup(cleanup_queue)))
5623
task_queue *queue = create_queue();
5624
g_assert_nonnull(queue);
5625
__attribute__((cleanup(cleanup_string)))
5626
char *tempdir = make_temporary_directory();
5627
g_assert_nonnull(tempdir);
5628
char socket_name[] = "nonexistent";
5629
char *filename = NULL;
5630
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5632
g_assert_nonnull(filename);
5634
task_context task = {
5635
.func=connect_question_socket,
5636
.question_filename=strdup(question_filename),
5638
.password=(buffer[]){{}},
5640
.cancelled_filenames=&cancelled_filenames,
5641
.mandos_client_exited=(bool[]){false},
5642
.password_is_read=(bool[]){false},
5643
.current_time=¤t_time,
5645
g_assert_nonnull(task.question_filename);
5646
run_task_with_stderr_to_dev_null(task, queue);
5648
g_assert_nonnull(find_matching_task(queue, task));
5650
g_assert_false(string_set_contains(cancelled_filenames,
5651
question_filename));
5652
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5653
g_assert_true(queue->next_run == 1000000 + current_time);
5655
g_assert_cmpint(rmdir(tempdir), ==, 0);
5659
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5660
test_fixture *fixture,
5661
__attribute__((unused))
5662
gconstpointer user_data){
5663
__attribute__((cleanup(cleanup_close)))
5664
const int epoll_fd = open("/dev/null",
5665
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5666
__attribute__((cleanup(cleanup_string)))
5667
char *const question_filename = strdup("/nonexistent/question");
5668
g_assert_nonnull(question_filename);
5669
__attribute__((cleanup(string_set_clear)))
5670
string_set cancelled_filenames = {};
5671
const mono_microsecs current_time = 5;
5672
__attribute__((cleanup(cleanup_queue)))
5673
task_queue *queue = create_queue();
5674
g_assert_nonnull(queue);
5675
__attribute__((cleanup(cleanup_string)))
5676
char *tempdir = make_temporary_directory();
5677
g_assert_nonnull(tempdir);
5678
__attribute__((cleanup(cleanup_close)))
5679
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5680
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5681
g_assert_cmpint(sock_fd, >=, 0);
5682
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5683
const char socket_name[] = "socket_name";
5684
__attribute__((cleanup(cleanup_string)))
5685
char *filename = NULL;
5686
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5688
g_assert_nonnull(filename);
5689
g_assert_cmpint((int)strlen(filename), <,
5690
(int)sizeof(sock_name.sun_path));
5691
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5692
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5693
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5694
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5695
task_context task = {
5696
.func=connect_question_socket,
5697
.question_filename=strdup(question_filename),
5699
.password=(buffer[]){{}},
5700
.filename=strdup(filename),
5701
.cancelled_filenames=&cancelled_filenames,
5702
.mandos_client_exited=(bool[]){false},
5703
.password_is_read=(bool[]){false},
5704
.current_time=¤t_time,
5706
g_assert_nonnull(task.question_filename);
5707
run_task_with_stderr_to_dev_null(task, queue);
5709
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5710
const task_context *const added_task
5711
= find_matching_task(queue, task);
5712
g_assert_nonnull(added_task);
5713
g_assert_true(queue->next_run == 1000000 + current_time);
5715
g_assert_cmpint(unlink(filename), ==, 0);
5716
g_assert_cmpint(rmdir(tempdir), ==, 0);
5720
void test_connect_question_socket_usable(__attribute__((unused))
5721
test_fixture *fixture,
5722
__attribute__((unused))
5723
gconstpointer user_data){
5724
__attribute__((cleanup(cleanup_close)))
5725
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5726
g_assert_cmpint(epoll_fd, >=, 0);
5727
__attribute__((cleanup(cleanup_string)))
5728
char *const question_filename = strdup("/nonexistent/question");
5729
g_assert_nonnull(question_filename);
5730
__attribute__((cleanup(string_set_clear)))
5731
string_set cancelled_filenames = {};
5732
buffer password = {};
5733
bool mandos_client_exited = false;
5734
bool password_is_read = false;
5735
const mono_microsecs current_time = 0;
5736
__attribute__((cleanup(cleanup_queue)))
5737
task_queue *queue = create_queue();
5738
g_assert_nonnull(queue);
5739
__attribute__((cleanup(cleanup_string)))
5740
char *tempdir = make_temporary_directory();
5741
g_assert_nonnull(tempdir);
5742
__attribute__((cleanup(cleanup_close)))
5743
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5744
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5745
g_assert_cmpint(sock_fd, >=, 0);
5746
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5747
const char socket_name[] = "socket_name";
5748
__attribute__((cleanup(cleanup_string)))
5749
char *filename = NULL;
5750
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5752
g_assert_nonnull(filename);
5753
g_assert_cmpint((int)strlen(filename), <,
5754
(int)sizeof(sock_name.sun_path));
5755
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5756
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5757
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5758
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5759
task_context task = {
5760
.func=connect_question_socket,
5761
.question_filename=strdup(question_filename),
5763
.password=&password,
5764
.filename=strdup(filename),
5765
.cancelled_filenames=&cancelled_filenames,
5766
.mandos_client_exited=&mandos_client_exited,
5767
.password_is_read=&password_is_read,
5768
.current_time=¤t_time,
5770
g_assert_nonnull(task.question_filename);
5771
task.func(task, queue);
5773
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5774
const task_context *const added_task
5775
= find_matching_task(queue, (task_context){
5776
.func=send_password_to_socket,
5777
.question_filename=question_filename,
5780
.password=&password,
5781
.cancelled_filenames=&cancelled_filenames,
5782
.mandos_client_exited=&mandos_client_exited,
5783
.password_is_read=&password_is_read,
5784
.current_time=¤t_time,
5786
g_assert_nonnull(added_task);
5787
g_assert_cmpint(added_task->fd, >, 0);
5789
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5792
const int fd = added_task->fd;
5793
g_assert_cmpint(fd, >, 0);
5794
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5797
char write_data[PIPE_BUF];
5799
/* Construct test password buffer */
5800
/* Start with + since that is what the real procotol uses */
5801
write_data[0] = '+';
5802
/* Set a special character at string end just to mark the end */
5803
write_data[sizeof(write_data)-2] = 'y';
5804
/* Set NUL at buffer end, as suggested by the protocol */
5805
write_data[sizeof(write_data)-1] = '\0';
5806
/* Fill rest of password with 'x' */
5807
memset(write_data+1, 'x', sizeof(write_data)-3);
5808
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5809
MSG_NOSIGNAL), ==, sizeof(write_data));
5812
/* read from sock_fd */
5813
char read_data[sizeof(write_data)];
5814
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5815
==, sizeof(read_data));
5817
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5820
/* writing to sock_fd should fail */
5821
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5822
MSG_NOSIGNAL), <, 0);
5824
/* reading from fd should fail */
5825
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5826
MSG_NOSIGNAL), <, 0);
5828
g_assert_cmpint(unlink(filename), ==, 0);
5829
g_assert_cmpint(rmdir(tempdir), ==, 0);
5833
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5834
test_fixture *fixture,
5835
__attribute__((unused))
5838
__attribute__((cleanup(cleanup_close)))
5839
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5840
g_assert_cmpint(epoll_fd, >=, 0);
5841
__attribute__((cleanup(cleanup_string)))
5842
char *const question_filename = strdup("/nonexistent/question");
5843
g_assert_nonnull(question_filename);
5844
__attribute__((cleanup(cleanup_string)))
5845
char *const filename = strdup("/nonexistent/socket");
5846
g_assert_nonnull(filename);
5847
__attribute__((cleanup(string_set_clear)))
5848
string_set cancelled_filenames = {};
5849
buffer password = {};
5850
bool password_is_read = true;
5851
__attribute__((cleanup(cleanup_queue)))
5852
task_queue *queue = create_queue();
5853
g_assert_nonnull(queue);
5855
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5856
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5858
__attribute__((cleanup(cleanup_close)))
5859
const int read_socket = socketfds[0];
5860
const int write_socket = socketfds[1];
5861
task_context task = {
5862
.func=send_password_to_socket,
5863
.question_filename=strdup(question_filename),
5864
.filename=strdup(filename),
5867
.password=&password,
5868
.cancelled_filenames=&cancelled_filenames,
5869
.mandos_client_exited=(bool[]){false},
5870
.password_is_read=&password_is_read,
5871
.current_time=(mono_microsecs[]){0},
5873
g_assert_nonnull(task.question_filename);
5875
task.func(task, queue);
5877
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5879
const task_context *const added_task
5880
= find_matching_task(queue, task);
5881
g_assert_nonnull(added_task);
5882
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5883
g_assert_true(password_is_read);
5885
g_assert_cmpint(added_task->fd, >, 0);
5886
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5891
test_send_password_to_socket_password_not_read(__attribute__((unused))
5892
test_fixture *fixture,
5893
__attribute__((unused))
5896
__attribute__((cleanup(cleanup_close)))
5897
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5898
g_assert_cmpint(epoll_fd, >=, 0);
5899
__attribute__((cleanup(cleanup_string)))
5900
char *const question_filename = strdup("/nonexistent/question");
5901
g_assert_nonnull(question_filename);
5902
__attribute__((cleanup(cleanup_string)))
5903
char *const filename = strdup("/nonexistent/socket");
5904
__attribute__((cleanup(string_set_clear)))
5905
string_set cancelled_filenames = {};
5906
buffer password = {};
5907
__attribute__((cleanup(cleanup_queue)))
5908
task_queue *queue = create_queue();
5909
g_assert_nonnull(queue);
5911
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5912
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5914
__attribute__((cleanup(cleanup_close)))
5915
const int read_socket = socketfds[0];
5916
const int write_socket = socketfds[1];
5917
task_context task = {
5918
.func=send_password_to_socket,
5919
.question_filename=strdup(question_filename),
5920
.filename=strdup(filename),
5923
.password=&password,
5924
.cancelled_filenames=&cancelled_filenames,
5925
.mandos_client_exited=(bool[]){false},
5926
.password_is_read=(bool[]){false},
5927
.current_time=(mono_microsecs[]){0},
5929
g_assert_nonnull(task.question_filename);
5931
task.func(task, queue);
5933
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5935
const task_context *const added_task = find_matching_task(queue,
5937
g_assert_nonnull(added_task);
5938
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5939
g_assert_true(queue->next_run == 0);
5941
g_assert_cmpint(added_task->fd, >, 0);
5942
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5947
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5948
test_fixture *fixture,
5949
__attribute__((unused))
5950
gconstpointer user_data){
5951
__attribute__((cleanup(cleanup_close)))
5952
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5953
g_assert_cmpint(epoll_fd, >=, 0);
5954
const char question_filename[] = "/nonexistent/question";
5955
char *const filename = strdup("/nonexistent/socket");
5956
__attribute__((cleanup(string_set_clear)))
5957
string_set cancelled_filenames = {};
5958
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5959
__attribute__((cleanup(cleanup_buffer)))
5961
.data=malloc(oversized),
5963
.allocated=oversized,
5965
g_assert_nonnull(password.data);
5966
if(mlock(password.data, password.allocated) != 0){
5967
g_assert_true(errno == EPERM or errno == ENOMEM);
5969
/* Construct test password buffer */
5970
/* Start with + since that is what the real procotol uses */
5971
password.data[0] = '+';
5972
/* Set a special character at string end just to mark the end */
5973
password.data[oversized-3] = 'y';
5974
/* Set NUL at buffer end, as suggested by the protocol */
5975
password.data[oversized-2] = '\0';
5976
/* Fill rest of password with 'x' */
5977
memset(password.data+1, 'x', oversized-3);
5979
__attribute__((cleanup(cleanup_queue)))
5980
task_queue *queue = create_queue();
5981
g_assert_nonnull(queue);
5983
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5984
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5986
__attribute__((cleanup(cleanup_close)))
5987
const int read_socket = socketfds[0];
5988
__attribute__((cleanup(cleanup_close)))
5989
const int write_socket = socketfds[1];
5990
task_context task = {
5991
.func=send_password_to_socket,
5992
.question_filename=strdup(question_filename),
5996
.password=&password,
5997
.cancelled_filenames=&cancelled_filenames,
5998
.mandos_client_exited=(bool[]){true},
5999
.password_is_read=(bool[]){true},
6000
.current_time=(mono_microsecs[]){0},
6002
g_assert_nonnull(task.question_filename);
6004
run_task_with_stderr_to_dev_null(task, queue);
6006
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6007
g_assert_true(string_set_contains(cancelled_filenames,
6008
question_filename));
6011
static void test_send_password_to_socket_retry(__attribute__((unused))
6012
test_fixture *fixture,
6013
__attribute__((unused))
6016
__attribute__((cleanup(cleanup_close)))
6017
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6018
g_assert_cmpint(epoll_fd, >=, 0);
6019
__attribute__((cleanup(cleanup_string)))
6020
char *const question_filename = strdup("/nonexistent/question");
6021
g_assert_nonnull(question_filename);
6022
__attribute__((cleanup(cleanup_string)))
6023
char *const filename = strdup("/nonexistent/socket");
6024
g_assert_nonnull(filename);
6025
__attribute__((cleanup(string_set_clear)))
6026
string_set cancelled_filenames = {};
6027
__attribute__((cleanup(cleanup_buffer)))
6028
buffer password = {};
6030
__attribute__((cleanup(cleanup_queue)))
6031
task_queue *queue = create_queue();
6032
g_assert_nonnull(queue);
6034
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6035
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6037
__attribute__((cleanup(cleanup_close)))
6038
const int read_socket = socketfds[0];
6039
const int write_socket = socketfds[1];
6040
/* Close the server side socket to force ECONNRESET on client */
6041
g_assert_cmpint(close(read_socket), ==, 0);
6042
task_context task = {
6043
.func=send_password_to_socket,
6044
.question_filename=strdup(question_filename),
6045
.filename=strdup(filename),
6048
.password=&password,
6049
.cancelled_filenames=&cancelled_filenames,
6050
.mandos_client_exited=(bool[]){true},
6051
.password_is_read=(bool[]){true},
6052
.current_time=(mono_microsecs[]){0},
6054
g_assert_nonnull(task.question_filename);
6056
task.func(task, queue);
6058
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6060
const task_context *const added_task = find_matching_task(queue,
6062
g_assert_nonnull(added_task);
6063
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6065
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6070
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6071
test_fixture *fixture,
6072
__attribute__((unused))
6073
gconstpointer user_data){
6074
__attribute__((cleanup(cleanup_close)))
6075
const int epoll_fd = open("/dev/null",
6076
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6077
__attribute__((cleanup(cleanup_string)))
6078
char *const question_filename = strdup("/nonexistent/question");
6079
g_assert_nonnull(question_filename);
6080
__attribute__((cleanup(cleanup_string)))
6081
char *const filename = strdup("/nonexistent/socket");
6082
g_assert_nonnull(filename);
6083
__attribute__((cleanup(string_set_clear)))
6084
string_set cancelled_filenames = {};
6085
__attribute__((cleanup(cleanup_buffer)))
6086
buffer password = {};
6088
const mono_microsecs current_time = 11;
6089
__attribute__((cleanup(cleanup_queue)))
6090
task_queue *queue = create_queue();
6091
g_assert_nonnull(queue);
6093
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6094
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6096
__attribute__((cleanup(cleanup_close)))
6097
const int read_socket = socketfds[0];
6098
const int write_socket = socketfds[1];
6099
/* Close the server side socket to force ECONNRESET on client */
6100
g_assert_cmpint(close(read_socket), ==, 0);
6101
task_context task = {
6102
.func=send_password_to_socket,
6103
.question_filename=strdup(question_filename),
6104
.filename=strdup(filename),
6107
.password=&password,
6108
.cancelled_filenames=&cancelled_filenames,
6109
.mandos_client_exited=(bool[]){true},
6110
.password_is_read=(bool[]){true},
6111
.current_time=¤t_time,
6113
g_assert_nonnull(task.question_filename);
6115
run_task_with_stderr_to_dev_null(task, queue);
6117
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6119
const task_context *const added_task = find_matching_task(queue,
6121
g_assert_nonnull(added_task);
6122
g_assert_true(queue->next_run == current_time + 1000000);
6123
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6126
static void assert_send_password_to_socket_password(buffer password){
6127
__attribute__((cleanup(cleanup_close)))
6128
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6129
g_assert_cmpint(epoll_fd, >=, 0);
6130
char *const question_filename = strdup("/nonexistent/question");
6131
g_assert_nonnull(question_filename);
6132
char *const filename = strdup("/nonexistent/socket");
6133
g_assert_nonnull(filename);
6134
__attribute__((cleanup(string_set_clear)))
6135
string_set cancelled_filenames = {};
6137
__attribute__((cleanup(cleanup_queue)))
6138
task_queue *queue = create_queue();
6139
g_assert_nonnull(queue);
6141
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6142
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6144
__attribute__((cleanup(cleanup_close)))
6145
const int read_socket = socketfds[0];
6146
const int write_socket = socketfds[1];
6147
task_context task = {
6148
.func=send_password_to_socket,
6149
.question_filename=question_filename,
6153
.password=&password,
6154
.cancelled_filenames=&cancelled_filenames,
6155
.mandos_client_exited=(bool[]){true},
6156
.password_is_read=(bool[]){true},
6157
.current_time=(mono_microsecs[]){0},
6160
char *expected_written_data = malloc(password.length + 2);
6161
g_assert_nonnull(expected_written_data);
6162
expected_written_data[0] = '+';
6163
expected_written_data[password.length + 1] = '\0';
6164
if(password.length > 0){
6165
g_assert_nonnull(password.data);
6166
memcpy(expected_written_data + 1, password.data, password.length);
6169
task.func(task, queue);
6172
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6173
(int)(password.length + 2));
6174
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6176
g_assert_true(memcmp(expected_written_data, buf,
6177
password.length + 2) == 0);
6179
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6181
free(expected_written_data);
6185
test_send_password_to_socket_null_password(__attribute__((unused))
6186
test_fixture *fixture,
6187
__attribute__((unused))
6188
gconstpointer user_data){
6189
__attribute__((cleanup(cleanup_buffer)))
6190
buffer password = {};
6191
assert_send_password_to_socket_password(password);
6195
test_send_password_to_socket_empty_password(__attribute__((unused))
6196
test_fixture *fixture,
6197
__attribute__((unused))
6198
gconstpointer user_data){
6199
__attribute__((cleanup(cleanup_buffer)))
6201
.data=malloc(1), /* because malloc(0) may return NULL */
6203
.allocated=0, /* deliberate lie */
6205
g_assert_nonnull(password.data);
6206
assert_send_password_to_socket_password(password);
6210
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6211
test_fixture *fixture,
6212
__attribute__((unused))
6213
gconstpointer user_data){
6214
__attribute__((cleanup(cleanup_buffer)))
6220
if(mlock(password.data, password.allocated) != 0){
6221
g_assert_true(errno == EPERM or errno == ENOMEM);
6223
assert_send_password_to_socket_password(password);
6227
test_send_password_to_socket_text_password(__attribute__((unused))
6228
test_fixture *fixture,
6229
__attribute__((unused))
6230
gconstpointer user_data){
6231
const char dummy_test_password[] = "dummy test password";
6232
__attribute__((cleanup(cleanup_buffer)))
6234
.data = strdup(dummy_test_password),
6235
.length = strlen(dummy_test_password),
6236
.allocated = sizeof(dummy_test_password),
6238
if(mlock(password.data, password.allocated) != 0){
6239
g_assert_true(errno == EPERM or errno == ENOMEM);
6241
assert_send_password_to_socket_password(password);
6245
test_send_password_to_socket_binary_password(__attribute__((unused))
6246
test_fixture *fixture,
6247
__attribute__((unused))
6248
gconstpointer user_data){
6249
__attribute__((cleanup(cleanup_buffer)))
6255
g_assert_nonnull(password.data);
6256
if(mlock(password.data, password.allocated) != 0){
6257
g_assert_true(errno == EPERM or errno == ENOMEM);
6259
char c = 1; /* Start at 1, avoiding NUL */
6260
for(int i=0; i < 255; i++){
6261
password.data[i] = c++;
6263
assert_send_password_to_socket_password(password);
6267
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6268
test_fixture *fixture,
6269
__attribute__((unused))
6272
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6273
__attribute__((cleanup(cleanup_buffer)))
6275
.data=malloc(sizeof(test_password)),
6276
.length=sizeof(test_password),
6277
.allocated=sizeof(test_password),
6279
g_assert_nonnull(password.data);
6280
if(mlock(password.data, password.allocated) !=0){
6281
g_assert_true(errno == EPERM or errno == ENOMEM);
6283
memcpy(password.data, test_password, password.allocated);
6284
assert_send_password_to_socket_password(password);
6287
static bool assert_add_existing_questions_to_devnull(task_queue
6300
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6301
test_fixture *fixture,
6302
__attribute__((unused))
6305
__attribute__((cleanup(cleanup_queue)))
6306
task_queue *queue = create_queue();
6307
g_assert_nonnull(queue);
6308
__attribute__((cleanup(cleanup_close)))
6309
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6310
g_assert_cmpint(epoll_fd, >=, 0);
6311
__attribute__((cleanup(string_set_clear)))
6312
string_set cancelled_filenames = {};
6314
g_assert_false(assert_add_existing_questions_to_devnull
6317
(buffer[]){{}}, /* password */
6318
&cancelled_filenames,
6319
(mono_microsecs[]){0}, /* current_time */
6320
(bool[]){false}, /* mandos_client_exited */
6321
(bool[]){false}, /* password_is_read */
6322
"/nonexistent")); /* dirname */
6324
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6328
bool assert_add_existing_questions_to_devnull(task_queue
6335
*cancelled_filenames,
6336
const mono_microsecs
6337
*const current_time,
6339
mandos_client_exited,
6344
__attribute__((cleanup(cleanup_close)))
6345
const int devnull_fd = open("/dev/null",
6346
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6347
g_assert_cmpint(devnull_fd, >=, 0);
6348
__attribute__((cleanup(cleanup_close)))
6349
const int real_stderr_fd = dup(STDERR_FILENO);
6350
g_assert_cmpint(real_stderr_fd, >=, 0);
6351
dup2(devnull_fd, STDERR_FILENO);
6352
const bool ret = add_existing_questions(queue, epoll_fd, password,
6353
cancelled_filenames,
6355
mandos_client_exited,
6356
password_is_read, dirname);
6357
dup2(real_stderr_fd, STDERR_FILENO);
6362
void test_add_existing_questions_no_questions(__attribute__((unused))
6363
test_fixture *fixture,
6364
__attribute__((unused))
6367
__attribute__((cleanup(cleanup_queue)))
6368
task_queue *queue = create_queue();
6369
g_assert_nonnull(queue);
6370
__attribute__((cleanup(cleanup_close)))
6371
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6372
g_assert_cmpint(epoll_fd, >=, 0);
6373
__attribute__((cleanup(string_set_clear)))
6374
string_set cancelled_filenames = {};
6375
__attribute__((cleanup(cleanup_string)))
6376
char *tempdir = make_temporary_directory();
6377
g_assert_nonnull(tempdir);
6379
g_assert_false(assert_add_existing_questions_to_devnull
6382
(buffer[]){{}}, /* password */
6383
&cancelled_filenames,
6384
(mono_microsecs[]){0}, /* current_time */
6385
(bool[]){false}, /* mandos_client_exited */
6386
(bool[]){false}, /* password_is_read */
6389
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6391
g_assert_cmpint(rmdir(tempdir), ==, 0);
6394
static char *make_question_file_in_directory(const char *const);
6397
void test_add_existing_questions_one_question(__attribute__((unused))
6398
test_fixture *fixture,
6399
__attribute__((unused))
6402
__attribute__((cleanup(cleanup_queue)))
6403
task_queue *queue = create_queue();
6404
g_assert_nonnull(queue);
6405
__attribute__((cleanup(cleanup_close)))
6406
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6407
g_assert_cmpint(epoll_fd, >=, 0);
6408
__attribute__((cleanup(cleanup_buffer)))
6409
buffer password = {};
6410
__attribute__((cleanup(string_set_clear)))
6411
string_set cancelled_filenames = {};
6412
const mono_microsecs current_time = 0;
6413
bool mandos_client_exited = false;
6414
bool password_is_read = false;
6415
__attribute__((cleanup(cleanup_string)))
6416
char *tempdir = make_temporary_directory();
6417
g_assert_nonnull(tempdir);
6418
__attribute__((cleanup(cleanup_string)))
6419
char *question_filename
6420
= make_question_file_in_directory(tempdir);
6421
g_assert_nonnull(question_filename);
6423
g_assert_true(assert_add_existing_questions_to_devnull
6427
&cancelled_filenames,
6429
&mandos_client_exited,
6433
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6435
g_assert_nonnull(find_matching_task(queue, (task_context){
6436
.func=open_and_parse_question,
6438
.filename=question_filename,
6439
.question_filename=question_filename,
6440
.password=&password,
6441
.cancelled_filenames=&cancelled_filenames,
6442
.current_time=¤t_time,
6443
.mandos_client_exited=&mandos_client_exited,
6444
.password_is_read=&password_is_read,
6447
g_assert_true(queue->next_run == 1);
6449
g_assert_cmpint(unlink(question_filename), ==, 0);
6450
g_assert_cmpint(rmdir(tempdir), ==, 0);
6453
static char *make_question_file_in_directory(const char
6455
return make_temporary_prefixed_file_in_directory("ask.", dir);
6459
void test_add_existing_questions_two_questions(__attribute__((unused))
6460
test_fixture *fixture,
6461
__attribute__((unused))
6464
__attribute__((cleanup(cleanup_queue)))
6465
task_queue *queue = create_queue();
6466
g_assert_nonnull(queue);
6467
__attribute__((cleanup(cleanup_close)))
6468
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6469
g_assert_cmpint(epoll_fd, >=, 0);
6470
__attribute__((cleanup(cleanup_buffer)))
6471
buffer password = {};
6472
__attribute__((cleanup(string_set_clear)))
6473
string_set cancelled_filenames = {};
6474
const mono_microsecs current_time = 0;
6475
bool mandos_client_exited = false;
6476
bool password_is_read = false;
6477
__attribute__((cleanup(cleanup_string)))
6478
char *tempdir = make_temporary_directory();
6479
g_assert_nonnull(tempdir);
6480
__attribute__((cleanup(cleanup_string)))
6481
char *question_filename1
6482
= make_question_file_in_directory(tempdir);
6483
g_assert_nonnull(question_filename1);
6484
__attribute__((cleanup(cleanup_string)))
6485
char *question_filename2
6486
= make_question_file_in_directory(tempdir);
6487
g_assert_nonnull(question_filename2);
6489
g_assert_true(assert_add_existing_questions_to_devnull
6493
&cancelled_filenames,
6495
&mandos_client_exited,
6499
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6501
g_assert_true(queue->next_run == 1);
6503
__attribute__((cleanup(string_set_clear)))
6504
string_set seen_questions = {};
6506
bool queue_contains_question_opener(char *const question_filename){
6507
return(find_matching_task(queue, (task_context){
6508
.func=open_and_parse_question,
6510
.question_filename=question_filename,
6511
.password=&password,
6512
.cancelled_filenames=&cancelled_filenames,
6513
.current_time=¤t_time,
6514
.mandos_client_exited=&mandos_client_exited,
6515
.password_is_read=&password_is_read,
6519
g_assert_true(queue_contains_question_opener(question_filename1));
6520
g_assert_true(queue_contains_question_opener(question_filename2));
6522
g_assert_true(queue->next_run == 1);
6524
g_assert_cmpint(unlink(question_filename1), ==, 0);
6525
g_assert_cmpint(unlink(question_filename2), ==, 0);
6526
g_assert_cmpint(rmdir(tempdir), ==, 0);
6530
test_add_existing_questions_non_questions(__attribute__((unused))
6531
test_fixture *fixture,
6532
__attribute__((unused))
6533
gconstpointer user_data){
6534
__attribute__((cleanup(cleanup_queue)))
6535
task_queue *queue = create_queue();
6536
g_assert_nonnull(queue);
6537
__attribute__((cleanup(cleanup_close)))
6538
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6539
g_assert_cmpint(epoll_fd, >=, 0);
6540
__attribute__((cleanup(string_set_clear)))
6541
string_set cancelled_filenames = {};
6542
__attribute__((cleanup(cleanup_string)))
6543
char *tempdir = make_temporary_directory();
6544
g_assert_nonnull(tempdir);
6545
__attribute__((cleanup(cleanup_string)))
6546
char *question_filename1
6547
= make_temporary_file_in_directory(tempdir);
6548
g_assert_nonnull(question_filename1);
6549
__attribute__((cleanup(cleanup_string)))
6550
char *question_filename2
6551
= make_temporary_file_in_directory(tempdir);
6552
g_assert_nonnull(question_filename2);
6554
g_assert_false(assert_add_existing_questions_to_devnull
6557
(buffer[]){{}}, /* password */
6558
&cancelled_filenames,
6559
(mono_microsecs[]){0}, /* current_time */
6560
(bool[]){false}, /* mandos_client_exited */
6561
(bool[]){false}, /* password_is_read */
6564
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6566
g_assert_cmpint(unlink(question_filename1), ==, 0);
6567
g_assert_cmpint(unlink(question_filename2), ==, 0);
6568
g_assert_cmpint(rmdir(tempdir), ==, 0);
6572
test_add_existing_questions_both_types(__attribute__((unused))
6573
test_fixture *fixture,
6574
__attribute__((unused))
6575
gconstpointer user_data){
6576
__attribute__((cleanup(cleanup_queue)))
6577
task_queue *queue = create_queue();
6578
g_assert_nonnull(queue);
6579
__attribute__((cleanup(cleanup_close)))
6580
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6581
g_assert_cmpint(epoll_fd, >=, 0);
6582
__attribute__((cleanup(cleanup_buffer)))
6583
buffer password = {};
6584
__attribute__((cleanup(string_set_clear)))
6585
string_set cancelled_filenames = {};
6586
const mono_microsecs current_time = 0;
6587
bool mandos_client_exited = false;
6588
bool password_is_read = false;
6589
__attribute__((cleanup(cleanup_string)))
6590
char *tempdir = make_temporary_directory();
6591
g_assert_nonnull(tempdir);
6592
__attribute__((cleanup(cleanup_string)))
6593
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6594
g_assert_nonnull(tempfilename1);
6595
__attribute__((cleanup(cleanup_string)))
6596
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6597
g_assert_nonnull(tempfilename2);
6598
__attribute__((cleanup(cleanup_string)))
6599
char *question_filename
6600
= make_question_file_in_directory(tempdir);
6601
g_assert_nonnull(question_filename);
6603
g_assert_true(assert_add_existing_questions_to_devnull
6607
&cancelled_filenames,
6609
&mandos_client_exited,
6613
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6615
g_assert_nonnull(find_matching_task(queue, (task_context){
6616
.func=open_and_parse_question,
6618
.filename=question_filename,
6619
.question_filename=question_filename,
6620
.password=&password,
6621
.cancelled_filenames=&cancelled_filenames,
6622
.current_time=¤t_time,
6623
.mandos_client_exited=&mandos_client_exited,
6624
.password_is_read=&password_is_read,
6627
g_assert_true(queue->next_run == 1);
6629
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6630
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6631
g_assert_cmpint(unlink(question_filename), ==, 0);
6632
g_assert_cmpint(rmdir(tempdir), ==, 0);
6635
static void test_wait_for_event_timeout(__attribute__((unused))
6636
test_fixture *fixture,
6637
__attribute__((unused))
6638
gconstpointer user_data){
6639
__attribute__((cleanup(cleanup_close)))
6640
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6641
g_assert_cmpint(epoll_fd, >=, 0);
6643
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6646
static void test_wait_for_event_event(__attribute__((unused))
6647
test_fixture *fixture,
6648
__attribute__((unused))
6649
gconstpointer user_data){
6650
__attribute__((cleanup(cleanup_close)))
6651
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6652
g_assert_cmpint(epoll_fd, >=, 0);
6654
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6655
__attribute__((cleanup(cleanup_close)))
6656
const int read_pipe = pipefds[0];
6657
__attribute__((cleanup(cleanup_close)))
6658
const int write_pipe = pipefds[1];
6659
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6660
&(struct epoll_event)
6661
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6662
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6664
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6667
static void test_wait_for_event_sigchld(test_fixture *fixture,
6668
__attribute__((unused))
6669
gconstpointer user_data){
6670
const pid_t pid = fork();
6671
if(pid == 0){ /* Child */
6672
if(not restore_signal_handler(&fixture->orig_sigaction)){
6673
_exit(EXIT_FAILURE);
6675
if(not restore_sigmask(&fixture->orig_sigmask)){
6676
_exit(EXIT_FAILURE);
6680
g_assert_true(pid != -1);
6681
__attribute__((cleanup(cleanup_close)))
6682
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6683
g_assert_cmpint(epoll_fd, >=, 0);
6685
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6688
g_assert_true(waitpid(pid, &status, 0) == pid);
6689
g_assert_true(WIFEXITED(status));
6690
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6693
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6694
test_fixture *fixture,
6695
__attribute__((unused))
6696
gconstpointer user_data){
6697
__attribute__((cleanup(cleanup_queue)))
6698
task_queue *queue = create_queue();
6699
g_assert_nonnull(queue);
6700
queue->next_run = 1;
6701
__attribute__((cleanup(cleanup_close)))
6702
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6703
__attribute__((cleanup(string_set_clear)))
6704
string_set cancelled_filenames = {};
6705
bool quit_now = false;
6707
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6708
g_assert_false(quit_now);
6709
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6713
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6714
test_fixture *fixture,
6715
__attribute__((unused))
6718
__attribute__((cleanup(cleanup_queue)))
6719
task_queue *queue = create_queue();
6720
g_assert_nonnull(queue);
6721
__attribute__((cleanup(string_set_clear)))
6722
string_set cancelled_filenames = {};
6723
bool quit_now = false;
6724
const char question_filename[] = "/nonexistent/question_filename";
6725
g_assert_true(string_set_add(&cancelled_filenames,
6726
question_filename));
6728
g_assert_true(add_to_queue(queue,
6729
(task_context){ .func=dummy_func }));
6731
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6732
g_assert_false(quit_now);
6733
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6734
g_assert_false(string_set_contains(cancelled_filenames,
6735
question_filename));
6739
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6740
test_fixture *fixture,
6741
__attribute__((unused))
6744
__attribute__((cleanup(cleanup_queue)))
6745
task_queue *queue = create_queue();
6746
g_assert_nonnull(queue);
6747
__attribute__((cleanup(string_set_clear)))
6748
string_set cancelled_filenames = {};
6749
bool quit_now = false;
6751
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6752
__attribute__((cleanup(cleanup_close)))
6753
const int read_pipe = pipefds[0];
6754
g_assert_cmpint(close(pipefds[1]), ==, 0);
6755
const char question_filename[] = "/nonexistent/question_filename";
6756
g_assert_true(string_set_add(&cancelled_filenames,
6757
question_filename));
6758
__attribute__((nonnull))
6759
void quit_func(const task_context task,
6760
__attribute__((unused)) task_queue *const q){
6761
g_assert_nonnull(task.quit_now);
6762
*task.quit_now = true;
6764
task_context task = {
6766
.question_filename=strdup(question_filename),
6767
.quit_now=&quit_now,
6770
g_assert_nonnull(task.question_filename);
6772
g_assert_true(add_to_queue(queue, task));
6774
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6775
g_assert_false(quit_now);
6777
/* read_pipe should be closed already */
6779
bool read_pipe_closed = (close(read_pipe) == -1);
6780
read_pipe_closed &= (errno == EBADF);
6781
g_assert_true(read_pipe_closed);
6784
static void test_run_queue_one_task(__attribute__((unused))
6785
test_fixture *fixture,
6786
__attribute__((unused))
6787
gconstpointer user_data){
6788
__attribute__((cleanup(cleanup_queue)))
6789
task_queue *queue = create_queue();
6790
g_assert_nonnull(queue);
6791
__attribute__((cleanup(string_set_clear)))
6792
string_set cancelled_filenames = {};
6793
bool quit_now = false;
6795
__attribute__((nonnull))
6796
void next_run_func(__attribute__((unused))
6797
const task_context task,
6798
task_queue *const q){
6802
task_context task = {
6803
.func=next_run_func,
6805
g_assert_true(add_to_queue(queue, task));
6807
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6808
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6809
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6812
static void test_run_queue_two_tasks(__attribute__((unused))
6813
test_fixture *fixture,
6814
__attribute__((unused))
6815
gconstpointer user_data){
6816
__attribute__((cleanup(cleanup_queue)))
6817
task_queue *queue = create_queue();
6818
g_assert_nonnull(queue);
6819
queue->next_run = 1;
6820
__attribute__((cleanup(string_set_clear)))
6821
string_set cancelled_filenames = {};
6822
bool quit_now = false;
6823
bool mandos_client_exited = false;
6825
__attribute__((nonnull))
6826
void next_run_func(__attribute__((unused))
6827
const task_context task,
6828
task_queue *const q){
6832
__attribute__((nonnull))
6833
void exited_func(const task_context task,
6834
__attribute__((unused)) task_queue *const q){
6835
*task.mandos_client_exited = true;
6838
task_context task1 = {
6839
.func=next_run_func,
6841
g_assert_true(add_to_queue(queue, task1));
6843
task_context task2 = {
6845
.mandos_client_exited=&mandos_client_exited,
6847
g_assert_true(add_to_queue(queue, task2));
6849
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6850
g_assert_false(quit_now);
6851
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6852
g_assert_true(mandos_client_exited);
6853
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6856
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6857
test_fixture *fixture,
6858
__attribute__((unused))
6859
gconstpointer user_data){
6860
__attribute__((cleanup(cleanup_queue)))
6861
task_queue *queue = create_queue();
6862
g_assert_nonnull(queue);
6863
__attribute__((cleanup(string_set_clear)))
6864
string_set cancelled_filenames = {};
6865
bool quit_now = false;
6866
bool mandos_client_exited = false;
6867
bool password_is_read = false;
6869
__attribute__((nonnull))
6870
void set_exited_func(const task_context task,
6871
__attribute__((unused)) task_queue *const q){
6872
*task.mandos_client_exited = true;
6873
*task.quit_now = true;
6875
task_context task1 = {
6876
.func=set_exited_func,
6877
.quit_now=&quit_now,
6878
.mandos_client_exited=&mandos_client_exited,
6880
g_assert_true(add_to_queue(queue, task1));
6882
__attribute__((nonnull))
6883
void set_read_func(const task_context task,
6884
__attribute__((unused)) task_queue *const q){
6885
*task.quit_now = true;
6886
*task.password_is_read = true;
6888
task_context task2 = {
6889
.func=set_read_func,
6890
.quit_now=&quit_now,
6891
.password_is_read=&password_is_read,
6893
g_assert_true(add_to_queue(queue, task2));
6895
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6896
g_assert_true(quit_now);
6897
g_assert_true(mandos_client_exited xor password_is_read);
6898
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6901
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6902
test_fixture *fixture,
6903
__attribute__((unused))
6904
gconstpointer user_data){
6905
__attribute__((cleanup(cleanup_queue)))
6906
task_queue *queue = create_queue();
6907
g_assert_nonnull(queue);
6908
__attribute__((cleanup(string_set_clear)))
6909
string_set cancelled_filenames = {};
6911
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6912
__attribute__((cleanup(cleanup_close)))
6913
const int read_pipe = pipefds[0];
6914
__attribute__((cleanup(cleanup_close)))
6915
const int write_pipe = pipefds[1];
6916
bool quit_now = false;
6918
__attribute__((nonnull))
6919
void read_func(const task_context task,
6920
__attribute__((unused)) task_queue *const q){
6921
*task.quit_now = true;
6923
task_context task1 = {
6925
.quit_now=&quit_now,
6928
g_assert_true(add_to_queue(queue, task1));
6930
__attribute__((nonnull))
6931
void write_func(const task_context task,
6932
__attribute__((unused)) task_queue *const q){
6933
*task.quit_now = true;
6935
task_context task2 = {
6937
.quit_now=&quit_now,
6940
g_assert_true(add_to_queue(queue, task2));
6942
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6943
g_assert_true(quit_now);
6945
/* Either read_pipe or write_pipe should be closed already */
6947
bool close_read_pipe = (close(read_pipe) == -1);
6948
close_read_pipe &= (errno == EBADF);
6950
bool close_write_pipe = (close(write_pipe) == -1);
6951
close_write_pipe &= (errno == EBADF);
6952
g_assert_true(close_read_pipe xor close_write_pipe);
6953
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6956
static void test_setup_signal_handler(__attribute__((unused))
6957
test_fixture *fixture,
6958
__attribute__((unused))
6959
gconstpointer user_data){
6960
/* Save current SIGCHLD action, whatever it is */
6961
struct sigaction expected_sigchld_action;
6962
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6965
/* Act; i.e. run the setup_signal_handler() function */
6966
struct sigaction actual_old_sigchld_action;
6967
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6969
/* Check that the function correctly set "actual_old_sigchld_action"
6970
to the same values as the previously saved
6971
"expected_sigchld_action" */
6972
/* Check member sa_handler */
6973
g_assert_true(actual_old_sigchld_action.sa_handler
6974
== expected_sigchld_action.sa_handler);
6975
/* Check member sa_mask */
6976
for(int signum = 1; signum < NSIG; signum++){
6977
const int expected_old_block_state
6978
= sigismember(&expected_sigchld_action.sa_mask, signum);
6979
g_assert_cmpint(expected_old_block_state, >=, 0);
6980
const int actual_old_block_state
6981
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6982
g_assert_cmpint(actual_old_block_state, >=, 0);
6983
g_assert_cmpint(actual_old_block_state,
6984
==, expected_old_block_state);
6986
/* Check member sa_flags */
6987
g_assert_true((actual_old_sigchld_action.sa_flags
6988
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6989
== (expected_sigchld_action.sa_flags
6990
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6992
/* Retrieve the current signal handler for SIGCHLD as set by
6993
setup_signal_handler() */
6994
struct sigaction actual_new_sigchld_action;
6995
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6996
&actual_new_sigchld_action), ==, 0);
6997
/* Check that the signal handler (member sa_handler) is correctly
6998
set to the "handle_sigchld" function */
6999
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7000
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7001
g_assert_true(actual_new_sigchld_action.sa_handler
7003
/* Check (in member sa_mask) that at least a handful of signals are
7004
actually blocked during the signal handler */
7005
for(int signum = 1; signum < NSIG; signum++){
7006
int actual_new_block_state;
7012
actual_new_block_state
7013
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7014
g_assert_cmpint(actual_new_block_state, ==, 1);
7016
case SIGKILL: /* non-blockable */
7017
case SIGSTOP: /* non-blockable */
7018
case SIGCHLD: /* always blocked */
7023
/* Check member sa_flags */
7024
g_assert_true((actual_new_sigchld_action.sa_flags
7025
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7026
== (SA_NOCLDSTOP | SA_RESTART));
7028
/* Restore signal handler */
7029
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7033
static void test_restore_signal_handler(__attribute__((unused))
7034
test_fixture *fixture,
7035
__attribute__((unused))
7036
gconstpointer user_data){
7037
/* Save current SIGCHLD action, whatever it is */
7038
struct sigaction expected_sigchld_action;
7039
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7041
/* Since we haven't established a signal handler yet, there should
7042
not be one established. But another test may have relied on
7043
restore_signal_handler() to restore the signal handler, and if
7044
restore_signal_handler() is buggy (which we should be prepared
7045
for in this test) the signal handler may not have been restored
7046
properly; check for this: */
7047
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7049
/* Establish a signal handler */
7050
struct sigaction sigchld_action = {
7051
.sa_handler=handle_sigchld,
7052
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7054
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7055
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7057
/* Act; i.e. run the restore_signal_handler() function */
7058
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7060
/* Retrieve the restored signal handler data */
7061
struct sigaction actual_restored_sigchld_action;
7062
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7063
&actual_restored_sigchld_action), ==, 0);
7065
/* Check that the function correctly restored the signal action, as
7066
saved in "actual_restored_sigchld_action", to the same values as
7067
the previously saved "expected_sigchld_action" */
7068
/* Check member sa_handler */
7069
g_assert_true(actual_restored_sigchld_action.sa_handler
7070
== expected_sigchld_action.sa_handler);
7071
/* Check member sa_mask */
7072
for(int signum = 1; signum < NSIG; signum++){
7073
const int expected_old_block_state
7074
= sigismember(&expected_sigchld_action.sa_mask, signum);
7075
g_assert_cmpint(expected_old_block_state, >=, 0);
7076
const int actual_restored_block_state
7077
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7078
g_assert_cmpint(actual_restored_block_state, >=, 0);
7079
g_assert_cmpint(actual_restored_block_state,
7080
==, expected_old_block_state);
7082
/* Check member sa_flags */
7083
g_assert_true((actual_restored_sigchld_action.sa_flags
7084
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7085
== (expected_sigchld_action.sa_flags
7086
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7089
static void test_block_sigchld(__attribute__((unused))
7090
test_fixture *fixture,
7091
__attribute__((unused))
7092
gconstpointer user_data){
7093
/* Save original signal mask */
7094
sigset_t expected_sigmask;
7095
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7098
/* Make sure SIGCHLD is unblocked for this test */
7099
sigset_t sigchld_sigmask;
7100
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7101
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7102
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7105
/* Act; i.e. run the block_sigchld() function */
7106
sigset_t actual_old_sigmask;
7107
g_assert_true(block_sigchld(&actual_old_sigmask));
7109
/* Check the actual_old_sigmask; it should be the same as the
7110
previously saved signal mask "expected_sigmask". */
7111
for(int signum = 1; signum < NSIG; signum++){
7112
const int expected_old_block_state
7113
= sigismember(&expected_sigmask, signum);
7114
g_assert_cmpint(expected_old_block_state, >=, 0);
7115
const int actual_old_block_state
7116
= sigismember(&actual_old_sigmask, signum);
7117
g_assert_cmpint(actual_old_block_state, >=, 0);
7118
g_assert_cmpint(actual_old_block_state,
7119
==, expected_old_block_state);
7122
/* Retrieve the newly set signal mask */
7123
sigset_t actual_sigmask;
7124
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7126
/* SIGCHLD should be blocked */
7127
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7129
/* Restore signal mask */
7130
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7134
static void test_restore_sigmask(__attribute__((unused))
7135
test_fixture *fixture,
7136
__attribute__((unused))
7137
gconstpointer user_data){
7138
/* Save original signal mask */
7139
sigset_t orig_sigmask;
7140
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7142
/* Make sure SIGCHLD is blocked for this test */
7143
sigset_t sigchld_sigmask;
7144
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7145
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7146
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7149
/* Act; i.e. run the restore_sigmask() function */
7150
g_assert_true(restore_sigmask(&orig_sigmask));
7152
/* Retrieve the newly restored signal mask */
7153
sigset_t restored_sigmask;
7154
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7157
/* Check the restored_sigmask; it should be the same as the
7158
previously saved signal mask "orig_sigmask". */
7159
for(int signum = 1; signum < NSIG; signum++){
7160
const int orig_block_state = sigismember(&orig_sigmask, signum);
7161
g_assert_cmpint(orig_block_state, >=, 0);
7162
const int restored_block_state = sigismember(&restored_sigmask,
7164
g_assert_cmpint(restored_block_state, >=, 0);
7165
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7168
/* Restore signal mask */
7169
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7173
static void test_parse_arguments_noargs(__attribute__((unused))
7174
test_fixture *fixture,
7175
__attribute__((unused))
7176
gconstpointer user_data){
7180
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7182
char *agent_directory = NULL;
7183
char *helper_directory = NULL;
7186
char *mandos_argz = NULL;
7187
size_t mandos_argz_length = 0;
7189
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7190
&helper_directory, &user, &group,
7191
&mandos_argz, &mandos_argz_length));
7192
g_assert_null(agent_directory);
7193
g_assert_null(helper_directory);
7194
g_assert_true(user == 0);
7195
g_assert_true(group == 0);
7196
g_assert_null(mandos_argz);
7197
g_assert_true(mandos_argz_length == 0);
7199
for(char **arg = argv; *arg != NULL; arg++){
7204
__attribute__((nonnull))
7205
static bool parse_arguments_devnull(int argc, char *argv[],
7206
const bool exit_failure,
7207
char **agent_directory,
7208
char **helper_directory,
7212
size_t *mandos_argz_length){
7214
FILE *real_stderr = stderr;
7215
FILE *devnull = fopen("/dev/null", "we");
7216
g_assert_nonnull(devnull);
7219
const bool ret = parse_arguments(argc, argv, exit_failure,
7221
helper_directory, user, group,
7222
mandos_argz, mandos_argz_length);
7223
const error_t saved_errno = errno;
7225
stderr = real_stderr;
7226
g_assert_cmpint(fclose(devnull), ==, 0);
7228
errno = saved_errno;
7233
static void test_parse_arguments_invalid(__attribute__((unused))
7234
test_fixture *fixture,
7235
__attribute__((unused))
7236
gconstpointer user_data){
7239
strdup("--invalid"),
7241
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7243
char *agent_directory = NULL;
7244
char *helper_directory = NULL;
7247
char *mandos_argz = NULL;
7248
size_t mandos_argz_length = 0;
7250
g_assert_false(parse_arguments_devnull(argc, argv, false,
7252
&helper_directory, &user,
7253
&group, &mandos_argz,
7254
&mandos_argz_length));
7256
g_assert_true(errno == EINVAL);
7257
g_assert_null(agent_directory);
7258
g_assert_null(helper_directory);
7259
g_assert_null(mandos_argz);
7260
g_assert_true(mandos_argz_length == 0);
7262
for(char **arg = argv; *arg != NULL; arg++){
7267
static void test_parse_arguments_long_dir(__attribute__((unused))
7268
test_fixture *fixture,
7269
__attribute__((unused))
7270
gconstpointer user_data){
7273
strdup("--agent-directory"),
7276
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7278
__attribute__((cleanup(cleanup_string)))
7279
char *agent_directory = NULL;
7280
char *helper_directory = NULL;
7283
__attribute__((cleanup(cleanup_string)))
7284
char *mandos_argz = NULL;
7285
size_t mandos_argz_length = 0;
7287
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7288
&helper_directory, &user, &group,
7289
&mandos_argz, &mandos_argz_length));
7291
g_assert_cmpstr(agent_directory, ==, "/tmp");
7292
g_assert_null(helper_directory);
7293
g_assert_true(user == 0);
7294
g_assert_true(group == 0);
7295
g_assert_null(mandos_argz);
7296
g_assert_true(mandos_argz_length == 0);
7298
for(char **arg = argv; *arg != NULL; arg++){
7303
static void test_parse_arguments_short_dir(__attribute__((unused))
7304
test_fixture *fixture,
7305
__attribute__((unused))
7306
gconstpointer user_data){
7312
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7314
__attribute__((cleanup(cleanup_string)))
7315
char *agent_directory = NULL;
7316
char *helper_directory = NULL;
7319
__attribute__((cleanup(cleanup_string)))
7320
char *mandos_argz = NULL;
7321
size_t mandos_argz_length = 0;
7323
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7324
&helper_directory, &user, &group,
7325
&mandos_argz, &mandos_argz_length));
7327
g_assert_cmpstr(agent_directory, ==, "/tmp");
7328
g_assert_null(helper_directory);
7329
g_assert_true(user == 0);
7330
g_assert_true(group == 0);
7331
g_assert_null(mandos_argz);
7332
g_assert_true(mandos_argz_length == 0);
7334
for(char **arg = argv; *arg != NULL; arg++){
7340
void test_parse_arguments_helper_directory(__attribute__((unused))
7341
test_fixture *fixture,
7342
__attribute__((unused))
7343
gconstpointer user_data){
7346
strdup("--helper-directory"),
7349
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7351
char *agent_directory = NULL;
7352
__attribute__((cleanup(cleanup_string)))
7353
char *helper_directory = NULL;
7356
__attribute__((cleanup(cleanup_string)))
7357
char *mandos_argz = NULL;
7358
size_t mandos_argz_length = 0;
7360
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7361
&helper_directory, &user, &group,
7362
&mandos_argz, &mandos_argz_length));
7364
g_assert_cmpstr(helper_directory, ==, "/tmp");
7365
g_assert_null(agent_directory);
7366
g_assert_true(user == 0);
7367
g_assert_true(group == 0);
7368
g_assert_null(mandos_argz);
7369
g_assert_true(mandos_argz_length == 0);
7371
for(char **arg = argv; *arg != NULL; arg++){
7377
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7378
test_fixture *fixture,
7379
__attribute__((unused))
7380
gconstpointer user_data){
7383
strdup("--plugin-helper-dir"),
7386
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7388
char *agent_directory = NULL;
7389
__attribute__((cleanup(cleanup_string)))
7390
char *helper_directory = NULL;
7393
__attribute__((cleanup(cleanup_string)))
7394
char *mandos_argz = NULL;
7395
size_t mandos_argz_length = 0;
7397
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7398
&helper_directory, &user, &group,
7399
&mandos_argz, &mandos_argz_length));
7401
g_assert_cmpstr(helper_directory, ==, "/tmp");
7402
g_assert_null(agent_directory);
7403
g_assert_true(user == 0);
7404
g_assert_true(group == 0);
7405
g_assert_null(mandos_argz);
7406
g_assert_true(mandos_argz_length == 0);
7408
for(char **arg = argv; *arg != NULL; arg++){
7413
static void test_parse_arguments_user(__attribute__((unused))
7414
test_fixture *fixture,
7415
__attribute__((unused))
7416
gconstpointer user_data){
7422
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7424
char *agent_directory = NULL;
7425
__attribute__((cleanup(cleanup_string)))
7426
char *helper_directory = NULL;
7429
__attribute__((cleanup(cleanup_string)))
7430
char *mandos_argz = NULL;
7431
size_t mandos_argz_length = 0;
7433
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7434
&helper_directory, &user, &group,
7435
&mandos_argz, &mandos_argz_length));
7437
g_assert_null(helper_directory);
7438
g_assert_null(agent_directory);
7439
g_assert_cmpuint((unsigned int)user, ==, 1000);
7440
g_assert_true(group == 0);
7441
g_assert_null(mandos_argz);
7442
g_assert_true(mandos_argz_length == 0);
7444
for(char **arg = argv; *arg != NULL; arg++){
7449
static void test_parse_arguments_user_invalid(__attribute__((unused))
7450
test_fixture *fixture,
7451
__attribute__((unused))
7459
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7461
char *agent_directory = NULL;
7462
__attribute__((cleanup(cleanup_string)))
7463
char *helper_directory = NULL;
7466
__attribute__((cleanup(cleanup_string)))
7467
char *mandos_argz = NULL;
7468
size_t mandos_argz_length = 0;
7470
g_assert_false(parse_arguments_devnull(argc, argv, false,
7472
&helper_directory, &user,
7473
&group, &mandos_argz,
7474
&mandos_argz_length));
7476
g_assert_null(helper_directory);
7477
g_assert_null(agent_directory);
7478
g_assert_cmpuint((unsigned int)user, ==, 0);
7479
g_assert_true(group == 0);
7480
g_assert_null(mandos_argz);
7481
g_assert_true(mandos_argz_length == 0);
7483
for(char **arg = argv; *arg != NULL; arg++){
7489
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7490
test_fixture *fixture,
7491
__attribute__((unused))
7492
gconstpointer user_data){
7498
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7500
char *agent_directory = NULL;
7501
__attribute__((cleanup(cleanup_string)))
7502
char *helper_directory = NULL;
7505
__attribute__((cleanup(cleanup_string)))
7506
char *mandos_argz = NULL;
7507
size_t mandos_argz_length = 0;
7509
g_assert_false(parse_arguments_devnull(argc, argv, false,
7511
&helper_directory, &user,
7512
&group, &mandos_argz,
7513
&mandos_argz_length));
7515
g_assert_null(helper_directory);
7516
g_assert_null(agent_directory);
7517
g_assert_cmpuint((unsigned int)user, ==, 0);
7518
g_assert_true(group == 0);
7519
g_assert_null(mandos_argz);
7520
g_assert_true(mandos_argz_length == 0);
7522
for(char **arg = argv; *arg != NULL; arg++){
7527
static void test_parse_arguments_group(__attribute__((unused))
7528
test_fixture *fixture,
7529
__attribute__((unused))
7530
gconstpointer user_data){
7536
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7538
char *agent_directory = NULL;
7539
__attribute__((cleanup(cleanup_string)))
7540
char *helper_directory = NULL;
7543
__attribute__((cleanup(cleanup_string)))
7544
char *mandos_argz = NULL;
7545
size_t mandos_argz_length = 0;
7547
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7548
&helper_directory, &user, &group,
7549
&mandos_argz, &mandos_argz_length));
7551
g_assert_null(helper_directory);
7552
g_assert_null(agent_directory);
7553
g_assert_true(user == 0);
7554
g_assert_cmpuint((unsigned int)group, ==, 1000);
7555
g_assert_null(mandos_argz);
7556
g_assert_true(mandos_argz_length == 0);
7558
for(char **arg = argv; *arg != NULL; arg++){
7563
static void test_parse_arguments_group_invalid(__attribute__((unused))
7564
test_fixture *fixture,
7565
__attribute__((unused))
7573
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7575
char *agent_directory = NULL;
7576
__attribute__((cleanup(cleanup_string)))
7577
char *helper_directory = NULL;
7580
__attribute__((cleanup(cleanup_string)))
7581
char *mandos_argz = NULL;
7582
size_t mandos_argz_length = 0;
7584
g_assert_false(parse_arguments_devnull(argc, argv, false,
7586
&helper_directory, &user,
7587
&group, &mandos_argz,
7588
&mandos_argz_length));
7590
g_assert_null(helper_directory);
7591
g_assert_null(agent_directory);
7592
g_assert_true(user == 0);
7593
g_assert_true(group == 0);
7594
g_assert_null(mandos_argz);
7595
g_assert_true(mandos_argz_length == 0);
7597
for(char **arg = argv; *arg != NULL; arg++){
7603
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7604
test_fixture *fixture,
7605
__attribute__((unused))
7606
gconstpointer user_data){
7612
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7614
char *agent_directory = NULL;
7615
__attribute__((cleanup(cleanup_string)))
7616
char *helper_directory = NULL;
7619
__attribute__((cleanup(cleanup_string)))
7620
char *mandos_argz = NULL;
7621
size_t mandos_argz_length = 0;
7623
g_assert_false(parse_arguments_devnull(argc, argv, false,
7625
&helper_directory, &user,
7626
&group, &mandos_argz,
7627
&mandos_argz_length));
7629
g_assert_null(helper_directory);
7630
g_assert_null(agent_directory);
7631
g_assert_cmpuint((unsigned int)group, ==, 0);
7632
g_assert_true(group == 0);
7633
g_assert_null(mandos_argz);
7634
g_assert_true(mandos_argz_length == 0);
7636
for(char **arg = argv; *arg != NULL; arg++){
7641
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7642
test_fixture *fixture,
7643
__attribute__((unused))
7648
strdup("mandos-client"),
7650
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7652
__attribute__((cleanup(cleanup_string)))
7653
char *agent_directory = NULL;
7654
__attribute__((cleanup(cleanup_string)))
7655
char *helper_directory = NULL;
7658
__attribute__((cleanup(cleanup_string)))
7659
char *mandos_argz = NULL;
7660
size_t mandos_argz_length = 0;
7662
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7663
&helper_directory, &user, &group,
7664
&mandos_argz, &mandos_argz_length));
7666
g_assert_null(agent_directory);
7667
g_assert_null(helper_directory);
7668
g_assert_true(user == 0);
7669
g_assert_true(group == 0);
7670
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7671
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7672
mandos_argz_length),
7675
for(char **arg = argv; *arg != NULL; arg++){
7680
static void test_parse_arguments_mandos_args(__attribute__((unused))
7681
test_fixture *fixture,
7682
__attribute__((unused))
7683
gconstpointer user_data){
7686
strdup("mandos-client"),
7691
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7693
__attribute__((cleanup(cleanup_string)))
7694
char *agent_directory = NULL;
7695
__attribute__((cleanup(cleanup_string)))
7696
char *helper_directory = NULL;
7699
__attribute__((cleanup(cleanup_string)))
7700
char *mandos_argz = NULL;
7701
size_t mandos_argz_length = 0;
7703
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7704
&helper_directory, &user, &group,
7705
&mandos_argz, &mandos_argz_length));
7707
g_assert_null(agent_directory);
7708
g_assert_null(helper_directory);
7709
g_assert_true(user == 0);
7710
g_assert_true(group == 0);
7711
char *marg = mandos_argz;
7712
g_assert_cmpstr(marg, ==, "mandos-client");
7713
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7714
g_assert_cmpstr(marg, ==, "one");
7715
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7716
g_assert_cmpstr(marg, ==, "two");
7717
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7718
g_assert_cmpstr(marg, ==, "three");
7719
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7720
mandos_argz_length),
7723
for(char **arg = argv; *arg != NULL; arg++){
7728
static void test_parse_arguments_all_args(__attribute__((unused))
7729
test_fixture *fixture,
7730
__attribute__((unused))
7731
gconstpointer user_data){
7734
strdup("--agent-directory"),
7736
strdup("--helper-directory"),
7742
strdup("mandos-client"),
7747
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7749
__attribute__((cleanup(cleanup_string)))
7750
char *agent_directory = NULL;
7751
__attribute__((cleanup(cleanup_string)))
7752
char *helper_directory = NULL;
7755
__attribute__((cleanup(cleanup_string)))
7756
char *mandos_argz = NULL;
7757
size_t mandos_argz_length = 0;
7759
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7760
&helper_directory, &user, &group,
7761
&mandos_argz, &mandos_argz_length));
7763
g_assert_cmpstr(agent_directory, ==, "/tmp");
7764
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7765
g_assert_true(user == 1);
7766
g_assert_true(group == 2);
7767
char *marg = mandos_argz;
7768
g_assert_cmpstr(marg, ==, "mandos-client");
7769
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7770
g_assert_cmpstr(marg, ==, "one");
7771
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7772
g_assert_cmpstr(marg, ==, "two");
7773
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7774
g_assert_cmpstr(marg, ==, "three");
7775
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7776
mandos_argz_length),
7779
for(char **arg = argv; *arg != NULL; arg++){
7784
static void test_parse_arguments_mixed(__attribute__((unused))
7785
test_fixture *fixture,
7786
__attribute__((unused))
7787
gconstpointer user_data){
7790
strdup("mandos-client"),
7794
strdup("--agent-directory"),
7798
strdup("--helper-directory=/var/tmp"),
7800
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7802
__attribute__((cleanup(cleanup_string)))
7803
char *agent_directory = NULL;
7804
__attribute__((cleanup(cleanup_string)))
7805
char *helper_directory = NULL;
7808
__attribute__((cleanup(cleanup_string)))
7809
char *mandos_argz = NULL;
7810
size_t mandos_argz_length = 0;
7812
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7813
&helper_directory, &user, &group,
7814
&mandos_argz, &mandos_argz_length));
7816
g_assert_cmpstr(agent_directory, ==, "/tmp");
7817
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7818
g_assert_true(user == 1);
7819
g_assert_true(group == 0);
7820
char *marg = mandos_argz;
7821
g_assert_cmpstr(marg, ==, "mandos-client");
7822
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7823
g_assert_cmpstr(marg, ==, "one");
7824
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7825
g_assert_cmpstr(marg, ==, "two");
7826
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7827
g_assert_cmpstr(marg, ==, "three");
7828
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7829
mandos_argz_length),
7832
for(char **arg = argv; *arg != NULL; arg++){
7837
/* End of tests section */
7839
/* Test boilerplate section; New tests should be added to the test
7840
suite definition here, in the "run_tests" function.
7842
Finally, this section also contains the should_only_run_tests()
7843
function used by main() for deciding if tests should be run or to
7846
__attribute__((cold))
7847
static bool run_tests(int argc, char *argv[]){
7848
g_test_init(&argc, &argv, NULL);
7850
/* A macro to add a test with no setup or teardown functions */
7851
#define test_add(testpath, testfunc) \
7853
g_test_add((testpath), test_fixture, NULL, NULL, \
7854
(testfunc), NULL); \
7857
/* Test the signal-related functions first, since some other tests
7858
depend on these functions in their setups and teardowns */
7859
test_add("/signal-handling/setup", test_setup_signal_handler);
7860
test_add("/signal-handling/restore", test_restore_signal_handler);
7861
test_add("/signal-handling/block", test_block_sigchld);
7862
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7864
/* Regular non-signal-related tests; these use no setups or
7866
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7867
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7868
test_add("/parse_arguments/long-dir",
7869
test_parse_arguments_long_dir);
7870
test_add("/parse_arguments/short-dir",
7871
test_parse_arguments_short_dir);
7872
test_add("/parse_arguments/helper-directory",
7873
test_parse_arguments_helper_directory);
7874
test_add("/parse_arguments/plugin-helper-dir",
7875
test_parse_arguments_plugin_helper_dir);
7876
test_add("/parse_arguments/user", test_parse_arguments_user);
7877
test_add("/parse_arguments/user-invalid",
7878
test_parse_arguments_user_invalid);
7879
test_add("/parse_arguments/user-zero-invalid",
7880
test_parse_arguments_user_zero_invalid);
7881
test_add("/parse_arguments/group", test_parse_arguments_group);
7882
test_add("/parse_arguments/group-invalid",
7883
test_parse_arguments_group_invalid);
7884
test_add("/parse_arguments/group-zero-invalid",
7885
test_parse_arguments_group_zero_invalid);
7886
test_add("/parse_arguments/mandos-noargs",
7887
test_parse_arguments_mandos_noargs);
7888
test_add("/parse_arguments/mandos-args",
7889
test_parse_arguments_mandos_args);
7890
test_add("/parse_arguments/all-args",
7891
test_parse_arguments_all_args);
7892
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7893
test_add("/queue/create", test_create_queue);
7894
test_add("/queue/add", test_add_to_queue);
7895
test_add("/queue/add/overflow", test_add_to_queue_overflow);
7896
test_add("/queue/has_question/empty",
7897
test_queue_has_question_empty);
7898
test_add("/queue/has_question/false",
7899
test_queue_has_question_false);
7900
test_add("/queue/has_question/true", test_queue_has_question_true);
7901
test_add("/queue/has_question/false2",
7902
test_queue_has_question_false2);
7903
test_add("/queue/has_question/true2",
7904
test_queue_has_question_true2);
7905
test_add("/buffer/cleanup", test_cleanup_buffer);
7906
test_add("/string_set/net-set-contains-nothing",
7907
test_string_set_new_set_contains_nothing);
7908
test_add("/string_set/with-added-string-contains-it",
7909
test_string_set_with_added_string_contains_it);
7910
test_add("/string_set/cleared-does-not-contain-string",
7911
test_string_set_cleared_does_not_contain_str);
7912
test_add("/string_set/swap/one-with-empty",
7913
test_string_set_swap_one_with_empty);
7914
test_add("/string_set/swap/empty-with-one",
7915
test_string_set_swap_empty_with_one);
7916
test_add("/string_set/swap/one-with-one",
7917
test_string_set_swap_one_with_one);
7919
/* A macro to add a test using the setup and teardown functions */
7920
#define test_add_st(path, func) \
7922
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7926
/* Signal-related tests; these use setups and teardowns which
7927
establish, during each test run, a signal handler for, and a
7928
signal mask blocking, the SIGCHLD signal, just like main() */
7929
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7930
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7931
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7932
test_add_st("/run_queue/zeroes-next-run",
7933
test_run_queue_zeroes_next_run);
7934
test_add_st("/run_queue/clears-cancelled_filenames",
7935
test_run_queue_clears_cancelled_filenames);
7936
test_add_st("/run_queue/skips-cancelled-filenames",
7937
test_run_queue_skips_cancelled_filenames);
7938
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7939
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7940
test_add_st("/run_queue/two-tasks/quit",
7941
test_run_queue_two_tasks_quit);
7942
test_add_st("/run_queue/two-tasks-cleanup",
7943
test_run_queue_two_tasks_cleanup);
7944
test_add_st("/task-creators/start_mandos_client",
7945
test_start_mandos_client);
7946
test_add_st("/task-creators/start_mandos_client/execv",
7947
test_start_mandos_client_execv);
7948
test_add_st("/task-creators/start_mandos_client/suid/euid",
7949
test_start_mandos_client_suid_euid);
7950
test_add_st("/task-creators/start_mandos_client/suid/egid",
7951
test_start_mandos_client_suid_egid);
7952
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7953
test_start_mandos_client_suid_ruid);
7954
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7955
test_start_mandos_client_suid_rgid);
7956
test_add_st("/task-creators/start_mandos_client/read",
7957
test_start_mandos_client_read);
7958
test_add_st("/task-creators/start_mandos_client/helper-directory",
7959
test_start_mandos_client_helper_directory);
7960
test_add_st("/task-creators/start_mandos_client/sigmask",
7961
test_start_mandos_client_sigmask);
7962
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7963
test_wait_for_mandos_client_exit_badpid);
7964
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7965
test_wait_for_mandos_client_exit_noexit);
7966
test_add_st("/task/wait_for_mandos_client_exit/success",
7967
test_wait_for_mandos_client_exit_success);
7968
test_add_st("/task/wait_for_mandos_client_exit/failure",
7969
test_wait_for_mandos_client_exit_failure);
7970
test_add_st("/task/wait_for_mandos_client_exit/killed",
7971
test_wait_for_mandos_client_exit_killed);
7972
test_add_st("/task/read_mandos_client_output/readerror",
7973
test_read_mandos_client_output_readerror);
7974
test_add_st("/task/read_mandos_client_output/nodata",
7975
test_read_mandos_client_output_nodata);
7976
test_add_st("/task/read_mandos_client_output/eof",
7977
test_read_mandos_client_output_eof);
7978
test_add_st("/task/read_mandos_client_output/once",
7979
test_read_mandos_client_output_once);
7980
test_add_st("/task/read_mandos_client_output/malloc",
7981
test_read_mandos_client_output_malloc);
7982
test_add_st("/task/read_mandos_client_output/append",
7983
test_read_mandos_client_output_append);
7984
test_add_st("/task-creators/add_inotify_dir_watch",
7985
test_add_inotify_dir_watch);
7986
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7987
test_add_inotify_dir_watch_fail);
7988
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7989
test_add_inotify_dir_watch_nondir);
7990
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7991
test_add_inotify_dir_watch_EAGAIN);
7992
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7993
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7994
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7995
test_add_inotify_dir_watch_IN_MOVED_TO);
7996
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7997
test_add_inotify_dir_watch_IN_MOVED_FROM);
7998
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7999
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8000
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8001
test_add_inotify_dir_watch_IN_DELETE);
8002
test_add_st("/task/read_inotify_event/readerror",
8003
test_read_inotify_event_readerror);
8004
test_add_st("/task/read_inotify_event/bad-epoll",
8005
test_read_inotify_event_bad_epoll);
8006
test_add_st("/task/read_inotify_event/nodata",
8007
test_read_inotify_event_nodata);
8008
test_add_st("/task/read_inotify_event/eof",
8009
test_read_inotify_event_eof);
8010
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8011
test_read_inotify_event_IN_CLOSE_WRITE);
8012
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8013
test_read_inotify_event_IN_MOVED_TO);
8014
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8015
test_read_inotify_event_IN_MOVED_FROM);
8016
test_add_st("/task/read_inotify_event/IN_DELETE",
8017
test_read_inotify_event_IN_DELETE);
8018
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8019
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8020
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8021
test_read_inotify_event_IN_MOVED_TO_badname);
8022
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8023
test_read_inotify_event_IN_MOVED_FROM_badname);
8024
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8025
test_read_inotify_event_IN_DELETE_badname);
8026
test_add_st("/task/open_and_parse_question/ENOENT",
8027
test_open_and_parse_question_ENOENT);
8028
test_add_st("/task/open_and_parse_question/EIO",
8029
test_open_and_parse_question_EIO);
8030
test_add_st("/task/open_and_parse_question/parse-error",
8031
test_open_and_parse_question_parse_error);
8032
test_add_st("/task/open_and_parse_question/nosocket",
8033
test_open_and_parse_question_nosocket);
8034
test_add_st("/task/open_and_parse_question/badsocket",
8035
test_open_and_parse_question_badsocket);
8036
test_add_st("/task/open_and_parse_question/nopid",
8037
test_open_and_parse_question_nopid);
8038
test_add_st("/task/open_and_parse_question/badpid",
8039
test_open_and_parse_question_badpid);
8040
test_add_st("/task/open_and_parse_question/noexist_pid",
8041
test_open_and_parse_question_noexist_pid);
8042
test_add_st("/task/open_and_parse_question/no-notafter",
8043
test_open_and_parse_question_no_notafter);
8044
test_add_st("/task/open_and_parse_question/bad-notafter",
8045
test_open_and_parse_question_bad_notafter);
8046
test_add_st("/task/open_and_parse_question/notafter-0",
8047
test_open_and_parse_question_notafter_0);
8048
test_add_st("/task/open_and_parse_question/notafter-1",
8049
test_open_and_parse_question_notafter_1);
8050
test_add_st("/task/open_and_parse_question/notafter-1-1",
8051
test_open_and_parse_question_notafter_1_1);
8052
test_add_st("/task/open_and_parse_question/notafter-1-2",
8053
test_open_and_parse_question_notafter_1_2);
8054
test_add_st("/task/open_and_parse_question/equal-notafter",
8055
test_open_and_parse_question_equal_notafter);
8056
test_add_st("/task/open_and_parse_question/late-notafter",
8057
test_open_and_parse_question_late_notafter);
8058
test_add_st("/task/cancel_old_question/0-1-2",
8059
test_cancel_old_question_0_1_2);
8060
test_add_st("/task/cancel_old_question/0-2-1",
8061
test_cancel_old_question_0_2_1);
8062
test_add_st("/task/cancel_old_question/1-2-3",
8063
test_cancel_old_question_1_2_3);
8064
test_add_st("/task/cancel_old_question/1-3-2",
8065
test_cancel_old_question_1_3_2);
8066
test_add_st("/task/cancel_old_question/2-1-3",
8067
test_cancel_old_question_2_1_3);
8068
test_add_st("/task/cancel_old_question/2-3-1",
8069
test_cancel_old_question_2_3_1);
8070
test_add_st("/task/cancel_old_question/3-1-2",
8071
test_cancel_old_question_3_1_2);
8072
test_add_st("/task/cancel_old_question/3-2-1",
8073
test_cancel_old_question_3_2_1);
8074
test_add_st("/task/connect_question_socket/name-too-long",
8075
test_connect_question_socket_name_too_long);
8076
test_add_st("/task/connect_question_socket/connect-fail",
8077
test_connect_question_socket_connect_fail);
8078
test_add_st("/task/connect_question_socket/bad-epoll",
8079
test_connect_question_socket_bad_epoll);
8080
test_add_st("/task/connect_question_socket/usable",
8081
test_connect_question_socket_usable);
8082
test_add_st("/task/send_password_to_socket/client-not-exited",
8083
test_send_password_to_socket_client_not_exited);
8084
test_add_st("/task/send_password_to_socket/password-not-read",
8085
test_send_password_to_socket_password_not_read);
8086
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8087
test_send_password_to_socket_EMSGSIZE);
8088
test_add_st("/task/send_password_to_socket/retry",
8089
test_send_password_to_socket_retry);
8090
test_add_st("/task/send_password_to_socket/bad-epoll",
8091
test_send_password_to_socket_bad_epoll);
8092
test_add_st("/task/send_password_to_socket/null-password",
8093
test_send_password_to_socket_null_password);
8094
test_add_st("/task/send_password_to_socket/empty-password",
8095
test_send_password_to_socket_empty_password);
8096
test_add_st("/task/send_password_to_socket/empty-str-password",
8097
test_send_password_to_socket_empty_str_pass);
8098
test_add_st("/task/send_password_to_socket/text-password",
8099
test_send_password_to_socket_text_password);
8100
test_add_st("/task/send_password_to_socket/binary-password",
8101
test_send_password_to_socket_binary_password);
8102
test_add_st("/task/send_password_to_socket/nuls-in-password",
8103
test_send_password_to_socket_nuls_in_password);
8104
test_add_st("/task-creators/add_existing_questions/ENOENT",
8105
test_add_existing_questions_ENOENT);
8106
test_add_st("/task-creators/add_existing_questions/no-questions",
8107
test_add_existing_questions_no_questions);
8108
test_add_st("/task-creators/add_existing_questions/one-question",
8109
test_add_existing_questions_one_question);
8110
test_add_st("/task-creators/add_existing_questions/two-questions",
8111
test_add_existing_questions_two_questions);
8112
test_add_st("/task-creators/add_existing_questions/non-questions",
8113
test_add_existing_questions_non_questions);
8114
test_add_st("/task-creators/add_existing_questions/both-types",
8115
test_add_existing_questions_both_types);
8117
return g_test_run() == 0;
8120
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8121
GOptionContext *context = g_option_context_new("");
8123
g_option_context_set_help_enabled(context, FALSE);
8124
g_option_context_set_ignore_unknown_options(context, TRUE);
8126
gboolean should_run_tests = FALSE;
8127
GOptionEntry entries[] = {
8128
{ "test", 0, 0, G_OPTION_ARG_NONE,
8129
&should_run_tests, "Run tests", NULL },
8132
g_option_context_add_main_entries(context, entries, NULL);
8134
GError *error = NULL;
8136
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8137
g_option_context_free(context);
8138
g_error("Failed to parse options: %s", error->message);
8141
g_option_context_free(context);
8142
return should_run_tests != FALSE;