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, EEXIST,
52
ECHILD, EPERM, ENOMEM, EAGAIN,
53
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 <unistd.h> /* uid_t, gid_t, close(), pipe2(),
77
fork(), _exit(), dup2(),
78
STDOUT_FILENO, setresgid(),
79
setresuid(), execv(), ssize_t,
80
read(), dup3(), getuid(), dup(),
81
STDERR_FILENO, pause(), write(),
82
rmdir(), unlink(), getpid() */
83
#include <sys/mman.h> /* munlock(), mlock() */
84
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
F_GETFD, F_GETFL, FD_CLOEXEC,
86
open(), O_WRONLY, O_RDONLY */
87
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
89
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
90
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
91
IN_CLOEXEC, inotify_add_watch(),
92
IN_CLOSE_WRITE, IN_MOVED_TO,
93
IN_DELETE, struct inotify_event */
94
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
95
#include <stdio.h> /* asprintf(), FILE, fopen(),
96
getline(), sscanf(), feof(),
97
ferror(), fclose(), stderr,
98
rename(), fdopen(), fprintf(),
100
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
GError, g_key_file_load_from_file(),
102
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
103
g_key_file_get_string(), guint64,
104
g_key_file_get_uint64(),
105
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
106
g_assert_true(), g_assert_nonnull(),
107
g_assert_null(), g_assert_false(),
108
g_assert_cmpint(), g_assert_cmpuint(),
109
g_test_skip(), g_assert_cmpstr(),
110
g_test_init(), g_test_add(), g_test_run(),
111
GOptionContext, g_option_context_new(),
112
g_option_context_set_help_enabled(), FALSE,
113
g_option_context_set_ignore_unknown_options(),
114
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
115
g_option_context_add_main_entries(),
116
g_option_context_parse(),
117
g_option_context_free(), g_error() */
118
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
119
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
120
SOCK_DGRAM, SOCK_NONBLOCK,
121
SOCK_CLOEXEC, connect(),
122
struct sockaddr, socklen_t,
123
shutdown(), SHUT_RD, send(),
124
MSG_NOSIGNAL, bind(), recv(),
126
#include <glob.h> /* globfree(), glob_t, glob(),
127
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
128
GLOB_ABORTED, GLOB_NOMATCH,
131
/* End of includes */
133
/* Start of declarations of private types and functions */
135
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
136
typedef uintmax_t mono_microsecs;
138
/* "task_queue" - A queue of tasks to be run */
140
struct task_struct *tasks; /* Tasks in this queue */
141
size_t length; /* Number of tasks */
142
/* Memory allocated for "tasks", in bytes */
144
/* Time when this queue should be run, at the latest */
145
mono_microsecs next_run;
146
} __attribute__((designated_init)) task_queue;
148
/* "func_type" - A function type for task functions
150
I.e. functions for the code which runs when a task is run, all have
152
typedef void (task_func) (const struct task_struct,
154
__attribute__((nonnull));
156
/* "buffer" - A data buffer for a growing array of bytes
158
Used for the "password" variable */
163
} __attribute__((designated_init)) buffer;
165
/* "string_set" - A set type which can contain strings
167
Used by the "cancelled_filenames" variable */
169
char *argz; /* Do not access these except in */
170
size_t argz_len; /* the string_set_* functions */
171
} __attribute__((designated_init)) string_set;
173
/* "task_context" - local variables for tasks
175
This data structure distinguishes between different tasks which are
176
using the same function. This data structure is passed to every
177
task function when each task is run.
179
Note that not every task uses every struct member. */
180
typedef struct task_struct {
181
task_func *const func; /* The function run by this task */
182
char *const question_filename; /* The question file */
183
const pid_t pid; /* Mandos client process ID */
184
const int epoll_fd; /* The epoll set file descriptor */
185
bool *const quit_now; /* Set to true on fatal errors */
186
const int fd; /* General purpose file descriptor */
187
bool *const mandos_client_exited; /* Set true when client exits */
188
buffer *const password; /* As read from client process */
189
bool *const password_is_read; /* "password" is done growing */
190
char *filename; /* General purpose file name */
191
/* A set of strings of all the file names of questions which have
192
been cancelled for any reason; tasks pertaining to these question
193
files should not be run */
194
string_set *const cancelled_filenames;
195
const mono_microsecs notafter; /* "NotAfter" from question file */
196
/* Updated before each queue run; is compared with queue.next_run */
197
const mono_microsecs *const current_time;
198
} __attribute__((designated_init)) task_context;
200
/* Declare all our functions here so we can define them in any order
201
below. Note: test functions are *not* declared here, they are
202
declared in the test section. */
203
__attribute__((warn_unused_result))
204
static bool should_only_run_tests(int *, char **[]);
205
__attribute__((warn_unused_result, cold))
206
static bool run_tests(int, char *[]);
207
static void handle_sigchld(__attribute__((unused)) int sig){}
208
__attribute__((warn_unused_result, malloc))
209
task_queue *create_queue(void);
210
__attribute__((nonnull, warn_unused_result))
211
bool add_to_queue(task_queue *const, const task_context);
212
__attribute__((nonnull))
213
void cleanup_task(const task_context *const);
214
__attribute__((nonnull))
215
void cleanup_queue(task_queue *const *const);
216
__attribute__((pure, nonnull, warn_unused_result))
217
bool queue_has_question(const task_queue *const);
218
__attribute__((nonnull))
219
void cleanup_close(const int *const);
220
__attribute__((nonnull))
221
void cleanup_string(char *const *const);
222
__attribute__((nonnull))
223
void cleanup_buffer(buffer *const);
224
__attribute__((pure, nonnull, warn_unused_result))
225
bool string_set_contains(const string_set, const char *const);
226
__attribute__((nonnull, warn_unused_result))
227
bool string_set_add(string_set *const, const char *const);
228
__attribute__((nonnull))
229
void string_set_clear(string_set *);
230
void string_set_swap(string_set *const, string_set *const);
231
__attribute__((nonnull, warn_unused_result))
232
bool start_mandos_client(task_queue *const, const int, bool *const,
233
bool *const, buffer *const, bool *const,
234
const struct sigaction *const,
235
const sigset_t, const char *const,
236
const uid_t, const gid_t,
237
const char *const *const);
238
__attribute__((nonnull))
239
task_func wait_for_mandos_client_exit;
240
__attribute__((nonnull))
241
task_func read_mandos_client_output;
242
__attribute__((warn_unused_result))
243
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
244
buffer *const, const char *const,
245
string_set *, const mono_microsecs *const,
246
bool *const, bool *const);
247
__attribute__((nonnull))
248
task_func read_inotify_event;
249
__attribute__((nonnull))
250
task_func open_and_parse_question;
251
__attribute__((nonnull))
252
task_func cancel_old_question;
253
__attribute__((nonnull))
254
task_func connect_question_socket;
255
__attribute__((nonnull))
256
task_func send_password_to_socket;
257
__attribute__((warn_unused_result))
258
bool add_existing_questions(task_queue *const, const int,
259
buffer *const, string_set *,
260
const mono_microsecs *const,
261
bool *const, bool *const,
263
__attribute__((nonnull, warn_unused_result))
264
bool wait_for_event(const int, const mono_microsecs,
265
const mono_microsecs);
266
bool run_queue(task_queue **const, string_set *const, bool *const);
267
bool clear_all_fds_from_epoll_set(const int);
268
mono_microsecs get_current_time(void);
269
__attribute__((nonnull, warn_unused_result))
270
bool setup_signal_handler(struct sigaction *const);
271
__attribute__((nonnull))
272
bool restore_signal_handler(const struct sigaction *const);
273
__attribute__((nonnull, warn_unused_result))
274
bool block_sigchld(sigset_t *const);
275
__attribute__((nonnull))
276
bool restore_sigmask(const sigset_t *const);
277
__attribute__((nonnull))
278
bool parse_arguments(int, char *[], const bool, char **, char **,
279
uid_t *const , gid_t *const, char **, size_t *);
281
/* End of declarations of private types and functions */
283
/* Start of "main" section; this section LACKS TESTS!
285
Code here should be as simple as possible. */
287
/* These are required to be global by Argp */
288
const char *argp_program_version = "password-agent " VERSION;
289
const char *argp_program_bug_address = "<mandos@recompile.se>";
291
int main(int argc, char *argv[]){
293
/* If the --test option is passed, skip all normal operations and
294
instead only run the run_tests() function, which also does all
295
its own option parsing, so we don't have to do anything here. */
296
if(should_only_run_tests(&argc, &argv)){
297
if(run_tests(argc, argv)){
298
return EXIT_SUCCESS; /* All tests successful */
300
return EXIT_FAILURE; /* Some test(s) failed */
303
__attribute__((cleanup(cleanup_string)))
304
char *agent_directory = NULL;
306
__attribute__((cleanup(cleanup_string)))
307
char *helper_directory = NULL;
312
__attribute__((cleanup(cleanup_string)))
313
char *mandos_argz = NULL;
314
size_t mandos_argz_length = 0;
316
if(not parse_arguments(argc, argv, true, &agent_directory,
317
&helper_directory, &user, &group,
318
&mandos_argz, &mandos_argz_length)){
319
/* This should never happen, since "true" is passed as the third
320
argument to parse_arguments() above, which should make
321
argp_parse() call exit() if any parsing error occurs. */
322
error(EX_USAGE, errno, "Failed to parse arguments");
325
const char default_agent_directory[] = "/run/systemd/ask-password";
326
const char default_helper_directory[]
327
= "/lib/mandos/plugin-helpers";
328
const char *const default_argv[]
329
= {"/lib/mandos/plugins.d/mandos-client", NULL };
331
/* Set variables to default values if unset */
332
if(agent_directory == NULL){
333
agent_directory = strdup(default_agent_directory);
334
if(agent_directory == NULL){
335
error(EX_OSERR, errno, "Failed strdup()");
338
if(helper_directory == NULL){
339
helper_directory = strdup(default_helper_directory);
340
if(helper_directory == NULL){
341
error(EX_OSERR, errno, "Failed strdup()");
345
user = 65534; /* nobody */
348
group = 65534; /* nogroup */
350
/* If parse_opt did not create an argz vector, create one with
352
if(mandos_argz == NULL){
354
#pragma GCC diagnostic push
355
/* argz_create() takes a non-const argv for some unknown reason -
356
argz_create() isn't modifying the strings, just copying them.
357
Therefore, this cast to non-const should be safe. */
358
#pragma GCC diagnostic ignored "-Wcast-qual"
360
errno = argz_create((char *const *)default_argv, &mandos_argz,
361
&mandos_argz_length);
363
#pragma GCC diagnostic pop
366
error(EX_OSERR, errno, "Failed argz_create()");
369
/* Use argz vector to create a normal argv, usable by execv() */
371
char **mandos_argv = malloc((argz_count(mandos_argz,
373
+ 1) * sizeof(char *));
374
if(mandos_argv == NULL){
375
error_t saved_errno = errno;
377
error(EX_OSERR, saved_errno, "Failed malloc()");
379
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
381
sigset_t orig_sigmask;
382
if(not block_sigchld(&orig_sigmask)){
386
struct sigaction old_sigchld_action;
387
if(not setup_signal_handler(&old_sigchld_action)){
391
mono_microsecs current_time = 0;
393
bool mandos_client_exited = false;
394
bool quit_now = false;
395
__attribute__((cleanup(cleanup_close)))
396
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
398
error(EX_OSERR, errno, "Failed to create epoll set fd");
400
__attribute__((cleanup(cleanup_queue)))
401
task_queue *queue = create_queue();
403
error(EX_OSERR, errno, "Failed to create task queue");
406
__attribute__((cleanup(cleanup_buffer)))
407
buffer password = {};
408
bool password_is_read = false;
410
__attribute__((cleanup(string_set_clear)))
411
string_set cancelled_filenames = {};
413
/* Add tasks to queue */
414
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
415
&quit_now, &password, &password_is_read,
416
&old_sigchld_action, orig_sigmask,
417
helper_directory, user, group,
418
(const char *const *)mandos_argv)){
419
return EX_OSERR; /* Error has already been printed */
421
/* These variables were only for start_mandos_client() and are not
426
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
427
agent_directory, &cancelled_filenames,
428
¤t_time, &mandos_client_exited,
430
switch(errno){ /* Error has already been printed */
439
if(not add_existing_questions(queue, epoll_fd, &password,
440
&cancelled_filenames, ¤t_time,
441
&mandos_client_exited,
442
&password_is_read, agent_directory)){
443
return EXIT_FAILURE; /* Error has already been printed */
448
current_time = get_current_time();
449
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
450
const error_t saved_errno = errno;
451
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
455
current_time = get_current_time();
456
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
457
const error_t saved_errno = errno;
458
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
461
/* When no tasks about questions are left in the queue, break out
462
of the loop (and implicitly exit the program) */
463
} while(queue_has_question(queue));
465
restore_signal_handler(&old_sigchld_action);
466
restore_sigmask(&orig_sigmask);
471
__attribute__((warn_unused_result))
472
mono_microsecs get_current_time(void){
473
struct timespec currtime;
474
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
475
error(0, errno, "Failed to get current time");
478
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
479
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
482
/* End of "main" section */
484
/* Start of regular code section; ALL this code has tests */
486
__attribute__((nonnull))
487
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
488
char **agent_directory, char **helper_directory,
489
uid_t *const user, gid_t *const group,
490
char **mandos_argz, size_t *mandos_argz_length){
492
const struct argp_option options[] = {
493
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
494
.doc="Systemd password agent directory" },
495
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
496
.doc="Mandos Client password helper directory" },
497
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
498
.flags=OPTION_HIDDEN | OPTION_ALIAS },
499
{ .name="user", .key='u', .arg="USERID",
500
.doc="User ID the Mandos Client will use as its unprivileged"
502
{ .name="userid", .key=130, /* From plugin--runner */
503
.flags=OPTION_HIDDEN | OPTION_ALIAS },
504
{ .name="group", .key='g', .arg="GROUPID",
505
.doc="Group ID the Mandos Client will use as its unprivileged"
507
{ .name="groupid", .key=131, /* From plugin--runner */
508
.flags=OPTION_HIDDEN | OPTION_ALIAS },
509
{ .name="test", .key=255, /* See should_only_run_tests() */
510
.doc="Skip normal operation, and only run self-tests. See"
511
" --test --help.", .group=10, },
515
__attribute__((nonnull(3)))
516
error_t parse_opt(int key, char *arg, struct argp_state *state){
519
case 'd': /* --agent-directory */
520
*agent_directory = strdup(arg);
522
case 128: /* --helper-directory */
523
case 129: /* --plugin-helper-dir */
524
*helper_directory = strdup(arg);
526
case 'u': /* --user */
527
case 130: /* --userid */
530
uintmax_t tmp_id = 0;
532
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
533
if(errno != 0 or tmp == arg or *tmp != '\0'
534
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
535
return ARGP_ERR_UNKNOWN;
537
*user = (uid_t)tmp_id;
541
case 'g': /* --group */
542
case 131: /* --groupid */
545
uintmax_t tmp_id = 0;
547
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
548
if(errno != 0 or tmp == arg or *tmp != '\0'
549
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
550
return ARGP_ERR_UNKNOWN;
552
*group = (gid_t)tmp_id;
557
/* Copy arguments into argz vector */
558
return argz_create(state->argv + state->next, mandos_argz,
561
return ARGP_ERR_UNKNOWN;
566
const struct argp argp = {
569
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
570
.doc = "Mandos password agent -- runs Mandos client as a"
571
" systemd password agent",
574
errno = argp_parse(&argp, argc, argv,
575
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
580
__attribute__((nonnull, warn_unused_result))
581
bool block_sigchld(sigset_t *const orig_sigmask){
582
sigset_t sigchld_sigmask;
583
if(sigemptyset(&sigchld_sigmask) < 0){
584
error(0, errno, "Failed to empty signal set");
587
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
588
error(0, errno, "Failed to add SIGCHLD to signal set");
591
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
592
error(0, errno, "Failed to block SIGCHLD signal");
598
__attribute__((nonnull, warn_unused_result, const))
599
bool restore_sigmask(const sigset_t *const orig_sigmask){
600
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
601
error(0, errno, "Failed to restore blocked signals");
607
__attribute__((nonnull, warn_unused_result))
608
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
609
struct sigaction sigchld_action = {
610
.sa_handler=handle_sigchld,
611
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
613
/* Set all signals in "sa_mask" struct member; this makes all
614
signals automatically blocked during signal handler */
615
if(sigfillset(&sigchld_action.sa_mask) != 0){
616
error(0, errno, "Failed to do sigfillset()");
619
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
620
error(0, errno, "Failed to set SIGCHLD signal handler");
626
__attribute__((nonnull, warn_unused_result))
627
bool restore_signal_handler(const struct sigaction *const
629
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
630
error(0, errno, "Failed to restore signal handler");
636
__attribute__((warn_unused_result, malloc))
637
task_queue *create_queue(void){
638
task_queue *queue = malloc(sizeof(task_queue));
642
queue->allocated = 0;
648
__attribute__((nonnull, warn_unused_result))
649
bool add_to_queue(task_queue *const queue, const task_context task){
650
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
if(needed_size > (queue->allocated)){
652
task_context *const new_tasks = realloc(queue->tasks,
654
if(new_tasks == NULL){
655
error(0, errno, "Failed to allocate %" PRIuMAX
656
" bytes for queue->tasks", (uintmax_t)needed_size);
659
queue->tasks = new_tasks;
660
queue->allocated = needed_size;
662
/* Using memcpy here is necessary because doing */
663
/* queue->tasks[queue->length++] = task; */
664
/* would violate const-ness of task members */
665
memcpy(&(queue->tasks[queue->length++]), &task,
666
sizeof(task_context));
670
__attribute__((nonnull))
671
void cleanup_task(const task_context *const task){
672
const error_t saved_errno = errno;
673
/* free and close all task data */
674
free(task->question_filename);
675
if(task->filename != task->question_filename){
676
free(task->filename);
679
kill(task->pid, SIGTERM);
687
__attribute__((nonnull))
688
void free_queue(task_queue *const queue){
693
__attribute__((nonnull))
694
void cleanup_queue(task_queue *const *const queue){
698
for(size_t i = 0; i < (*queue)->length; i++){
699
const task_context *const task = ((*queue)->tasks)+i;
705
__attribute__((pure, nonnull, warn_unused_result))
706
bool queue_has_question(const task_queue *const queue){
707
for(size_t i=0; i < queue->length; i++){
708
if(queue->tasks[i].question_filename != NULL){
715
__attribute__((nonnull))
716
void cleanup_close(const int *const fd){
717
const error_t saved_errno = errno;
722
__attribute__((nonnull))
723
void cleanup_string(char *const *const ptr){
727
__attribute__((nonnull))
728
void cleanup_buffer(buffer *buf){
729
if(buf->allocated > 0){
730
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
731
explicit_bzero(buf->data, buf->allocated);
733
memset(buf->data, '\0', buf->allocated);
736
if(buf->data != NULL){
737
if(munlock(buf->data, buf->allocated) != 0){
738
error(0, errno, "Failed to unlock memory of old buffer");
747
__attribute__((pure, nonnull, warn_unused_result))
748
bool string_set_contains(const string_set set, const char *const str){
749
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
750
s = argz_next(set.argz, set.argz_len, s)){
751
if(strcmp(s, str) == 0){
758
__attribute__((nonnull, warn_unused_result))
759
bool string_set_add(string_set *const set, const char *const str){
760
if(string_set_contains(*set, str)){
763
error_t error = argz_add(&set->argz, &set->argz_len, str);
771
__attribute__((nonnull))
772
void string_set_clear(string_set *set){
778
__attribute__((nonnull))
779
void string_set_swap(string_set *const set1, string_set *const set2){
780
/* Swap contents of two string sets */
782
char *const tmp_argz = set1->argz;
783
set1->argz = set2->argz;
784
set2->argz = tmp_argz;
787
const size_t tmp_argz_len = set1->argz_len;
788
set1->argz_len = set2->argz_len;
789
set2->argz_len = tmp_argz_len;
793
__attribute__((nonnull, warn_unused_result))
794
bool start_mandos_client(task_queue *const queue,
796
bool *const mandos_client_exited,
797
bool *const quit_now, buffer *const password,
798
bool *const password_is_read,
799
const struct sigaction *const
800
old_sigchld_action, const sigset_t sigmask,
801
const char *const helper_directory,
802
const uid_t user, const gid_t group,
803
const char *const *const argv){
805
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
806
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
810
const pid_t pid = fork();
812
if(not restore_signal_handler(old_sigchld_action)){
815
if(not restore_sigmask(&sigmask)){
818
if(close(pipefds[0]) != 0){
819
error(0, errno, "Failed to close() parent pipe fd");
822
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
823
error(0, errno, "Failed to dup2() pipe fd to stdout");
826
if(close(pipefds[1]) != 0){
827
error(0, errno, "Failed to close() old child pipe fd");
830
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
831
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
832
" \"%s\", 1)", helper_directory);
835
if(group != 0 and setresgid(group, 0, 0) == -1){
836
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
837
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
840
if(user != 0 and setresuid(user, 0, 0) == -1){
841
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
842
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
846
#pragma GCC diagnostic push
847
/* For historical reasons, the "argv" argument to execv() is not
848
const, but it is safe to override this. */
849
#pragma GCC diagnostic ignored "-Wcast-qual"
851
execv(argv[0], (char **)argv);
853
#pragma GCC diagnostic pop
855
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
860
if(not add_to_queue(queue, (task_context){
861
.func=wait_for_mandos_client_exit,
863
.mandos_client_exited=mandos_client_exited,
866
error(0, errno, "Failed to add wait_for_mandos_client to queue");
871
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
872
&(struct epoll_event)
873
{ .events=EPOLLIN | EPOLLRDHUP });
874
if(ret != 0 and errno != EEXIST){
875
error(0, errno, "Failed to add file descriptor to epoll set");
880
return add_to_queue(queue, (task_context){
881
.func=read_mandos_client_output,
886
.password_is_read=password_is_read,
890
__attribute__((nonnull))
891
void wait_for_mandos_client_exit(const task_context task,
892
task_queue *const queue){
893
const pid_t pid = task.pid;
894
bool *const mandos_client_exited = task.mandos_client_exited;
895
bool *const quit_now = task.quit_now;
898
switch(waitpid(pid, &status, WNOHANG)){
899
case 0: /* Not exited yet */
900
if(not add_to_queue(queue, task)){
901
error(0, errno, "Failed to add myself to queue");
906
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
912
default: /* Has exited */
913
*mandos_client_exited = true;
914
if((not WIFEXITED(status))
915
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
916
error(0, 0, "Mandos client failed or was killed");
922
__attribute__((nonnull))
923
void read_mandos_client_output(const task_context task,
924
task_queue *const queue){
925
buffer *const password = task.password;
926
bool *const quit_now = task.quit_now;
927
bool *const password_is_read = task.password_is_read;
928
const int fd = task.fd;
929
const int epoll_fd = task.epoll_fd;
931
const size_t new_potential_size = (password->length + PIPE_BUF);
932
if(password->allocated < new_potential_size){
933
char *const new_buffer = calloc(new_potential_size, 1);
934
if(new_buffer == NULL){
935
error(0, errno, "Failed to allocate %" PRIuMAX
936
" bytes for password", (uintmax_t)new_potential_size);
941
if(mlock(new_buffer, new_potential_size) != 0){
942
/* Warn but do not treat as fatal error */
943
if(errno != EPERM and errno != ENOMEM){
944
error(0, errno, "Failed to lock memory for password");
947
if(password->length > 0){
948
memcpy(new_buffer, password->data, password->length);
949
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
950
explicit_bzero(password->data, password->allocated);
952
memset(password->data, '\0', password->allocated);
955
if(password->data != NULL){
956
if(munlock(password->data, password->allocated) != 0){
957
error(0, errno, "Failed to unlock memory of old buffer");
959
free(password->data);
961
password->data = new_buffer;
962
password->allocated = new_potential_size;
965
const ssize_t read_length = read(fd, password->data
966
+ password->length, PIPE_BUF);
968
if(read_length == 0){ /* EOF */
969
*password_is_read = true;
973
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
974
error(0, errno, "Failed to read password from Mandos client");
979
if(read_length > 0){ /* Data has been read */
980
password->length += (size_t)read_length;
983
/* Either data was read, or EAGAIN was indicated, meaning no data
986
/* Re-add the fd to the epoll set */
987
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
988
&(struct epoll_event)
989
{ .events=EPOLLIN | EPOLLRDHUP });
990
if(ret != 0 and errno != EEXIST){
991
error(0, errno, "Failed to re-add file descriptor to epoll set");
997
/* Re-add myself to the queue */
998
if(not add_to_queue(queue, task)){
999
error(0, errno, "Failed to add myself to queue");
1005
__attribute__((nonnull, warn_unused_result))
1006
bool add_inotify_dir_watch(task_queue *const queue,
1007
const int epoll_fd, bool *const quit_now,
1008
buffer *const password,
1009
const char *const dir,
1010
string_set *cancelled_filenames,
1011
const mono_microsecs *const current_time,
1012
bool *const mandos_client_exited,
1013
bool *const password_is_read){
1014
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1016
error(0, errno, "Failed to create inotify instance");
1020
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
1021
| IN_MOVED_TO | IN_DELETE)
1023
error(0, errno, "Failed to create inotify watch on %s", dir);
1027
/* Add the inotify fd to the epoll set */
1028
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1029
&(struct epoll_event)
1030
{ .events=EPOLLIN | EPOLLRDHUP });
1031
if(ret != 0 and errno != EEXIST){
1032
error(0, errno, "Failed to add file descriptor to epoll set");
1037
const task_context read_inotify_event_task = {
1038
.func=read_inotify_event,
1043
.filename=strdup(dir),
1044
.cancelled_filenames=cancelled_filenames,
1045
.current_time=current_time,
1046
.mandos_client_exited=mandos_client_exited,
1047
.password_is_read=password_is_read,
1049
if(read_inotify_event_task.filename == NULL){
1050
error(0, errno, "Failed to strdup(\"%s\")", dir);
1055
return add_to_queue(queue, read_inotify_event_task);
1058
__attribute__((nonnull))
1059
void read_inotify_event(const task_context task,
1060
task_queue *const queue){
1061
const int fd = task.fd;
1062
const int epoll_fd = task.epoll_fd;
1063
char *const filename = task.filename;
1064
bool *quit_now = task.quit_now;
1065
buffer *const password = task.password;
1066
string_set *const cancelled_filenames = task.cancelled_filenames;
1067
const mono_microsecs *const current_time = task.current_time;
1068
bool *const mandos_client_exited = task.mandos_client_exited;
1069
bool *const password_is_read = task.password_is_read;
1071
/* "sufficient to read at least one event." - inotify(7) */
1072
const size_t ievent_size = (sizeof(struct inotify_event)
1074
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
1075
struct inotify_event *ievent = ((struct inotify_event *)
1078
const ssize_t read_length = read(fd, ievent, ievent_size);
1079
if(read_length == 0){ /* EOF */
1080
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1082
cleanup_task(&task);
1085
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1086
error(0, errno, "Failed to read from inotify fd for directory %s",
1089
cleanup_task(&task);
1092
if(read_length > 0 /* Data has been read */
1093
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1094
char *question_filename = NULL;
1095
const ssize_t question_filename_length
1096
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1097
if(question_filename_length < 0){
1098
error(0, errno, "Failed to create file name from directory name"
1099
" %s and file name %s", filename, ievent->name);
1101
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1102
if(not add_to_queue(queue, (task_context){
1103
.func=open_and_parse_question,
1105
.question_filename=question_filename,
1106
.filename=question_filename,
1108
.cancelled_filenames=cancelled_filenames,
1109
.current_time=current_time,
1110
.mandos_client_exited=mandos_client_exited,
1111
.password_is_read=password_is_read,
1113
error(0, errno, "Failed to add open_and_parse_question task"
1114
" for file name %s to queue", filename);
1116
/* Force the added task (open_and_parse_question) to run
1118
queue->next_run = 1;
1120
} else if(ievent->mask & IN_DELETE){
1121
if(not string_set_add(cancelled_filenames,
1122
question_filename)){
1123
error(0, errno, "Could not add question %s to"
1124
" cancelled_questions", question_filename);
1126
free(question_filename);
1127
cleanup_task(&task);
1130
free(question_filename);
1135
/* Either data was read, or EAGAIN was indicated, meaning no data
1138
/* Re-add myself to the queue */
1139
if(not add_to_queue(queue, task)){
1140
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1141
" queue", filename);
1143
cleanup_task(&task);
1147
/* Re-add the fd to the epoll set */
1148
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1149
&(struct epoll_event)
1150
{ .events=EPOLLIN | EPOLLRDHUP });
1151
if(ret != 0 and errno != EEXIST){
1152
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1153
" directory %s to epoll set", fd, filename);
1154
/* Force the added task (read_inotify_event) to run again, at most
1155
one second from now */
1156
if((queue->next_run == 0)
1157
or (queue->next_run > (*current_time + 1000000))){
1158
queue->next_run = *current_time + 1000000;
1163
__attribute__((nonnull))
1164
void open_and_parse_question(const task_context task,
1165
task_queue *const queue){
1166
__attribute__((cleanup(cleanup_string)))
1167
char *question_filename = task.question_filename;
1168
const int epoll_fd = task.epoll_fd;
1169
buffer *const password = task.password;
1170
string_set *const cancelled_filenames = task.cancelled_filenames;
1171
const mono_microsecs *const current_time = task.current_time;
1172
bool *const mandos_client_exited = task.mandos_client_exited;
1173
bool *const password_is_read = task.password_is_read;
1175
/* We use the GLib "Key-value file parser" functions to parse the
1176
question file. See <https://www.freedesktop.org/wiki/Software
1177
/systemd/PasswordAgents/> for specification of contents */
1178
__attribute__((nonnull))
1179
void cleanup_g_key_file(GKeyFile **key_file){
1180
if(*key_file != NULL){
1181
g_key_file_free(*key_file);
1185
__attribute__((cleanup(cleanup_g_key_file)))
1186
GKeyFile *key_file = g_key_file_new();
1187
if(key_file == NULL){
1188
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1192
GError *glib_error = NULL;
1193
if(g_key_file_load_from_file(key_file, question_filename,
1194
G_KEY_FILE_NONE, &glib_error) != TRUE){
1195
/* If a file was removed, we should ignore it, so */
1196
/* only show error message if file actually existed */
1197
if(glib_error->code != G_FILE_ERROR_NOENT){
1198
error(0, 0, "Failed to load question data from file \"%s\": %s",
1199
question_filename, glib_error->message);
1204
__attribute__((cleanup(cleanup_string)))
1205
char *socket_name = g_key_file_get_string(key_file, "Ask",
1208
if(socket_name == NULL){
1209
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1210
question_filename, glib_error->message);
1214
if(strlen(socket_name) == 0){
1215
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1220
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1222
if(glib_error != NULL){
1223
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1224
question_filename, glib_error->message);
1228
if((pid != (guint64)((pid_t)pid))
1229
or (kill((pid_t)pid, 0) != 0)){
1230
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1231
" does not exist", (uintmax_t)pid, question_filename);
1235
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1236
"NotAfter", &glib_error);
1237
if(glib_error != NULL){
1238
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1239
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1240
" %s", question_filename, glib_error->message);
1245
if(queue->next_run == 0 or (queue->next_run > notafter)){
1246
queue->next_run = notafter;
1248
if(*current_time >= notafter){
1253
const task_context connect_question_socket_task = {
1254
.func=connect_question_socket,
1255
.question_filename=strdup(question_filename),
1258
.filename=strdup(socket_name),
1259
.cancelled_filenames=task.cancelled_filenames,
1260
.mandos_client_exited=mandos_client_exited,
1261
.password_is_read=password_is_read,
1262
.current_time=current_time,
1264
if(connect_question_socket_task.question_filename == NULL
1265
or connect_question_socket_task.filename == NULL
1266
or not add_to_queue(queue, connect_question_socket_task)){
1267
error(0, errno, "Failed to add connect_question_socket for socket"
1268
" %s (from \"%s\") to queue", socket_name,
1270
cleanup_task(&connect_question_socket_task);
1273
/* Force the added task (connect_question_socket) to run
1275
queue->next_run = 1;
1278
char *const dup_filename = strdup(question_filename);
1279
const task_context cancel_old_question_task = {
1280
.func=cancel_old_question,
1281
.question_filename=dup_filename,
1283
.filename=dup_filename,
1284
.cancelled_filenames=cancelled_filenames,
1285
.current_time=current_time,
1287
if(cancel_old_question_task.question_filename == NULL
1288
or not add_to_queue(queue, cancel_old_question_task)){
1289
error(0, errno, "Failed to add cancel_old_question for file "
1290
"\"%s\" to queue", question_filename);
1291
cleanup_task(&cancel_old_question_task);
1297
__attribute__((nonnull))
1298
void cancel_old_question(const task_context task,
1299
task_queue *const queue){
1300
char *const question_filename = task.question_filename;
1301
string_set *const cancelled_filenames = task.cancelled_filenames;
1302
const mono_microsecs notafter = task.notafter;
1303
const mono_microsecs *const current_time = task.current_time;
1305
if(*current_time >= notafter){
1306
if(not string_set_add(cancelled_filenames, question_filename)){
1307
error(0, errno, "Failed to cancel question for file %s",
1310
cleanup_task(&task);
1314
if(not add_to_queue(queue, task)){
1315
error(0, errno, "Failed to add cancel_old_question for file "
1316
"%s to queue", question_filename);
1317
cleanup_task(&task);
1321
if((queue->next_run == 0) or (queue->next_run > notafter)){
1322
queue->next_run = notafter;
1326
__attribute__((nonnull))
1327
void connect_question_socket(const task_context task,
1328
task_queue *const queue){
1329
char *const question_filename = task.question_filename;
1330
char *const filename = task.filename;
1331
const int epoll_fd = task.epoll_fd;
1332
buffer *const password = task.password;
1333
string_set *const cancelled_filenames = task.cancelled_filenames;
1334
bool *const mandos_client_exited = task.mandos_client_exited;
1335
bool *const password_is_read = task.password_is_read;
1336
const mono_microsecs *const current_time = task.current_time;
1338
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1340
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1341
error(0, 0, "Socket filename is larger than"
1342
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1343
(uintmax_t)sizeof(sock_name.sun_path), filename);
1344
if(not string_set_add(cancelled_filenames, question_filename)){
1345
error(0, errno, "Failed to cancel question for file %s",
1348
cleanup_task(&task);
1352
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1353
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1356
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1357
if(not add_to_queue(queue, task)){
1358
error(0, errno, "Failed to add connect_question_socket for file"
1359
" \"%s\" and socket \"%s\" to queue", question_filename,
1361
cleanup_task(&task);
1363
/* Force the added task (connect_question_socket) to run
1365
queue->next_run = 1;
1370
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1371
if(connect(fd, (struct sockaddr *)&sock_name,
1372
(socklen_t)SUN_LEN(&sock_name)) != 0){
1373
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1374
if(not add_to_queue(queue, task)){
1375
error(0, errno, "Failed to add connect_question_socket for file"
1376
" \"%s\" and socket \"%s\" to queue", question_filename,
1378
cleanup_task(&task);
1380
/* Force the added task (connect_question_socket) to run again,
1381
at most one second from now */
1382
if((queue->next_run == 0)
1383
or (queue->next_run > (*current_time + 1000000))){
1384
queue->next_run = *current_time + 1000000;
1390
/* Not necessary, but we can try, and merely warn on failure */
1391
if(shutdown(fd, SHUT_RD) != 0){
1392
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1396
/* Add the fd to the epoll set */
1397
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1398
&(struct epoll_event){ .events=EPOLLOUT })
1400
error(0, errno, "Failed to add inotify file descriptor %d for"
1401
" socket %s to epoll set", fd, filename);
1402
if(not add_to_queue(queue, task)){
1403
error(0, errno, "Failed to add connect_question_socket for file"
1404
" \"%s\" and socket \"%s\" to queue", question_filename,
1406
cleanup_task(&task);
1408
/* Force the added task (connect_question_socket) to run again,
1409
at most one second from now */
1410
if((queue->next_run == 0)
1411
or (queue->next_run > (*current_time + 1000000))){
1412
queue->next_run = *current_time + 1000000;
1418
/* add task send_password_to_socket to queue */
1419
const task_context send_password_to_socket_task = {
1420
.func=send_password_to_socket,
1421
.question_filename=question_filename,
1426
.cancelled_filenames=cancelled_filenames,
1427
.mandos_client_exited=mandos_client_exited,
1428
.password_is_read=password_is_read,
1429
.current_time=current_time,
1432
if(not add_to_queue(queue, send_password_to_socket_task)){
1433
error(0, errno, "Failed to add send_password_to_socket for"
1434
" file \"%s\" and socket \"%s\" to queue",
1435
question_filename, filename);
1436
cleanup_task(&send_password_to_socket_task);
1440
__attribute__((nonnull))
1441
void send_password_to_socket(const task_context task,
1442
task_queue *const queue){
1443
char *const question_filename=task.question_filename;
1444
char *const filename=task.filename;
1445
const int epoll_fd=task.epoll_fd;
1446
const int fd=task.fd;
1447
buffer *const password=task.password;
1448
string_set *const cancelled_filenames=task.cancelled_filenames;
1449
bool *const mandos_client_exited = task.mandos_client_exited;
1450
bool *const password_is_read = task.password_is_read;
1451
const mono_microsecs *const current_time = task.current_time;
1453
if(*mandos_client_exited and *password_is_read){
1455
const size_t send_buffer_length = password->length + 2;
1456
char *send_buffer = malloc(send_buffer_length);
1457
if(send_buffer == NULL){
1458
error(0, errno, "Failed to allocate send_buffer");
1460
if(mlock(send_buffer, send_buffer_length) != 0){
1461
/* Warn but do not treat as fatal error */
1462
if(errno != EPERM and errno != ENOMEM){
1463
error(0, errno, "Failed to lock memory for password"
1467
/* “[…] send a single datagram to the socket consisting of the
1468
password string either prefixed with "+" or with "-"
1469
depending on whether the password entry was successful or
1470
not. You may but don't have to include a final NUL byte in
1473
— <https://www.freedesktop.org/wiki/Software/systemd/
1474
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1476
send_buffer[0] = '+'; /* Prefix with "+" */
1477
/* Always add an extra NUL */
1478
send_buffer[password->length + 1] = '\0';
1479
if(password->length > 0){
1480
memcpy(send_buffer + 1, password->data, password->length);
1483
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1485
const error_t saved_errno = errno;
1486
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1487
explicit_bzero(send_buffer, send_buffer_length);
1489
memset(send_buffer, '\0', send_buffer_length);
1491
if(munlock(send_buffer, send_buffer_length) != 0){
1492
error(0, errno, "Failed to unlock memory of send buffer");
1495
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1496
switch(saved_errno){
1509
error(0, 0, "Password of size %" PRIuMAX " is too big",
1510
(uintmax_t)password->length);
1514
__attribute__((fallthrough));
1517
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1518
error(0, 0, "Password only partially sent to socket");
1523
__attribute__((fallthrough));
1526
error(0, saved_errno, "Failed to send() to socket %s",
1528
if(not string_set_add(cancelled_filenames,
1529
question_filename)){
1530
error(0, errno, "Failed to cancel question for file %s",
1533
cleanup_task(&task);
1538
cleanup_task(&task);
1544
/* We failed or are not ready yet; retry later */
1546
if(not add_to_queue(queue, task)){
1547
error(0, errno, "Failed to add send_password_to_socket for"
1548
" file %s and socket %s to queue", question_filename,
1550
cleanup_task(&task);
1553
/* Add the fd to the epoll set */
1554
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1555
&(struct epoll_event){ .events=EPOLLOUT })
1557
error(0, errno, "Failed to add socket file descriptor %d for"
1558
" socket %s to epoll set", fd, filename);
1559
/* Force the added task (send_password_to_socket) to run again, at
1560
most one second from now */
1561
if((queue->next_run == 0)
1562
or (queue->next_run > (*current_time + 1000000))){
1563
queue->next_run = *current_time + 1000000;
1568
__attribute__((warn_unused_result))
1569
bool add_existing_questions(task_queue *const queue,
1571
buffer *const password,
1572
string_set *cancelled_filenames,
1573
const mono_microsecs *const current_time,
1574
bool *const mandos_client_exited,
1575
bool *const password_is_read,
1576
const char *const dirname){
1577
__attribute__((cleanup(cleanup_string)))
1578
char *dir_pattern = NULL;
1579
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1580
if(ret < 0 or dir_pattern == NULL){
1581
error(0, errno, "Could not create glob pattern for directory %s",
1585
__attribute__((cleanup(globfree)))
1586
glob_t question_filenames = {};
1587
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1588
NULL, &question_filenames)){
1591
error(0, errno, "Failed to open directory %s", dirname);
1594
error(0, errno, "There are no question files in %s", dirname);
1597
error(0, errno, "Could not allocate memory for question file"
1598
" names in %s", dirname);
1602
__attribute__((fallthrough));
1605
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1606
char *const question_filename = strdup(question_filenames
1608
const task_context task = {
1609
.func=open_and_parse_question,
1611
.question_filename=question_filename,
1612
.filename=question_filename,
1614
.cancelled_filenames=cancelled_filenames,
1615
.current_time=current_time,
1616
.mandos_client_exited=mandos_client_exited,
1617
.password_is_read=password_is_read,
1620
if(question_filename == NULL
1621
or not add_to_queue(queue, task)){
1622
error(0, errno, "Failed to add open_and_parse_question for"
1623
" file %s to queue",
1624
question_filenames.gl_pathv[i]);
1625
free(question_filename);
1627
queue->next_run = 1;
1634
__attribute__((nonnull, warn_unused_result))
1635
bool wait_for_event(const int epoll_fd,
1636
const mono_microsecs queue_next_run,
1637
const mono_microsecs current_time){
1638
__attribute__((const))
1639
int milliseconds_to_wait(const mono_microsecs currtime,
1640
const mono_microsecs nextrun){
1641
if(currtime >= nextrun){
1644
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1645
if(wait_time_ms > (uintmax_t)INT_MAX){
1648
return (int)wait_time_ms;
1651
const int wait_time_ms = milliseconds_to_wait(current_time,
1654
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1655
sigset_t temporary_unblocked_sigmask;
1656
/* Get current signal mask */
1657
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1660
/* Remove SIGCHLD from the signal mask */
1661
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1664
struct epoll_event events[8]; /* Ignored */
1665
int ret = epoll_pwait(epoll_fd, events,
1666
sizeof(events) / sizeof(struct epoll_event),
1667
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1668
&temporary_unblocked_sigmask);
1669
if(ret < 0 and errno != EINTR){
1670
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1672
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1675
return clear_all_fds_from_epoll_set(epoll_fd);
1678
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1679
/* Create a new empty epoll set */
1680
__attribute__((cleanup(cleanup_close)))
1681
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1682
if(new_epoll_fd < 0){
1685
/* dup3() the new epoll set fd over the old one, replacing it */
1686
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1692
__attribute__((nonnull, warn_unused_result))
1693
bool run_queue(task_queue **const queue,
1694
string_set *const cancelled_filenames,
1695
bool *const quit_now){
1697
task_queue *new_queue = create_queue();
1698
if(new_queue == NULL){
1702
__attribute__((cleanup(string_set_clear)))
1703
string_set old_cancelled_filenames = {};
1704
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1706
/* Declare i outside the for loop, since we might need i after the
1707
loop in case we aborted in the middle */
1709
for(i=0; i < (*queue)->length and not *quit_now; i++){
1710
task_context *const task = &((*queue)->tasks[i]);
1711
const char *const question_filename = task->question_filename;
1712
/* Skip any task referencing a cancelled question filename */
1713
if(question_filename != NULL
1714
and string_set_contains(old_cancelled_filenames,
1715
question_filename)){
1719
task->func(*task, new_queue);
1723
/* we might be in the middle of the queue, so clean up any
1724
remaining tasks in the current queue */
1725
for(; i < (*queue)->length; i++){
1726
cleanup_task(&((*queue)->tasks[i]));
1740
/* End of regular code section */
1742
/* Start of tests section; here are the tests for the above code */
1744
/* This "fixture" data structure is used by the test setup and
1745
teardown functions */
1747
struct sigaction orig_sigaction;
1748
sigset_t orig_sigmask;
1751
static void test_setup(test_fixture *fixture,
1752
__attribute__((unused))
1753
gconstpointer user_data){
1754
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1755
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1758
static void test_teardown(test_fixture *fixture,
1759
__attribute__((unused))
1760
gconstpointer user_data){
1761
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1762
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1765
/* Utility function used by tests to search queue for matching task */
1766
__attribute__((pure, nonnull, warn_unused_result))
1767
static task_context *find_matching_task(const task_queue *const queue,
1768
const task_context task){
1769
/* The argument "task" structure is a pattern to match; 0 in any
1770
member means any value matches, otherwise the value must match.
1771
The filename strings are compared by strcmp(), not by pointer. */
1772
for(size_t i = 0; i < queue->length; i++){
1773
task_context *const current_task = queue->tasks+i;
1774
/* Check all members of task_context, if set to a non-zero value.
1775
If a member does not match, continue to next task in queue */
1777
/* task_func *const func */
1778
if(task.func != NULL and current_task->func != task.func){
1781
/* char *const question_filename; */
1782
if(task.question_filename != NULL
1783
and (current_task->question_filename == NULL
1784
or strcmp(current_task->question_filename,
1785
task.question_filename) != 0)){
1788
/* const pid_t pid; */
1789
if(task.pid != 0 and current_task->pid != task.pid){
1792
/* const int epoll_fd; */
1793
if(task.epoll_fd != 0
1794
and current_task->epoll_fd != task.epoll_fd){
1797
/* bool *const quit_now; */
1798
if(task.quit_now != NULL
1799
and current_task->quit_now != task.quit_now){
1803
if(task.fd != 0 and current_task->fd != task.fd){
1806
/* bool *const mandos_client_exited; */
1807
if(task.mandos_client_exited != NULL
1808
and current_task->mandos_client_exited
1809
!= task.mandos_client_exited){
1812
/* buffer *const password; */
1813
if(task.password != NULL
1814
and current_task->password != task.password){
1817
/* bool *const password_is_read; */
1818
if(task.password_is_read != NULL
1819
and current_task->password_is_read != task.password_is_read){
1822
/* char *filename; */
1823
if(task.filename != NULL
1824
and (current_task->filename == NULL
1825
or strcmp(current_task->filename, task.filename) != 0)){
1828
/* string_set *const cancelled_filenames; */
1829
if(task.cancelled_filenames != NULL
1830
and current_task->cancelled_filenames
1831
!= task.cancelled_filenames){
1834
/* const mono_microsecs notafter; */
1835
if(task.notafter != 0
1836
and current_task->notafter != task.notafter){
1839
/* const mono_microsecs *const current_time; */
1840
if(task.current_time != NULL
1841
and current_task->current_time != task.current_time){
1844
/* Current task matches all members; return it */
1845
return current_task;
1847
/* No task in queue matches passed pattern task */
1851
static void test_create_queue(__attribute__((unused))
1852
test_fixture *fixture,
1853
__attribute__((unused))
1854
gconstpointer user_data){
1855
__attribute__((cleanup(cleanup_queue)))
1856
task_queue *const queue = create_queue();
1857
g_assert_nonnull(queue);
1858
g_assert_null(queue->tasks);
1859
g_assert_true(queue->length == 0);
1860
g_assert_true(queue->next_run == 0);
1863
static task_func dummy_func;
1865
static void test_add_to_queue(__attribute__((unused))
1866
test_fixture *fixture,
1867
__attribute__((unused))
1868
gconstpointer user_data){
1869
__attribute__((cleanup(cleanup_queue)))
1870
task_queue *queue = create_queue();
1871
g_assert_nonnull(queue);
1873
g_assert_true(add_to_queue(queue,
1874
(task_context){ .func=dummy_func }));
1875
g_assert_true(queue->length == 1);
1876
g_assert_nonnull(queue->tasks);
1877
g_assert_true(queue->tasks[0].func == dummy_func);
1880
static void dummy_func(__attribute__((unused))
1881
const task_context task,
1882
__attribute__((unused))
1883
task_queue *const queue){
1886
static void test_queue_has_question_empty(__attribute__((unused))
1887
test_fixture *fixture,
1888
__attribute__((unused))
1889
gconstpointer user_data){
1890
__attribute__((cleanup(cleanup_queue)))
1891
task_queue *queue = create_queue();
1892
g_assert_nonnull(queue);
1893
g_assert_false(queue_has_question(queue));
1896
static void test_queue_has_question_false(__attribute__((unused))
1897
test_fixture *fixture,
1898
__attribute__((unused))
1899
gconstpointer user_data){
1900
__attribute__((cleanup(cleanup_queue)))
1901
task_queue *queue = create_queue();
1902
g_assert_nonnull(queue);
1903
g_assert_true(add_to_queue(queue,
1904
(task_context){ .func=dummy_func }));
1905
g_assert_false(queue_has_question(queue));
1908
static void test_queue_has_question_true(__attribute__((unused))
1909
test_fixture *fixture,
1910
__attribute__((unused))
1911
gconstpointer user_data){
1912
__attribute__((cleanup(cleanup_queue)))
1913
task_queue *queue = create_queue();
1914
g_assert_nonnull(queue);
1915
char *const question_filename
1916
= strdup("/nonexistent/question_filename");
1917
g_assert_nonnull(question_filename);
1918
task_context task = {
1920
.question_filename=question_filename,
1922
g_assert_true(add_to_queue(queue, task));
1923
g_assert_true(queue_has_question(queue));
1926
static void test_queue_has_question_false2(__attribute__((unused))
1927
test_fixture *fixture,
1928
__attribute__((unused))
1929
gconstpointer user_data){
1930
__attribute__((cleanup(cleanup_queue)))
1931
task_queue *queue = create_queue();
1932
g_assert_nonnull(queue);
1933
task_context task = { .func=dummy_func };
1934
g_assert_true(add_to_queue(queue, task));
1935
g_assert_true(add_to_queue(queue, task));
1936
g_assert_cmpint((int)queue->length, ==, 2);
1937
g_assert_false(queue_has_question(queue));
1940
static void test_queue_has_question_true2(__attribute__((unused))
1941
test_fixture *fixture,
1942
__attribute__((unused))
1943
gconstpointer user_data){
1944
__attribute__((cleanup(cleanup_queue)))
1945
task_queue *queue = create_queue();
1946
g_assert_nonnull(queue);
1947
task_context task1 = { .func=dummy_func };
1948
g_assert_true(add_to_queue(queue, task1));
1949
char *const question_filename
1950
= strdup("/nonexistent/question_filename");
1951
g_assert_nonnull(question_filename);
1952
task_context task2 = {
1954
.question_filename=question_filename,
1956
g_assert_true(add_to_queue(queue, task2));
1957
g_assert_cmpint((int)queue->length, ==, 2);
1958
g_assert_true(queue_has_question(queue));
1961
static void test_cleanup_buffer(__attribute__((unused))
1962
test_fixture *fixture,
1963
__attribute__((unused))
1964
gconstpointer user_data){
1967
const size_t buffersize = 10;
1969
buf.data = malloc(buffersize);
1970
g_assert_nonnull(buf.data);
1971
if(mlock(buf.data, buffersize) != 0){
1972
g_assert_true(errno == EPERM or errno == ENOMEM);
1975
cleanup_buffer(&buf);
1976
g_assert_null(buf.data);
1980
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1981
test_fixture *fixture,
1982
__attribute__((unused))
1985
__attribute__((cleanup(string_set_clear)))
1986
string_set set = {};
1987
g_assert_false(string_set_contains(set, "")); /* Empty string */
1988
g_assert_false(string_set_contains(set, "test_string"));
1992
test_string_set_with_added_string_contains_it(__attribute__((unused))
1993
test_fixture *fixture,
1994
__attribute__((unused))
1997
__attribute__((cleanup(string_set_clear)))
1998
string_set set = {};
1999
g_assert_true(string_set_add(&set, "test_string"));
2000
g_assert_true(string_set_contains(set, "test_string"));
2004
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2005
test_fixture *fixture,
2006
__attribute__((unused))
2007
gconstpointer user_data){
2008
__attribute__((cleanup(string_set_clear)))
2009
string_set set = {};
2010
g_assert_true(string_set_add(&set, "test_string"));
2011
string_set_clear(&set);
2012
g_assert_false(string_set_contains(set, "test_string"));
2016
void test_string_set_swap_one_with_empty(__attribute__((unused))
2017
test_fixture *fixture,
2018
__attribute__((unused))
2019
gconstpointer user_data){
2020
__attribute__((cleanup(string_set_clear)))
2021
string_set set1 = {};
2022
__attribute__((cleanup(string_set_clear)))
2023
string_set set2 = {};
2024
g_assert_true(string_set_add(&set1, "test_string1"));
2025
string_set_swap(&set1, &set2);
2026
g_assert_false(string_set_contains(set1, "test_string1"));
2027
g_assert_true(string_set_contains(set2, "test_string1"));
2031
void test_string_set_swap_empty_with_one(__attribute__((unused))
2032
test_fixture *fixture,
2033
__attribute__((unused))
2034
gconstpointer user_data){
2035
__attribute__((cleanup(string_set_clear)))
2036
string_set set1 = {};
2037
__attribute__((cleanup(string_set_clear)))
2038
string_set set2 = {};
2039
g_assert_true(string_set_add(&set2, "test_string2"));
2040
string_set_swap(&set1, &set2);
2041
g_assert_true(string_set_contains(set1, "test_string2"));
2042
g_assert_false(string_set_contains(set2, "test_string2"));
2045
static void test_string_set_swap_one_with_one(__attribute__((unused))
2046
test_fixture *fixture,
2047
__attribute__((unused))
2050
__attribute__((cleanup(string_set_clear)))
2051
string_set set1 = {};
2052
__attribute__((cleanup(string_set_clear)))
2053
string_set set2 = {};
2054
g_assert_true(string_set_add(&set1, "test_string1"));
2055
g_assert_true(string_set_add(&set2, "test_string2"));
2056
string_set_swap(&set1, &set2);
2057
g_assert_false(string_set_contains(set1, "test_string1"));
2058
g_assert_true(string_set_contains(set1, "test_string2"));
2059
g_assert_false(string_set_contains(set2, "test_string2"));
2060
g_assert_true(string_set_contains(set2, "test_string1"));
2063
static bool fd_has_cloexec_and_nonblock(const int);
2065
static bool epoll_set_contains(int, int, uint32_t);
2067
static void test_start_mandos_client(test_fixture *fixture,
2068
__attribute__((unused))
2069
gconstpointer user_data){
2071
bool mandos_client_exited = false;
2072
bool quit_now = false;
2073
__attribute__((cleanup(cleanup_close)))
2074
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2075
g_assert_cmpint(epoll_fd, >=, 0);
2076
__attribute__((cleanup(cleanup_queue)))
2077
task_queue *queue = create_queue();
2078
g_assert_nonnull(queue);
2079
buffer password = {};
2080
bool password_is_read = false;
2081
const char helper_directory[] = "/nonexistent";
2082
const char *const argv[] = { "/bin/true", NULL };
2084
g_assert_true(start_mandos_client(queue, epoll_fd,
2085
&mandos_client_exited, &quit_now,
2086
&password, &password_is_read,
2087
&fixture->orig_sigaction,
2088
fixture->orig_sigmask,
2089
helper_directory, 0, 0, argv));
2091
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2093
const task_context *const added_wait_task
2094
= find_matching_task(queue, (task_context){
2095
.func=wait_for_mandos_client_exit,
2096
.mandos_client_exited=&mandos_client_exited,
2097
.quit_now=&quit_now,
2099
g_assert_nonnull(added_wait_task);
2100
g_assert_cmpint(added_wait_task->pid, >, 0);
2101
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2102
waitpid(added_wait_task->pid, NULL, 0);
2104
const task_context *const added_read_task
2105
= find_matching_task(queue, (task_context){
2106
.func=read_mandos_client_output,
2108
.password=&password,
2109
.password_is_read=&password_is_read,
2110
.quit_now=&quit_now,
2112
g_assert_nonnull(added_read_task);
2113
g_assert_cmpint(added_read_task->fd, >, 2);
2114
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2115
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2116
EPOLLIN | EPOLLRDHUP));
2119
static bool fd_has_cloexec_and_nonblock(const int fd){
2120
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2121
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2122
return ((socket_fd_flags >= 0)
2123
and (socket_fd_flags & FD_CLOEXEC)
2124
and (socket_file_flags >= 0)
2125
and (socket_file_flags & O_NONBLOCK));
2128
__attribute__((const))
2129
bool is_privileged(void){
2130
uid_t user = getuid() + 1;
2131
if(user == 0){ /* Overflow check */
2134
gid_t group = getuid() + 1;
2135
if(group == 0){ /* Overflow check */
2138
const pid_t pid = fork();
2139
if(pid == 0){ /* Child */
2140
if(setresgid((uid_t)-1, group, group) == -1){
2142
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2143
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2147
if(setresuid((uid_t)-1, user, user) == -1){
2149
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2150
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2157
waitpid(pid, &status, 0);
2158
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2164
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2165
/* Only scan for events in this eventmask */
2166
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2167
__attribute__((cleanup(cleanup_string)))
2168
char *fdinfo_name = NULL;
2169
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2170
g_assert_cmpint(ret, >, 0);
2171
g_assert_nonnull(fdinfo_name);
2173
FILE *fdinfo = fopen(fdinfo_name, "r");
2174
g_assert_nonnull(fdinfo);
2175
uint32_t reported_events;
2180
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2183
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2184
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2185
&found_fd, &reported_events) == 2){
2190
} while(not feof(fdinfo) and not ferror(fdinfo));
2191
g_assert_cmpint(fclose(fdinfo), ==, 0);
2198
/* Don't check events if none are given */
2201
return (reported_events & eventmask) == (events & eventmask);
2204
static void test_start_mandos_client_execv(test_fixture *fixture,
2205
__attribute__((unused))
2206
gconstpointer user_data){
2207
bool mandos_client_exited = false;
2208
bool quit_now = false;
2209
__attribute__((cleanup(cleanup_close)))
2210
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2211
g_assert_cmpint(epoll_fd, >=, 0);
2212
__attribute__((cleanup(cleanup_queue)))
2213
task_queue *queue = create_queue();
2214
g_assert_nonnull(queue);
2215
__attribute__((cleanup(cleanup_buffer)))
2216
buffer password = {};
2217
const char helper_directory[] = "/nonexistent";
2218
/* Can't execv("/", ...), so this should fail */
2219
const char *const argv[] = { "/", NULL };
2222
__attribute__((cleanup(cleanup_close)))
2223
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2224
g_assert_cmpint(devnull_fd, >=, 0);
2225
__attribute__((cleanup(cleanup_close)))
2226
const int real_stderr_fd = dup(STDERR_FILENO);
2227
g_assert_cmpint(real_stderr_fd, >=, 0);
2228
dup2(devnull_fd, STDERR_FILENO);
2230
const bool success = start_mandos_client(queue, epoll_fd,
2231
&mandos_client_exited,
2235
&fixture->orig_sigaction,
2236
fixture->orig_sigmask,
2237
helper_directory, 0, 0,
2239
dup2(real_stderr_fd, STDERR_FILENO);
2240
g_assert_true(success);
2242
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2244
struct timespec starttime, currtime;
2245
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2247
queue->next_run = 0;
2248
string_set cancelled_filenames = {};
2251
__attribute__((cleanup(cleanup_close)))
2252
const int devnull_fd = open("/dev/null",
2253
O_WRONLY | O_CLOEXEC);
2254
g_assert_cmpint(devnull_fd, >=, 0);
2255
__attribute__((cleanup(cleanup_close)))
2256
const int real_stderr_fd = dup(STDERR_FILENO);
2257
g_assert_cmpint(real_stderr_fd, >=, 0);
2258
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2259
dup2(devnull_fd, STDERR_FILENO);
2260
const bool success = run_queue(&queue, &cancelled_filenames,
2262
dup2(real_stderr_fd, STDERR_FILENO);
2267
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2268
} while(((queue->length) > 0)
2270
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2272
g_assert_true(quit_now);
2273
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2274
g_assert_true(mandos_client_exited);
2277
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2278
__attribute__((unused))
2281
if(not is_privileged()){
2282
g_test_skip("Not privileged");
2286
bool mandos_client_exited = false;
2287
bool quit_now = false;
2288
__attribute__((cleanup(cleanup_close)))
2289
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2290
g_assert_cmpint(epoll_fd, >=, 0);
2291
__attribute__((cleanup(cleanup_queue)))
2292
task_queue *queue = create_queue();
2293
g_assert_nonnull(queue);
2294
__attribute__((cleanup(cleanup_buffer)))
2295
buffer password = {};
2296
bool password_is_read = false;
2297
const char helper_directory[] = "/nonexistent";
2298
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2302
const bool success = start_mandos_client(queue, epoll_fd,
2303
&mandos_client_exited,
2304
&quit_now, &password,
2306
&fixture->orig_sigaction,
2307
fixture->orig_sigmask,
2308
helper_directory, user,
2310
g_assert_true(success);
2311
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2313
struct timespec starttime, currtime;
2314
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2316
queue->next_run = 0;
2317
string_set cancelled_filenames = {};
2318
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2319
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2320
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2321
} while(((queue->length) > 0)
2323
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2325
g_assert_false(quit_now);
2326
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2327
g_assert_true(mandos_client_exited);
2329
g_assert_true(password_is_read);
2330
g_assert_nonnull(password.data);
2333
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2335
g_assert_true((uid_t)id == id);
2337
g_assert_cmpuint((unsigned int)id, ==, 0);
2340
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2341
__attribute__((unused))
2344
if(not is_privileged()){
2345
g_test_skip("Not privileged");
2349
bool mandos_client_exited = false;
2350
bool quit_now = false;
2351
__attribute__((cleanup(cleanup_close)))
2352
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2353
g_assert_cmpint(epoll_fd, >=, 0);
2354
__attribute__((cleanup(cleanup_queue)))
2355
task_queue *queue = create_queue();
2356
g_assert_nonnull(queue);
2357
__attribute__((cleanup(cleanup_buffer)))
2358
buffer password = {};
2359
bool password_is_read = false;
2360
const char helper_directory[] = "/nonexistent";
2361
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2365
const bool success = start_mandos_client(queue, epoll_fd,
2366
&mandos_client_exited,
2367
&quit_now, &password,
2369
&fixture->orig_sigaction,
2370
fixture->orig_sigmask,
2371
helper_directory, user,
2373
g_assert_true(success);
2374
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2376
struct timespec starttime, currtime;
2377
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2379
queue->next_run = 0;
2380
string_set cancelled_filenames = {};
2381
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2382
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2383
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2384
} while(((queue->length) > 0)
2386
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2388
g_assert_false(quit_now);
2389
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2390
g_assert_true(mandos_client_exited);
2392
g_assert_true(password_is_read);
2393
g_assert_nonnull(password.data);
2396
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2398
g_assert_true((gid_t)id == id);
2400
g_assert_cmpuint((unsigned int)id, ==, 0);
2403
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2404
__attribute__((unused))
2407
if(not is_privileged()){
2408
g_test_skip("Not privileged");
2412
bool mandos_client_exited = false;
2413
bool quit_now = false;
2414
__attribute__((cleanup(cleanup_close)))
2415
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2416
g_assert_cmpint(epoll_fd, >=, 0);
2417
__attribute__((cleanup(cleanup_queue)))
2418
task_queue *queue = create_queue();
2419
g_assert_nonnull(queue);
2420
__attribute__((cleanup(cleanup_buffer)))
2421
buffer password = {};
2422
bool password_is_read = false;
2423
const char helper_directory[] = "/nonexistent";
2424
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2429
const bool success = start_mandos_client(queue, epoll_fd,
2430
&mandos_client_exited,
2431
&quit_now, &password,
2433
&fixture->orig_sigaction,
2434
fixture->orig_sigmask,
2435
helper_directory, user,
2437
g_assert_true(success);
2438
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2440
struct timespec starttime, currtime;
2441
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2443
queue->next_run = 0;
2444
string_set cancelled_filenames = {};
2445
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2446
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2447
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2448
} while(((queue->length) > 0)
2450
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2452
g_assert_false(quit_now);
2453
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2454
g_assert_true(mandos_client_exited);
2456
g_assert_true(password_is_read);
2457
g_assert_nonnull(password.data);
2460
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2462
g_assert_true((uid_t)id == id);
2464
g_assert_cmpuint((unsigned int)id, ==, user);
2467
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2468
__attribute__((unused))
2471
if(not is_privileged()){
2472
g_test_skip("Not privileged");
2476
bool mandos_client_exited = false;
2477
bool quit_now = false;
2478
__attribute__((cleanup(cleanup_close)))
2479
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2480
g_assert_cmpint(epoll_fd, >=, 0);
2481
__attribute__((cleanup(cleanup_queue)))
2482
task_queue *queue = create_queue();
2483
g_assert_nonnull(queue);
2484
__attribute__((cleanup(cleanup_buffer)))
2485
buffer password = {};
2486
bool password_is_read = false;
2487
const char helper_directory[] = "/nonexistent";
2488
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2493
const bool success = start_mandos_client(queue, epoll_fd,
2494
&mandos_client_exited,
2495
&quit_now, &password,
2497
&fixture->orig_sigaction,
2498
fixture->orig_sigmask,
2499
helper_directory, user,
2501
g_assert_true(success);
2502
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2504
struct timespec starttime, currtime;
2505
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2507
queue->next_run = 0;
2508
string_set cancelled_filenames = {};
2509
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2510
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2511
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2512
} while(((queue->length) > 0)
2514
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2516
g_assert_false(quit_now);
2517
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2518
g_assert_true(mandos_client_exited);
2520
g_assert_true(password_is_read);
2521
g_assert_nonnull(password.data);
2524
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2526
g_assert_true((gid_t)id == id);
2528
g_assert_cmpuint((unsigned int)id, ==, group);
2531
static void test_start_mandos_client_read(test_fixture *fixture,
2532
__attribute__((unused))
2533
gconstpointer user_data){
2534
bool mandos_client_exited = false;
2535
bool quit_now = false;
2536
__attribute__((cleanup(cleanup_close)))
2537
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2538
g_assert_cmpint(epoll_fd, >=, 0);
2539
__attribute__((cleanup(cleanup_queue)))
2540
task_queue *queue = create_queue();
2541
g_assert_nonnull(queue);
2542
__attribute__((cleanup(cleanup_buffer)))
2543
buffer password = {};
2544
bool password_is_read = false;
2545
const char dummy_test_password[] = "dummy test password";
2546
const char helper_directory[] = "/nonexistent";
2547
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2550
const bool success = start_mandos_client(queue, epoll_fd,
2551
&mandos_client_exited,
2552
&quit_now, &password,
2554
&fixture->orig_sigaction,
2555
fixture->orig_sigmask,
2556
helper_directory, 0, 0,
2558
g_assert_true(success);
2559
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2561
struct timespec starttime, currtime;
2562
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2564
queue->next_run = 0;
2565
string_set cancelled_filenames = {};
2566
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2567
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2568
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2569
} while(((queue->length) > 0)
2571
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2573
g_assert_false(quit_now);
2574
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2575
g_assert_true(mandos_client_exited);
2577
g_assert_true(password_is_read);
2578
g_assert_cmpint((int)password.length, ==,
2579
sizeof(dummy_test_password)-1);
2580
g_assert_nonnull(password.data);
2581
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2582
sizeof(dummy_test_password)-1), ==, 0);
2586
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2587
__attribute__((unused))
2590
bool mandos_client_exited = false;
2591
bool quit_now = false;
2592
__attribute__((cleanup(cleanup_close)))
2593
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2594
g_assert_cmpint(epoll_fd, >=, 0);
2595
__attribute__((cleanup(cleanup_queue)))
2596
task_queue *queue = create_queue();
2597
g_assert_nonnull(queue);
2598
__attribute__((cleanup(cleanup_buffer)))
2599
buffer password = {};
2600
bool password_is_read = false;
2601
const char helper_directory[] = "/nonexistent";
2602
const char *const argv[] = { "/bin/sh", "-c",
2603
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2605
const bool success = start_mandos_client(queue, epoll_fd,
2606
&mandos_client_exited,
2607
&quit_now, &password,
2609
&fixture->orig_sigaction,
2610
fixture->orig_sigmask,
2611
helper_directory, 0, 0,
2613
g_assert_true(success);
2614
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2616
struct timespec starttime, currtime;
2617
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2619
queue->next_run = 0;
2620
string_set cancelled_filenames = {};
2621
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2622
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2623
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2624
} while(((queue->length) > 0)
2626
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2628
g_assert_false(quit_now);
2629
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2630
g_assert_true(mandos_client_exited);
2632
g_assert_true(password_is_read);
2633
g_assert_cmpint((int)password.length, ==,
2634
sizeof(helper_directory)-1);
2635
g_assert_nonnull(password.data);
2636
g_assert_cmpint(memcmp(helper_directory, password.data,
2637
sizeof(helper_directory)-1), ==, 0);
2640
__attribute__((nonnull, warn_unused_result))
2641
static bool proc_status_sigblk_to_sigset(const char *const,
2644
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2645
__attribute__((unused))
2646
gconstpointer user_data){
2647
bool mandos_client_exited = false;
2648
bool quit_now = false;
2649
__attribute__((cleanup(cleanup_close)))
2650
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2651
g_assert_cmpint(epoll_fd, >=, 0);
2652
__attribute__((cleanup(cleanup_queue)))
2653
task_queue *queue = create_queue();
2654
g_assert_nonnull(queue);
2655
__attribute__((cleanup(cleanup_buffer)))
2656
buffer password = {};
2657
bool password_is_read = false;
2658
const char helper_directory[] = "/nonexistent";
2659
/* see proc(5) for format of /proc/self/status */
2660
const char *const argv[] = { "/usr/bin/awk",
2661
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2663
g_assert_true(start_mandos_client(queue, epoll_fd,
2664
&mandos_client_exited, &quit_now,
2665
&password, &password_is_read,
2666
&fixture->orig_sigaction,
2667
fixture->orig_sigmask,
2668
helper_directory, 0, 0, argv));
2670
struct timespec starttime, currtime;
2671
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2673
queue->next_run = 0;
2674
string_set cancelled_filenames = {};
2675
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2676
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2677
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2678
} while((not (mandos_client_exited and password_is_read))
2680
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2681
g_assert_true(mandos_client_exited);
2682
g_assert_true(password_is_read);
2684
sigset_t parsed_sigmask;
2685
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2688
for(int signum = 1; signum < NSIG; signum++){
2689
const bool has_signal = sigismember(&parsed_sigmask, signum);
2690
if(sigismember(&fixture->orig_sigmask, signum)){
2691
g_assert_true(has_signal);
2693
g_assert_false(has_signal);
2698
__attribute__((nonnull, warn_unused_result))
2699
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2700
sigset_t *const sigmask){
2701
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2702
uintmax_t scanned_sigmask;
2703
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2706
if(sigemptyset(sigmask) != 0){
2709
for(int signum = 1; signum < NSIG; signum++){
2710
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2711
if(sigaddset(sigmask, signum) != 0){
2719
static void run_task_with_stderr_to_dev_null(const task_context task,
2720
task_queue *const queue);
2723
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2724
test_fixture *fixture,
2725
__attribute__((unused))
2726
gconstpointer user_data){
2728
bool mandos_client_exited = false;
2729
bool quit_now = false;
2731
__attribute__((cleanup(cleanup_queue)))
2732
task_queue *queue = create_queue();
2733
g_assert_nonnull(queue);
2734
const task_context task = {
2735
.func=wait_for_mandos_client_exit,
2737
.mandos_client_exited=&mandos_client_exited,
2738
.quit_now=&quit_now,
2740
run_task_with_stderr_to_dev_null(task, queue);
2742
g_assert_false(mandos_client_exited);
2743
g_assert_true(quit_now);
2744
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2747
static void run_task_with_stderr_to_dev_null(const task_context task,
2748
task_queue *const queue){
2749
FILE *real_stderr = stderr;
2750
FILE *devnull = fopen("/dev/null", "we");
2751
g_assert_nonnull(devnull);
2754
task.func(task, queue);
2755
stderr = real_stderr;
2757
g_assert_cmpint(fclose(devnull), ==, 0);
2761
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2762
__attribute__((unused))
2763
gconstpointer user_data){
2764
bool mandos_client_exited = false;
2765
bool quit_now = false;
2767
pid_t create_eternal_process(void){
2768
const pid_t pid = fork();
2769
if(pid == 0){ /* Child */
2770
if(not restore_signal_handler(&fixture->orig_sigaction)){
2771
_exit(EXIT_FAILURE);
2773
if(not restore_sigmask(&fixture->orig_sigmask)){
2774
_exit(EXIT_FAILURE);
2782
pid_t pid = create_eternal_process();
2783
g_assert_true(pid != -1);
2785
__attribute__((cleanup(cleanup_queue)))
2786
task_queue *queue = create_queue();
2787
g_assert_nonnull(queue);
2788
const task_context task = {
2789
.func=wait_for_mandos_client_exit,
2791
.mandos_client_exited=&mandos_client_exited,
2792
.quit_now=&quit_now,
2794
task.func(task, queue);
2796
g_assert_false(mandos_client_exited);
2797
g_assert_false(quit_now);
2798
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2800
g_assert_nonnull(find_matching_task(queue, (task_context){
2801
.func=wait_for_mandos_client_exit,
2803
.mandos_client_exited=&mandos_client_exited,
2804
.quit_now=&quit_now,
2809
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2810
__attribute__((unused))
2813
bool mandos_client_exited = false;
2814
bool quit_now = false;
2816
pid_t create_successful_process(void){
2817
const pid_t pid = fork();
2818
if(pid == 0){ /* Child */
2819
if(not restore_signal_handler(&fixture->orig_sigaction)){
2820
_exit(EXIT_FAILURE);
2822
if(not restore_sigmask(&fixture->orig_sigmask)){
2823
_exit(EXIT_FAILURE);
2829
const pid_t pid = create_successful_process();
2830
g_assert_true(pid != -1);
2832
__attribute__((cleanup(cleanup_queue)))
2833
task_queue *queue = create_queue();
2834
g_assert_nonnull(queue);
2835
const task_context initial_task = {
2836
.func=wait_for_mandos_client_exit,
2838
.mandos_client_exited=&mandos_client_exited,
2839
.quit_now=&quit_now,
2841
g_assert_true(add_to_queue(queue, initial_task));
2843
struct timespec starttime, currtime;
2844
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2845
__attribute__((cleanup(cleanup_close)))
2846
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2848
queue->next_run = 0;
2849
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2850
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2851
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2852
} while((not mandos_client_exited)
2854
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2856
g_assert_true(mandos_client_exited);
2857
g_assert_false(quit_now);
2858
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2862
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2863
__attribute__((unused))
2866
bool mandos_client_exited = false;
2867
bool quit_now = false;
2869
pid_t create_failing_process(void){
2870
const pid_t pid = fork();
2871
if(pid == 0){ /* Child */
2872
if(not restore_signal_handler(&fixture->orig_sigaction)){
2873
_exit(EXIT_FAILURE);
2875
if(not restore_sigmask(&fixture->orig_sigmask)){
2876
_exit(EXIT_FAILURE);
2882
const pid_t pid = create_failing_process();
2883
g_assert_true(pid != -1);
2885
__attribute__((cleanup(string_set_clear)))
2886
string_set cancelled_filenames = {};
2887
__attribute__((cleanup(cleanup_close)))
2888
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2889
g_assert_cmpint(epoll_fd, >=, 0);
2890
__attribute__((cleanup(cleanup_queue)))
2891
task_queue *queue = create_queue();
2892
g_assert_nonnull(queue);
2893
g_assert_true(add_to_queue(queue, (task_context){
2894
.func=wait_for_mandos_client_exit,
2896
.mandos_client_exited=&mandos_client_exited,
2897
.quit_now=&quit_now,
2900
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2902
__attribute__((cleanup(cleanup_close)))
2903
const int devnull_fd = open("/dev/null",
2904
O_WRONLY | O_CLOEXEC);
2905
g_assert_cmpint(devnull_fd, >=, 0);
2906
__attribute__((cleanup(cleanup_close)))
2907
const int real_stderr_fd = dup(STDERR_FILENO);
2908
g_assert_cmpint(real_stderr_fd, >=, 0);
2910
struct timespec starttime, currtime;
2911
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2913
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2914
dup2(devnull_fd, STDERR_FILENO);
2915
const bool success = run_queue(&queue, &cancelled_filenames,
2917
dup2(real_stderr_fd, STDERR_FILENO);
2922
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2923
} while((not mandos_client_exited)
2925
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2927
g_assert_true(quit_now);
2928
g_assert_true(mandos_client_exited);
2929
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2933
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2934
__attribute__((unused))
2935
gconstpointer user_data){
2936
bool mandos_client_exited = false;
2937
bool quit_now = false;
2939
pid_t create_killed_process(void){
2940
const pid_t pid = fork();
2941
if(pid == 0){ /* Child */
2942
if(not restore_signal_handler(&fixture->orig_sigaction)){
2943
_exit(EXIT_FAILURE);
2945
if(not restore_sigmask(&fixture->orig_sigmask)){
2946
_exit(EXIT_FAILURE);
2955
const pid_t pid = create_killed_process();
2956
g_assert_true(pid != -1);
2958
__attribute__((cleanup(string_set_clear)))
2959
string_set cancelled_filenames = {};
2960
__attribute__((cleanup(cleanup_close)))
2961
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2962
g_assert_cmpint(epoll_fd, >=, 0);
2963
__attribute__((cleanup(cleanup_queue)))
2964
task_queue *queue = create_queue();
2965
g_assert_nonnull(queue);
2966
g_assert_true(add_to_queue(queue, (task_context){
2967
.func=wait_for_mandos_client_exit,
2969
.mandos_client_exited=&mandos_client_exited,
2970
.quit_now=&quit_now,
2973
__attribute__((cleanup(cleanup_close)))
2974
const int devnull_fd = open("/dev/null",
2975
O_WRONLY | O_CLOEXEC);
2976
g_assert_cmpint(devnull_fd, >=, 0);
2977
__attribute__((cleanup(cleanup_close)))
2978
const int real_stderr_fd = dup(STDERR_FILENO);
2979
g_assert_cmpint(real_stderr_fd, >=, 0);
2981
struct timespec starttime, currtime;
2982
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2984
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2985
dup2(devnull_fd, STDERR_FILENO);
2986
const bool success = run_queue(&queue, &cancelled_filenames,
2988
dup2(real_stderr_fd, STDERR_FILENO);
2993
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2994
} while((not mandos_client_exited)
2996
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2998
g_assert_true(mandos_client_exited);
2999
g_assert_true(quit_now);
3000
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3003
static bool epoll_set_does_not_contain(int, int);
3006
void test_read_mandos_client_output_readerror(__attribute__((unused))
3007
test_fixture *fixture,
3008
__attribute__((unused))
3011
__attribute__((cleanup(cleanup_close)))
3012
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3013
g_assert_cmpint(epoll_fd, >=, 0);
3015
__attribute__((cleanup(cleanup_buffer)))
3016
buffer password = {};
3018
/* Reading /proc/self/mem from offset 0 will always give EIO */
3019
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3021
bool password_is_read = false;
3022
bool quit_now = false;
3023
__attribute__((cleanup(cleanup_queue)))
3024
task_queue *queue = create_queue();
3025
g_assert_nonnull(queue);
3027
task_context task = {
3028
.func=read_mandos_client_output,
3031
.password=&password,
3032
.password_is_read=&password_is_read,
3033
.quit_now=&quit_now,
3035
run_task_with_stderr_to_dev_null(task, queue);
3036
g_assert_false(password_is_read);
3037
g_assert_cmpint((int)password.length, ==, 0);
3038
g_assert_true(quit_now);
3039
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3041
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3043
g_assert_cmpint(close(fd), ==, -1);
3046
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3047
return not epoll_set_contains(epoll_fd, fd, 0);
3051
void test_read_mandos_client_output_nodata(__attribute__((unused))
3052
test_fixture *fixture,
3053
__attribute__((unused))
3054
gconstpointer user_data){
3055
__attribute__((cleanup(cleanup_close)))
3056
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3057
g_assert_cmpint(epoll_fd, >=, 0);
3060
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3062
__attribute__((cleanup(cleanup_buffer)))
3063
buffer password = {};
3065
bool password_is_read = false;
3066
bool quit_now = false;
3067
__attribute__((cleanup(cleanup_queue)))
3068
task_queue *queue = create_queue();
3069
g_assert_nonnull(queue);
3071
task_context task = {
3072
.func=read_mandos_client_output,
3075
.password=&password,
3076
.password_is_read=&password_is_read,
3077
.quit_now=&quit_now,
3079
task.func(task, queue);
3080
g_assert_false(password_is_read);
3081
g_assert_cmpint((int)password.length, ==, 0);
3082
g_assert_false(quit_now);
3083
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3085
g_assert_nonnull(find_matching_task(queue, (task_context){
3086
.func=read_mandos_client_output,
3089
.password=&password,
3090
.password_is_read=&password_is_read,
3091
.quit_now=&quit_now,
3094
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3095
EPOLLIN | EPOLLRDHUP));
3097
g_assert_cmpint(close(pipefds[1]), ==, 0);
3100
static void test_read_mandos_client_output_eof(__attribute__((unused))
3101
test_fixture *fixture,
3102
__attribute__((unused))
3105
__attribute__((cleanup(cleanup_close)))
3106
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3107
g_assert_cmpint(epoll_fd, >=, 0);
3110
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3111
g_assert_cmpint(close(pipefds[1]), ==, 0);
3113
__attribute__((cleanup(cleanup_buffer)))
3114
buffer password = {};
3116
bool password_is_read = false;
3117
bool quit_now = false;
3118
__attribute__((cleanup(cleanup_queue)))
3119
task_queue *queue = create_queue();
3120
g_assert_nonnull(queue);
3122
task_context task = {
3123
.func=read_mandos_client_output,
3126
.password=&password,
3127
.password_is_read=&password_is_read,
3128
.quit_now=&quit_now,
3130
task.func(task, queue);
3131
g_assert_true(password_is_read);
3132
g_assert_cmpint((int)password.length, ==, 0);
3133
g_assert_false(quit_now);
3134
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3136
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3138
g_assert_cmpint(close(pipefds[0]), ==, -1);
3142
void test_read_mandos_client_output_once(__attribute__((unused))
3143
test_fixture *fixture,
3144
__attribute__((unused))
3145
gconstpointer user_data){
3146
__attribute__((cleanup(cleanup_close)))
3147
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3148
g_assert_cmpint(epoll_fd, >=, 0);
3151
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3153
const char dummy_test_password[] = "dummy test password";
3154
/* Start with a pre-allocated buffer */
3155
__attribute__((cleanup(cleanup_buffer)))
3157
.data=malloc(sizeof(dummy_test_password)),
3159
.allocated=sizeof(dummy_test_password),
3161
g_assert_nonnull(password.data);
3162
if(mlock(password.data, password.allocated) != 0){
3163
g_assert_true(errno == EPERM or errno == ENOMEM);
3166
bool password_is_read = false;
3167
bool quit_now = false;
3168
__attribute__((cleanup(cleanup_queue)))
3169
task_queue *queue = create_queue();
3170
g_assert_nonnull(queue);
3172
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3173
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3174
sizeof(dummy_test_password)),
3175
==, (int)sizeof(dummy_test_password));
3177
task_context task = {
3178
.func=read_mandos_client_output,
3181
.password=&password,
3182
.password_is_read=&password_is_read,
3183
.quit_now=&quit_now,
3185
task.func(task, queue);
3187
g_assert_false(password_is_read);
3188
g_assert_cmpint((int)password.length, ==,
3189
(int)sizeof(dummy_test_password));
3190
g_assert_nonnull(password.data);
3191
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3192
sizeof(dummy_test_password)), ==, 0);
3194
g_assert_false(quit_now);
3195
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3197
g_assert_nonnull(find_matching_task(queue, (task_context){
3198
.func=read_mandos_client_output,
3201
.password=&password,
3202
.password_is_read=&password_is_read,
3203
.quit_now=&quit_now,
3206
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3207
EPOLLIN | EPOLLRDHUP));
3209
g_assert_cmpint(close(pipefds[1]), ==, 0);
3213
void test_read_mandos_client_output_malloc(__attribute__((unused))
3214
test_fixture *fixture,
3215
__attribute__((unused))
3216
gconstpointer user_data){
3217
__attribute__((cleanup(cleanup_close)))
3218
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3219
g_assert_cmpint(epoll_fd, >=, 0);
3222
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3224
const char dummy_test_password[] = "dummy test password";
3225
/* Start with an empty buffer */
3226
__attribute__((cleanup(cleanup_buffer)))
3227
buffer password = {};
3229
bool password_is_read = false;
3230
bool quit_now = false;
3231
__attribute__((cleanup(cleanup_queue)))
3232
task_queue *queue = create_queue();
3233
g_assert_nonnull(queue);
3235
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3236
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3237
sizeof(dummy_test_password)),
3238
==, (int)sizeof(dummy_test_password));
3240
task_context task = {
3241
.func=read_mandos_client_output,
3244
.password=&password,
3245
.password_is_read=&password_is_read,
3246
.quit_now=&quit_now,
3248
task.func(task, queue);
3250
g_assert_false(password_is_read);
3251
g_assert_cmpint((int)password.length, ==,
3252
(int)sizeof(dummy_test_password));
3253
g_assert_nonnull(password.data);
3254
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3255
sizeof(dummy_test_password)), ==, 0);
3257
g_assert_false(quit_now);
3258
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3260
g_assert_nonnull(find_matching_task(queue, (task_context){
3261
.func=read_mandos_client_output,
3264
.password=&password,
3265
.password_is_read=&password_is_read,
3266
.quit_now=&quit_now,
3269
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3270
EPOLLIN | EPOLLRDHUP));
3272
g_assert_cmpint(close(pipefds[1]), ==, 0);
3276
void test_read_mandos_client_output_append(__attribute__((unused))
3277
test_fixture *fixture,
3278
__attribute__((unused))
3279
gconstpointer user_data){
3280
__attribute__((cleanup(cleanup_close)))
3281
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3282
g_assert_cmpint(epoll_fd, >=, 0);
3285
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3287
const char dummy_test_password[] = "dummy test password";
3288
__attribute__((cleanup(cleanup_buffer)))
3290
.data=malloc(PIPE_BUF),
3292
.allocated=PIPE_BUF,
3294
g_assert_nonnull(password.data);
3295
if(mlock(password.data, password.allocated) != 0){
3296
g_assert_true(errno == EPERM or errno == ENOMEM);
3299
memset(password.data, 'x', PIPE_BUF);
3300
char password_expected[PIPE_BUF];
3301
memcpy(password_expected, password.data, PIPE_BUF);
3303
bool password_is_read = false;
3304
bool quit_now = false;
3305
__attribute__((cleanup(cleanup_queue)))
3306
task_queue *queue = create_queue();
3307
g_assert_nonnull(queue);
3309
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3310
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3311
sizeof(dummy_test_password)),
3312
==, (int)sizeof(dummy_test_password));
3314
task_context task = {
3315
.func=read_mandos_client_output,
3318
.password=&password,
3319
.password_is_read=&password_is_read,
3320
.quit_now=&quit_now,
3322
task.func(task, queue);
3324
g_assert_false(password_is_read);
3325
g_assert_cmpint((int)password.length, ==,
3326
PIPE_BUF + sizeof(dummy_test_password));
3327
g_assert_nonnull(password.data);
3328
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3330
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3331
dummy_test_password,
3332
sizeof(dummy_test_password)), ==, 0);
3333
g_assert_false(quit_now);
3334
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3336
g_assert_nonnull(find_matching_task(queue, (task_context){
3337
.func=read_mandos_client_output,
3340
.password=&password,
3341
.password_is_read=&password_is_read,
3342
.quit_now=&quit_now,
3345
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3346
EPOLLIN | EPOLLRDHUP));
3349
static char *make_temporary_directory(void);
3351
static void test_add_inotify_dir_watch(__attribute__((unused))
3352
test_fixture *fixture,
3353
__attribute__((unused))
3354
gconstpointer user_data){
3355
__attribute__((cleanup(cleanup_close)))
3356
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3357
g_assert_cmpint(epoll_fd, >=, 0);
3358
__attribute__((cleanup(cleanup_queue)))
3359
task_queue *queue = create_queue();
3360
g_assert_nonnull(queue);
3361
__attribute__((cleanup(string_set_clear)))
3362
string_set cancelled_filenames = {};
3363
const mono_microsecs current_time = 0;
3365
bool quit_now = false;
3366
buffer password = {};
3367
bool mandos_client_exited = false;
3368
bool password_is_read = false;
3370
__attribute__((cleanup(cleanup_string)))
3371
char *tempdir = make_temporary_directory();
3372
g_assert_nonnull(tempdir);
3374
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3376
&cancelled_filenames,
3378
&mandos_client_exited,
3379
&password_is_read));
3381
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3383
const task_context *const added_read_task
3384
= find_matching_task(queue, (task_context){
3385
.func=read_inotify_event,
3387
.quit_now=&quit_now,
3388
.password=&password,
3390
.cancelled_filenames=&cancelled_filenames,
3391
.current_time=¤t_time,
3392
.mandos_client_exited=&mandos_client_exited,
3393
.password_is_read=&password_is_read,
3395
g_assert_nonnull(added_read_task);
3397
g_assert_cmpint(added_read_task->fd, >, 2);
3398
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3399
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3400
added_read_task->fd,
3401
EPOLLIN | EPOLLRDHUP));
3403
g_assert_cmpint(rmdir(tempdir), ==, 0);
3406
static char *make_temporary_directory(void){
3407
char *name = strdup("/tmp/mandosXXXXXX");
3408
g_assert_nonnull(name);
3409
char *result = mkdtemp(name);
3416
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3417
test_fixture *fixture,
3418
__attribute__((unused))
3419
gconstpointer user_data){
3420
__attribute__((cleanup(cleanup_close)))
3421
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3422
g_assert_cmpint(epoll_fd, >=, 0);
3423
__attribute__((cleanup(cleanup_queue)))
3424
task_queue *queue = create_queue();
3425
g_assert_nonnull(queue);
3426
__attribute__((cleanup(string_set_clear)))
3427
string_set cancelled_filenames = {};
3428
const mono_microsecs current_time = 0;
3430
bool quit_now = false;
3431
buffer password = {};
3432
bool mandos_client_exited = false;
3433
bool password_is_read = false;
3435
const char nonexistent_dir[] = "/nonexistent";
3437
FILE *real_stderr = stderr;
3438
FILE *devnull = fopen("/dev/null", "we");
3439
g_assert_nonnull(devnull);
3441
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3442
&password, nonexistent_dir,
3443
&cancelled_filenames,
3445
&mandos_client_exited,
3446
&password_is_read));
3447
stderr = real_stderr;
3448
g_assert_cmpint(fclose(devnull), ==, 0);
3450
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3453
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3454
test_fixture *fixture,
3455
__attribute__((unused))
3458
__attribute__((cleanup(cleanup_close)))
3459
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3460
g_assert_cmpint(epoll_fd, >=, 0);
3461
__attribute__((cleanup(cleanup_queue)))
3462
task_queue *queue = create_queue();
3463
g_assert_nonnull(queue);
3464
__attribute__((cleanup(string_set_clear)))
3465
string_set cancelled_filenames = {};
3466
const mono_microsecs current_time = 0;
3468
bool quit_now = false;
3469
buffer password = {};
3470
bool mandos_client_exited = false;
3471
bool password_is_read = false;
3473
__attribute__((cleanup(cleanup_string)))
3474
char *tempdir = make_temporary_directory();
3475
g_assert_nonnull(tempdir);
3477
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3479
&cancelled_filenames,
3481
&mandos_client_exited,
3482
&password_is_read));
3484
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3486
const task_context *const added_read_task
3487
= find_matching_task(queue,
3488
(task_context){ .func=read_inotify_event });
3489
g_assert_nonnull(added_read_task);
3491
g_assert_cmpint(added_read_task->fd, >, 2);
3492
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3494
/* "sufficient to read at least one event." - inotify(7) */
3495
const size_t ievent_size = (sizeof(struct inotify_event)
3497
struct inotify_event *ievent = malloc(ievent_size);
3498
g_assert_nonnull(ievent);
3500
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3502
g_assert_cmpint(errno, ==, EAGAIN);
3506
g_assert_cmpint(rmdir(tempdir), ==, 0);
3509
static char *make_temporary_file_in_directory(const char
3513
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3514
test_fixture *fixture,
3515
__attribute__((unused))
3518
__attribute__((cleanup(cleanup_close)))
3519
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3520
g_assert_cmpint(epoll_fd, >=, 0);
3521
__attribute__((cleanup(cleanup_queue)))
3522
task_queue *queue = create_queue();
3523
g_assert_nonnull(queue);
3524
__attribute__((cleanup(string_set_clear)))
3525
string_set cancelled_filenames = {};
3526
const mono_microsecs current_time = 0;
3528
bool quit_now = false;
3529
buffer password = {};
3530
bool mandos_client_exited = false;
3531
bool password_is_read = false;
3533
__attribute__((cleanup(cleanup_string)))
3534
char *tempdir = make_temporary_directory();
3535
g_assert_nonnull(tempdir);
3537
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3539
&cancelled_filenames,
3541
&mandos_client_exited,
3542
&password_is_read));
3544
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3546
const task_context *const added_read_task
3547
= find_matching_task(queue,
3548
(task_context){ .func=read_inotify_event });
3549
g_assert_nonnull(added_read_task);
3551
g_assert_cmpint(added_read_task->fd, >, 2);
3552
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3554
__attribute__((cleanup(cleanup_string)))
3555
char *filename = make_temporary_file_in_directory(tempdir);
3556
g_assert_nonnull(filename);
3558
/* "sufficient to read at least one event." - inotify(7) */
3559
const size_t ievent_size = (sizeof(struct inotify_event)
3561
struct inotify_event *ievent = malloc(ievent_size);
3562
g_assert_nonnull(ievent);
3564
ssize_t read_size = 0;
3565
read_size = read(added_read_task->fd, ievent, ievent_size);
3567
g_assert_cmpint((int)read_size, >, 0);
3568
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3569
g_assert_cmpstr(ievent->name, ==, basename(filename));
3573
g_assert_cmpint(unlink(filename), ==, 0);
3574
g_assert_cmpint(rmdir(tempdir), ==, 0);
3577
static char *make_temporary_prefixed_file_in_directory(const char
3581
char *filename = NULL;
3582
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3584
g_assert_nonnull(filename);
3585
const int fd = mkostemp(filename, O_CLOEXEC);
3586
g_assert_cmpint(fd, >=, 0);
3587
g_assert_cmpint(close(fd), ==, 0);
3591
static char *make_temporary_file_in_directory(const char
3593
return make_temporary_prefixed_file_in_directory("temp", dir);
3597
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3598
test_fixture *fixture,
3599
__attribute__((unused))
3600
gconstpointer user_data){
3601
__attribute__((cleanup(cleanup_close)))
3602
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3603
g_assert_cmpint(epoll_fd, >=, 0);
3604
__attribute__((cleanup(cleanup_queue)))
3605
task_queue *queue = create_queue();
3606
g_assert_nonnull(queue);
3607
__attribute__((cleanup(string_set_clear)))
3608
string_set cancelled_filenames = {};
3609
const mono_microsecs current_time = 0;
3611
bool quit_now = false;
3612
buffer password = {};
3613
bool mandos_client_exited = false;
3614
bool password_is_read = false;
3616
__attribute__((cleanup(cleanup_string)))
3617
char *watchdir = make_temporary_directory();
3618
g_assert_nonnull(watchdir);
3620
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3621
&password, watchdir,
3622
&cancelled_filenames,
3624
&mandos_client_exited,
3625
&password_is_read));
3627
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3629
const task_context *const added_read_task
3630
= find_matching_task(queue,
3631
(task_context){ .func=read_inotify_event });
3632
g_assert_nonnull(added_read_task);
3634
g_assert_cmpint(added_read_task->fd, >, 2);
3635
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3637
char *sourcedir = make_temporary_directory();
3638
g_assert_nonnull(sourcedir);
3640
__attribute__((cleanup(cleanup_string)))
3641
char *filename = make_temporary_file_in_directory(sourcedir);
3642
g_assert_nonnull(filename);
3644
__attribute__((cleanup(cleanup_string)))
3645
char *targetfilename = NULL;
3646
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3647
basename(filename)), >, 0);
3648
g_assert_nonnull(targetfilename);
3650
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3651
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3654
/* "sufficient to read at least one event." - inotify(7) */
3655
const size_t ievent_size = (sizeof(struct inotify_event)
3657
struct inotify_event *ievent = malloc(ievent_size);
3658
g_assert_nonnull(ievent);
3660
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3662
g_assert_cmpint((int)read_size, >, 0);
3663
g_assert_true(ievent->mask & IN_MOVED_TO);
3664
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3668
g_assert_cmpint(unlink(targetfilename), ==, 0);
3669
g_assert_cmpint(rmdir(watchdir), ==, 0);
3673
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3674
test_fixture *fixture,
3675
__attribute__((unused))
3676
gconstpointer user_data){
3677
__attribute__((cleanup(cleanup_close)))
3678
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3679
g_assert_cmpint(epoll_fd, >=, 0);
3680
__attribute__((cleanup(cleanup_queue)))
3681
task_queue *queue = create_queue();
3682
g_assert_nonnull(queue);
3683
__attribute__((cleanup(string_set_clear)))
3684
string_set cancelled_filenames = {};
3685
const mono_microsecs current_time = 0;
3687
bool quit_now = false;
3688
buffer password = {};
3689
bool mandos_client_exited = false;
3690
bool password_is_read = false;
3692
__attribute__((cleanup(cleanup_string)))
3693
char *tempdir = make_temporary_directory();
3694
g_assert_nonnull(tempdir);
3696
__attribute__((cleanup(cleanup_string)))
3697
char *tempfile = make_temporary_file_in_directory(tempdir);
3698
g_assert_nonnull(tempfile);
3700
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3702
&cancelled_filenames,
3704
&mandos_client_exited,
3705
&password_is_read));
3706
g_assert_cmpint(unlink(tempfile), ==, 0);
3708
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3710
const task_context *const added_read_task
3711
= find_matching_task(queue,
3712
(task_context){ .func=read_inotify_event });
3713
g_assert_nonnull(added_read_task);
3715
g_assert_cmpint(added_read_task->fd, >, 2);
3716
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3718
/* "sufficient to read at least one event." - inotify(7) */
3719
const size_t ievent_size = (sizeof(struct inotify_event)
3721
struct inotify_event *ievent = malloc(ievent_size);
3722
g_assert_nonnull(ievent);
3724
ssize_t read_size = 0;
3725
read_size = read(added_read_task->fd, ievent, ievent_size);
3727
g_assert_cmpint((int)read_size, >, 0);
3728
g_assert_true(ievent->mask & IN_DELETE);
3729
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3733
g_assert_cmpint(rmdir(tempdir), ==, 0);
3736
static void test_read_inotify_event_readerror(__attribute__((unused))
3737
test_fixture *fixture,
3738
__attribute__((unused))
3741
__attribute__((cleanup(cleanup_close)))
3742
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3743
g_assert_cmpint(epoll_fd, >=, 0);
3744
const mono_microsecs current_time = 0;
3746
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3747
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3749
bool quit_now = false;
3750
__attribute__((cleanup(cleanup_queue)))
3751
task_queue *queue = create_queue();
3752
g_assert_nonnull(queue);
3754
task_context task = {
3755
.func=read_inotify_event,
3758
.quit_now=&quit_now,
3759
.filename=strdup("/nonexistent"),
3760
.cancelled_filenames = &(string_set){},
3762
.current_time=¤t_time,
3764
g_assert_nonnull(task.filename);
3765
run_task_with_stderr_to_dev_null(task, queue);
3766
g_assert_true(quit_now);
3767
g_assert_true(queue->next_run == 0);
3768
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3770
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3772
g_assert_cmpint(close(fd), ==, -1);
3775
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3776
test_fixture *fixture,
3777
__attribute__((unused))
3780
const mono_microsecs current_time = 17;
3783
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3784
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3786
bool quit_now = false;
3787
buffer password = {};
3788
bool mandos_client_exited = false;
3789
bool password_is_read = false;
3790
__attribute__((cleanup(cleanup_queue)))
3791
task_queue *queue = create_queue();
3792
g_assert_nonnull(queue);
3794
task_context task = {
3795
.func=read_inotify_event,
3798
.quit_now=&quit_now,
3799
.password=&password,
3800
.filename=strdup("/nonexistent"),
3801
.cancelled_filenames = &(string_set){},
3803
.current_time=¤t_time,
3804
.mandos_client_exited=&mandos_client_exited,
3805
.password_is_read=&password_is_read,
3807
g_assert_nonnull(task.filename);
3808
run_task_with_stderr_to_dev_null(task, queue);
3810
g_assert_nonnull(find_matching_task(queue, task));
3811
g_assert_true(queue->next_run == 1000000 + current_time);
3813
g_assert_cmpint(close(pipefds[0]), ==, 0);
3814
g_assert_cmpint(close(pipefds[1]), ==, 0);
3817
static void test_read_inotify_event_nodata(__attribute__((unused))
3818
test_fixture *fixture,
3819
__attribute__((unused))
3820
gconstpointer user_data){
3821
__attribute__((cleanup(cleanup_close)))
3822
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3823
g_assert_cmpint(epoll_fd, >=, 0);
3824
const mono_microsecs current_time = 0;
3827
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3829
bool quit_now = false;
3830
buffer password = {};
3831
bool mandos_client_exited = false;
3832
bool password_is_read = false;
3833
__attribute__((cleanup(cleanup_queue)))
3834
task_queue *queue = create_queue();
3835
g_assert_nonnull(queue);
3837
task_context task = {
3838
.func=read_inotify_event,
3841
.quit_now=&quit_now,
3842
.password=&password,
3843
.filename=strdup("/nonexistent"),
3844
.cancelled_filenames = &(string_set){},
3846
.current_time=¤t_time,
3847
.mandos_client_exited=&mandos_client_exited,
3848
.password_is_read=&password_is_read,
3850
g_assert_nonnull(task.filename);
3851
task.func(task, queue);
3852
g_assert_false(quit_now);
3853
g_assert_true(queue->next_run == 0);
3854
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3856
g_assert_nonnull(find_matching_task(queue, (task_context){
3857
.func=read_inotify_event,
3860
.quit_now=&quit_now,
3861
.password=&password,
3862
.filename=task.filename,
3863
.cancelled_filenames=task.cancelled_filenames,
3864
.current_time=¤t_time,
3865
.mandos_client_exited=&mandos_client_exited,
3866
.password_is_read=&password_is_read,
3869
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3870
EPOLLIN | EPOLLRDHUP));
3872
g_assert_cmpint(close(pipefds[1]), ==, 0);
3875
static void test_read_inotify_event_eof(__attribute__((unused))
3876
test_fixture *fixture,
3877
__attribute__((unused))
3878
gconstpointer user_data){
3879
__attribute__((cleanup(cleanup_close)))
3880
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3881
g_assert_cmpint(epoll_fd, >=, 0);
3882
const mono_microsecs current_time = 0;
3885
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3886
g_assert_cmpint(close(pipefds[1]), ==, 0);
3888
bool quit_now = false;
3889
buffer password = {};
3890
__attribute__((cleanup(cleanup_queue)))
3891
task_queue *queue = create_queue();
3892
g_assert_nonnull(queue);
3894
task_context task = {
3895
.func=read_inotify_event,
3898
.quit_now=&quit_now,
3899
.password=&password,
3900
.filename=strdup("/nonexistent"),
3901
.cancelled_filenames = &(string_set){},
3903
.current_time=¤t_time,
3905
run_task_with_stderr_to_dev_null(task, queue);
3906
g_assert_true(quit_now);
3907
g_assert_true(queue->next_run == 0);
3908
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3910
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3912
g_assert_cmpint(close(pipefds[0]), ==, -1);
3916
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
3917
test_fixture *fixture,
3918
__attribute__((unused))
3919
gconstpointer user_data){
3920
__attribute__((cleanup(cleanup_close)))
3921
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3922
g_assert_cmpint(epoll_fd, >=, 0);
3923
const mono_microsecs current_time = 0;
3926
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3928
/* "sufficient to read at least one event." - inotify(7) */
3929
const size_t ievent_max_size = (sizeof(struct inotify_event)
3931
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
3932
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
3933
struct inotify_event *ievent = ((struct inotify_event *)
3936
const char dummy_file_name[] = "ask.dummy_file_name";
3937
ievent->mask = IN_CLOSE_WRITE;
3938
ievent->len = sizeof(dummy_file_name);
3939
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
3940
const size_t ievent_size = (sizeof(struct inotify_event)
3941
+ sizeof(dummy_file_name));
3942
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
3944
g_assert_cmpint(close(pipefds[1]), ==, 0);
3946
bool quit_now = false;
3947
buffer password = {};
3948
bool mandos_client_exited = false;
3949
bool password_is_read = false;
3950
__attribute__((cleanup(cleanup_queue)))
3951
task_queue *queue = create_queue();
3952
g_assert_nonnull(queue);
3954
task_context task = {
3955
.func=read_inotify_event,
3958
.quit_now=&quit_now,
3959
.password=&password,
3960
.filename=strdup("/nonexistent"),
3961
.cancelled_filenames = &(string_set){},
3963
.current_time=¤t_time,
3964
.mandos_client_exited=&mandos_client_exited,
3965
.password_is_read=&password_is_read,
3967
task.func(task, queue);
3968
g_assert_false(quit_now);
3969
g_assert_true(queue->next_run != 0);
3970
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
3972
g_assert_nonnull(find_matching_task(queue, (task_context){
3973
.func=read_inotify_event,
3976
.quit_now=&quit_now,
3977
.password=&password,
3978
.filename=task.filename,
3979
.cancelled_filenames=task.cancelled_filenames,
3980
.current_time=¤t_time,
3981
.mandos_client_exited=&mandos_client_exited,
3982
.password_is_read=&password_is_read,
3985
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3986
EPOLLIN | EPOLLRDHUP));
3988
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
3990
__attribute__((cleanup(cleanup_string)))
3991
char *filename = NULL;
3992
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
3993
dummy_file_name), >, 0);
3994
g_assert_nonnull(filename);
3995
g_assert_nonnull(find_matching_task(queue, (task_context){
3996
.func=open_and_parse_question,
3999
.question_filename=filename,
4000
.password=&password,
4001
.cancelled_filenames=task.cancelled_filenames,
4002
.current_time=¤t_time,
4003
.mandos_client_exited=&mandos_client_exited,
4004
.password_is_read=&password_is_read,
4009
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4010
test_fixture *fixture,
4011
__attribute__((unused))
4012
gconstpointer user_data){
4013
__attribute__((cleanup(cleanup_close)))
4014
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4015
g_assert_cmpint(epoll_fd, >=, 0);
4016
const mono_microsecs current_time = 0;
4019
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4021
/* "sufficient to read at least one event." - inotify(7) */
4022
const size_t ievent_max_size = (sizeof(struct inotify_event)
4024
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4025
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4026
struct inotify_event *ievent = ((struct inotify_event *)
4029
const char dummy_file_name[] = "ask.dummy_file_name";
4030
ievent->mask = IN_MOVED_TO;
4031
ievent->len = sizeof(dummy_file_name);
4032
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4033
const size_t ievent_size = (sizeof(struct inotify_event)
4034
+ sizeof(dummy_file_name));
4035
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4037
g_assert_cmpint(close(pipefds[1]), ==, 0);
4039
bool quit_now = false;
4040
buffer password = {};
4041
bool mandos_client_exited = false;
4042
bool password_is_read = false;
4043
__attribute__((cleanup(cleanup_queue)))
4044
task_queue *queue = create_queue();
4045
g_assert_nonnull(queue);
4047
task_context task = {
4048
.func=read_inotify_event,
4051
.quit_now=&quit_now,
4052
.password=&password,
4053
.filename=strdup("/nonexistent"),
4054
.cancelled_filenames = &(string_set){},
4056
.current_time=¤t_time,
4057
.mandos_client_exited=&mandos_client_exited,
4058
.password_is_read=&password_is_read,
4060
task.func(task, queue);
4061
g_assert_false(quit_now);
4062
g_assert_true(queue->next_run != 0);
4063
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4065
g_assert_nonnull(find_matching_task(queue, (task_context){
4066
.func=read_inotify_event,
4069
.quit_now=&quit_now,
4070
.password=&password,
4071
.filename=task.filename,
4072
.cancelled_filenames=task.cancelled_filenames,
4073
.current_time=¤t_time,
4074
.mandos_client_exited=&mandos_client_exited,
4075
.password_is_read=&password_is_read,
4078
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4079
EPOLLIN | EPOLLRDHUP));
4081
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4083
__attribute__((cleanup(cleanup_string)))
4084
char *filename = NULL;
4085
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4086
dummy_file_name), >, 0);
4087
g_assert_nonnull(filename);
4088
g_assert_nonnull(find_matching_task(queue, (task_context){
4089
.func=open_and_parse_question,
4092
.question_filename=filename,
4093
.password=&password,
4094
.cancelled_filenames=task.cancelled_filenames,
4095
.current_time=¤t_time,
4096
.mandos_client_exited=&mandos_client_exited,
4097
.password_is_read=&password_is_read,
4101
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4102
test_fixture *fixture,
4103
__attribute__((unused))
4106
__attribute__((cleanup(cleanup_close)))
4107
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4108
g_assert_cmpint(epoll_fd, >=, 0);
4109
__attribute__((cleanup(string_set_clear)))
4110
string_set cancelled_filenames = {};
4111
const mono_microsecs current_time = 0;
4114
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4116
/* "sufficient to read at least one event." - inotify(7) */
4117
const size_t ievent_max_size = (sizeof(struct inotify_event)
4119
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4120
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4121
struct inotify_event *ievent = ((struct inotify_event *)
4124
const char dummy_file_name[] = "ask.dummy_file_name";
4125
ievent->mask = IN_DELETE;
4126
ievent->len = sizeof(dummy_file_name);
4127
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4128
const size_t ievent_size = (sizeof(struct inotify_event)
4129
+ sizeof(dummy_file_name));
4130
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4132
g_assert_cmpint(close(pipefds[1]), ==, 0);
4134
bool quit_now = false;
4135
buffer password = {};
4136
bool mandos_client_exited = false;
4137
bool password_is_read = false;
4138
__attribute__((cleanup(cleanup_queue)))
4139
task_queue *queue = create_queue();
4140
g_assert_nonnull(queue);
4142
task_context task = {
4143
.func=read_inotify_event,
4146
.quit_now=&quit_now,
4147
.password=&password,
4148
.filename=strdup("/nonexistent"),
4149
.cancelled_filenames=&cancelled_filenames,
4150
.current_time=¤t_time,
4151
.mandos_client_exited=&mandos_client_exited,
4152
.password_is_read=&password_is_read,
4154
task.func(task, queue);
4155
g_assert_false(quit_now);
4156
g_assert_true(queue->next_run == 0);
4157
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4159
g_assert_nonnull(find_matching_task(queue, (task_context){
4160
.func=read_inotify_event,
4163
.quit_now=&quit_now,
4164
.password=&password,
4165
.filename=task.filename,
4166
.cancelled_filenames=&cancelled_filenames,
4167
.current_time=¤t_time,
4168
.mandos_client_exited=&mandos_client_exited,
4169
.password_is_read=&password_is_read,
4172
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4173
EPOLLIN | EPOLLRDHUP));
4175
__attribute__((cleanup(cleanup_string)))
4176
char *filename = NULL;
4177
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4178
dummy_file_name), >, 0);
4179
g_assert_nonnull(filename);
4180
g_assert_true(string_set_contains(*task.cancelled_filenames,
4185
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4186
test_fixture *fixture,
4187
__attribute__((unused))
4190
__attribute__((cleanup(cleanup_close)))
4191
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4192
g_assert_cmpint(epoll_fd, >=, 0);
4193
const mono_microsecs current_time = 0;
4196
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4198
/* "sufficient to read at least one event." - inotify(7) */
4199
const size_t ievent_max_size = (sizeof(struct inotify_event)
4201
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4202
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4203
struct inotify_event *ievent = ((struct inotify_event *)
4206
const char dummy_file_name[] = "ignored.dummy_file_name";
4207
ievent->mask = IN_CLOSE_WRITE;
4208
ievent->len = sizeof(dummy_file_name);
4209
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4210
const size_t ievent_size = (sizeof(struct inotify_event)
4211
+ sizeof(dummy_file_name));
4212
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4214
g_assert_cmpint(close(pipefds[1]), ==, 0);
4216
bool quit_now = false;
4217
buffer password = {};
4218
bool mandos_client_exited = false;
4219
bool password_is_read = false;
4220
__attribute__((cleanup(cleanup_queue)))
4221
task_queue *queue = create_queue();
4222
g_assert_nonnull(queue);
4224
task_context task = {
4225
.func=read_inotify_event,
4228
.quit_now=&quit_now,
4229
.password=&password,
4230
.filename=strdup("/nonexistent"),
4231
.cancelled_filenames = &(string_set){},
4233
.current_time=¤t_time,
4234
.mandos_client_exited=&mandos_client_exited,
4235
.password_is_read=&password_is_read,
4237
task.func(task, queue);
4238
g_assert_false(quit_now);
4239
g_assert_true(queue->next_run == 0);
4240
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4242
g_assert_nonnull(find_matching_task(queue, (task_context){
4243
.func=read_inotify_event,
4246
.quit_now=&quit_now,
4247
.password=&password,
4248
.filename=task.filename,
4249
.cancelled_filenames=task.cancelled_filenames,
4250
.current_time=¤t_time,
4251
.mandos_client_exited=&mandos_client_exited,
4252
.password_is_read=&password_is_read,
4255
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4256
EPOLLIN | EPOLLRDHUP));
4260
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4261
test_fixture *fixture,
4262
__attribute__((unused))
4263
gconstpointer user_data){
4264
__attribute__((cleanup(cleanup_close)))
4265
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4266
g_assert_cmpint(epoll_fd, >=, 0);
4267
const mono_microsecs current_time = 0;
4270
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4272
/* "sufficient to read at least one event." - inotify(7) */
4273
const size_t ievent_max_size = (sizeof(struct inotify_event)
4275
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4276
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4277
struct inotify_event *ievent = ((struct inotify_event *)
4280
const char dummy_file_name[] = "ignored.dummy_file_name";
4281
ievent->mask = IN_MOVED_TO;
4282
ievent->len = sizeof(dummy_file_name);
4283
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4284
const size_t ievent_size = (sizeof(struct inotify_event)
4285
+ sizeof(dummy_file_name));
4286
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4288
g_assert_cmpint(close(pipefds[1]), ==, 0);
4290
bool quit_now = false;
4291
buffer password = {};
4292
bool mandos_client_exited = false;
4293
bool password_is_read = false;
4294
__attribute__((cleanup(cleanup_queue)))
4295
task_queue *queue = create_queue();
4296
g_assert_nonnull(queue);
4298
task_context task = {
4299
.func=read_inotify_event,
4302
.quit_now=&quit_now,
4303
.password=&password,
4304
.filename=strdup("/nonexistent"),
4305
.cancelled_filenames = &(string_set){},
4307
.current_time=¤t_time,
4308
.mandos_client_exited=&mandos_client_exited,
4309
.password_is_read=&password_is_read,
4311
task.func(task, queue);
4312
g_assert_false(quit_now);
4313
g_assert_true(queue->next_run == 0);
4314
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4316
g_assert_nonnull(find_matching_task(queue, (task_context){
4317
.func=read_inotify_event,
4320
.quit_now=&quit_now,
4321
.password=&password,
4322
.filename=task.filename,
4323
.cancelled_filenames=task.cancelled_filenames,
4324
.current_time=¤t_time,
4325
.mandos_client_exited=&mandos_client_exited,
4326
.password_is_read=&password_is_read,
4329
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4330
EPOLLIN | EPOLLRDHUP));
4334
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4335
test_fixture *fixture,
4336
__attribute__((unused))
4339
__attribute__((cleanup(cleanup_close)))
4340
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4341
g_assert_cmpint(epoll_fd, >=, 0);
4342
__attribute__((cleanup(string_set_clear)))
4343
string_set cancelled_filenames = {};
4344
const mono_microsecs current_time = 0;
4347
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4349
/* "sufficient to read at least one event." - inotify(7) */
4350
const size_t ievent_max_size = (sizeof(struct inotify_event)
4352
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4353
char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4354
struct inotify_event *ievent = ((struct inotify_event *)
4357
const char dummy_file_name[] = "ignored.dummy_file_name";
4358
ievent->mask = IN_DELETE;
4359
ievent->len = sizeof(dummy_file_name);
4360
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4361
const size_t ievent_size = (sizeof(struct inotify_event)
4362
+ sizeof(dummy_file_name));
4363
g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4365
g_assert_cmpint(close(pipefds[1]), ==, 0);
4367
bool quit_now = false;
4368
buffer password = {};
4369
bool mandos_client_exited = false;
4370
bool password_is_read = false;
4371
__attribute__((cleanup(cleanup_queue)))
4372
task_queue *queue = create_queue();
4373
g_assert_nonnull(queue);
4375
task_context task = {
4376
.func=read_inotify_event,
4379
.quit_now=&quit_now,
4380
.password=&password,
4381
.filename=strdup("/nonexistent"),
4382
.cancelled_filenames=&cancelled_filenames,
4383
.current_time=¤t_time,
4384
.mandos_client_exited=&mandos_client_exited,
4385
.password_is_read=&password_is_read,
4387
task.func(task, queue);
4388
g_assert_false(quit_now);
4389
g_assert_true(queue->next_run == 0);
4390
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4392
g_assert_nonnull(find_matching_task(queue, (task_context){
4393
.func=read_inotify_event,
4396
.quit_now=&quit_now,
4397
.password=&password,
4398
.filename=task.filename,
4399
.cancelled_filenames=&cancelled_filenames,
4400
.current_time=¤t_time,
4401
.mandos_client_exited=&mandos_client_exited,
4402
.password_is_read=&password_is_read,
4405
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4406
EPOLLIN | EPOLLRDHUP));
4408
__attribute__((cleanup(cleanup_string)))
4409
char *filename = NULL;
4410
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4411
dummy_file_name), >, 0);
4412
g_assert_nonnull(filename);
4413
g_assert_false(string_set_contains(cancelled_filenames, filename));
4417
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4418
test_fixture *fixture,
4419
__attribute__((unused))
4420
gconstpointer user_data){
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
bool mandos_client_exited = false;
4427
bool password_is_read = false;
4428
__attribute__((cleanup(cleanup_queue)))
4429
task_queue *queue = create_queue();
4430
g_assert_nonnull(queue);
4432
char *const filename = strdup("/nonexistent");
4433
g_assert_nonnull(filename);
4434
task_context task = {
4435
.func=open_and_parse_question,
4436
.question_filename=filename,
4438
.password=(buffer[]){{}},
4440
.cancelled_filenames=&cancelled_filenames,
4441
.current_time=(mono_microsecs[]){0},
4442
.mandos_client_exited=&mandos_client_exited,
4443
.password_is_read=&password_is_read,
4445
task.func(task, queue);
4446
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4449
static void test_open_and_parse_question_EIO(__attribute__((unused))
4450
test_fixture *fixture,
4451
__attribute__((unused))
4452
gconstpointer user_data){
4453
__attribute__((cleanup(cleanup_close)))
4454
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4455
g_assert_cmpint(epoll_fd, >=, 0);
4456
__attribute__((cleanup(string_set_clear)))
4457
string_set cancelled_filenames = {};
4458
buffer password = {};
4459
bool mandos_client_exited = false;
4460
bool password_is_read = false;
4461
__attribute__((cleanup(cleanup_queue)))
4462
task_queue *queue = create_queue();
4463
g_assert_nonnull(queue);
4464
const mono_microsecs current_time = 0;
4466
char *filename = strdup("/proc/self/mem");
4467
task_context task = {
4468
.func=open_and_parse_question,
4469
.question_filename=filename,
4471
.password=&password,
4473
.cancelled_filenames=&cancelled_filenames,
4474
.current_time=¤t_time,
4475
.mandos_client_exited=&mandos_client_exited,
4476
.password_is_read=&password_is_read,
4478
run_task_with_stderr_to_dev_null(task, queue);
4479
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4483
test_open_and_parse_question_parse_error(__attribute__((unused))
4484
test_fixture *fixture,
4485
__attribute__((unused))
4486
gconstpointer user_data){
4487
__attribute__((cleanup(cleanup_close)))
4488
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4489
g_assert_cmpint(epoll_fd, >=, 0);
4490
__attribute__((cleanup(string_set_clear)))
4491
string_set cancelled_filenames = {};
4492
__attribute__((cleanup(cleanup_queue)))
4493
task_queue *queue = create_queue();
4494
g_assert_nonnull(queue);
4496
__attribute__((cleanup(cleanup_string)))
4497
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4498
g_assert_nonnull(tempfilename);
4499
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4500
g_assert_cmpint(tempfile, >, 0);
4501
const char bad_data[] = "this is bad syntax\n";
4502
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4503
==, sizeof(bad_data));
4504
g_assert_cmpint(close(tempfile), ==, 0);
4506
char *const filename = strdup(tempfilename);
4507
g_assert_nonnull(filename);
4508
task_context task = {
4509
.func=open_and_parse_question,
4510
.question_filename=filename,
4512
.password=(buffer[]){{}},
4514
.cancelled_filenames=&cancelled_filenames,
4515
.current_time=(mono_microsecs[]){0},
4516
.mandos_client_exited=(bool[]){false},
4517
.password_is_read=(bool[]){false},
4519
run_task_with_stderr_to_dev_null(task, queue);
4521
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4523
g_assert_cmpint(unlink(tempfilename), ==, 0);
4527
void test_open_and_parse_question_nosocket(__attribute__((unused))
4528
test_fixture *fixture,
4529
__attribute__((unused))
4530
gconstpointer user_data){
4531
__attribute__((cleanup(cleanup_close)))
4532
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4533
g_assert_cmpint(epoll_fd, >=, 0);
4534
__attribute__((cleanup(string_set_clear)))
4535
string_set cancelled_filenames = {};
4536
__attribute__((cleanup(cleanup_queue)))
4537
task_queue *queue = create_queue();
4538
g_assert_nonnull(queue);
4540
__attribute__((cleanup(cleanup_string)))
4541
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4542
g_assert_nonnull(tempfilename);
4543
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4544
g_assert_cmpint(questionfile, >, 0);
4545
FILE *qf = fdopen(questionfile, "w");
4546
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4547
g_assert_cmpint(fclose(qf), ==, 0);
4549
char *const filename = strdup(tempfilename);
4550
g_assert_nonnull(filename);
4551
task_context task = {
4552
.func=open_and_parse_question,
4553
.question_filename=filename,
4555
.password=(buffer[]){{}},
4557
.cancelled_filenames=&cancelled_filenames,
4558
.current_time=(mono_microsecs[]){0},
4559
.mandos_client_exited=(bool[]){false},
4560
.password_is_read=(bool[]){false},
4562
run_task_with_stderr_to_dev_null(task, queue);
4563
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4565
g_assert_cmpint(unlink(tempfilename), ==, 0);
4569
void test_open_and_parse_question_badsocket(__attribute__((unused))
4570
test_fixture *fixture,
4571
__attribute__((unused))
4572
gconstpointer user_data){
4573
__attribute__((cleanup(cleanup_close)))
4574
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4575
g_assert_cmpint(epoll_fd, >=, 0);
4576
__attribute__((cleanup(string_set_clear)))
4577
string_set cancelled_filenames = {};
4578
__attribute__((cleanup(cleanup_queue)))
4579
task_queue *queue = create_queue();
4580
g_assert_nonnull(queue);
4582
__attribute__((cleanup(cleanup_string)))
4583
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4584
g_assert_nonnull(tempfilename);
4585
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4586
g_assert_cmpint(questionfile, >, 0);
4587
FILE *qf = fdopen(questionfile, "w");
4588
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4589
g_assert_cmpint(fclose(qf), ==, 0);
4591
char *const filename = strdup(tempfilename);
4592
g_assert_nonnull(filename);
4593
task_context task = {
4594
.func=open_and_parse_question,
4595
.question_filename=filename,
4597
.password=(buffer[]){{}},
4599
.cancelled_filenames=&cancelled_filenames,
4600
.current_time=(mono_microsecs[]){0},
4601
.mandos_client_exited=(bool[]){false},
4602
.password_is_read=(bool[]){false},
4604
run_task_with_stderr_to_dev_null(task, queue);
4605
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4607
g_assert_cmpint(unlink(tempfilename), ==, 0);
4611
void test_open_and_parse_question_nopid(__attribute__((unused))
4612
test_fixture *fixture,
4613
__attribute__((unused))
4614
gconstpointer user_data){
4615
__attribute__((cleanup(cleanup_close)))
4616
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4617
g_assert_cmpint(epoll_fd, >=, 0);
4618
__attribute__((cleanup(string_set_clear)))
4619
string_set cancelled_filenames = {};
4620
__attribute__((cleanup(cleanup_queue)))
4621
task_queue *queue = create_queue();
4622
g_assert_nonnull(queue);
4624
__attribute__((cleanup(cleanup_string)))
4625
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4626
g_assert_nonnull(tempfilename);
4627
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4628
g_assert_cmpint(questionfile, >, 0);
4629
FILE *qf = fdopen(questionfile, "w");
4630
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
4631
g_assert_cmpint(fclose(qf), ==, 0);
4633
char *const filename = strdup(tempfilename);
4634
g_assert_nonnull(filename);
4635
task_context task = {
4636
.func=open_and_parse_question,
4637
.question_filename=filename,
4639
.password=(buffer[]){{}},
4641
.cancelled_filenames=&cancelled_filenames,
4642
.current_time=(mono_microsecs[]){0},
4643
.mandos_client_exited=(bool[]){false},
4644
.password_is_read=(bool[]){false},
4646
run_task_with_stderr_to_dev_null(task, queue);
4647
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4649
g_assert_cmpint(unlink(tempfilename), ==, 0);
4653
void test_open_and_parse_question_badpid(__attribute__((unused))
4654
test_fixture *fixture,
4655
__attribute__((unused))
4656
gconstpointer user_data){
4657
__attribute__((cleanup(cleanup_close)))
4658
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4659
g_assert_cmpint(epoll_fd, >=, 0);
4660
__attribute__((cleanup(string_set_clear)))
4661
string_set cancelled_filenames = {};
4662
__attribute__((cleanup(cleanup_queue)))
4663
task_queue *queue = create_queue();
4664
g_assert_nonnull(queue);
4666
__attribute__((cleanup(cleanup_string)))
4667
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4668
g_assert_nonnull(tempfilename);
4669
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4670
g_assert_cmpint(questionfile, >, 0);
4671
FILE *qf = fdopen(questionfile, "w");
4672
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
4674
g_assert_cmpint(fclose(qf), ==, 0);
4676
char *const filename = strdup(tempfilename);
4677
g_assert_nonnull(filename);
4678
task_context task = {
4679
.func=open_and_parse_question,
4680
.question_filename=filename,
4682
.password=(buffer[]){{}},
4684
.cancelled_filenames=&cancelled_filenames,
4685
.current_time=(mono_microsecs[]){0},
4686
.mandos_client_exited=(bool[]){false},
4687
.password_is_read=(bool[]){false},
4689
run_task_with_stderr_to_dev_null(task, queue);
4690
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4692
g_assert_cmpint(unlink(tempfilename), ==, 0);
4696
test_open_and_parse_question_noexist_pid(__attribute__((unused))
4697
test_fixture *fixture,
4698
__attribute__((unused))
4699
gconstpointer user_data){
4700
__attribute__((cleanup(cleanup_close)))
4701
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4702
g_assert_cmpint(epoll_fd, >=, 0);
4703
__attribute__((cleanup(string_set_clear)))
4704
string_set cancelled_filenames = {};
4705
buffer password = {};
4706
bool mandos_client_exited = false;
4707
bool password_is_read = false;
4708
__attribute__((cleanup(cleanup_queue)))
4709
task_queue *queue = create_queue();
4710
g_assert_nonnull(queue);
4711
const mono_microsecs current_time = 0;
4713
/* Find value of sysctl kernel.pid_max */
4714
uintmax_t pid_max = 0;
4715
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
4716
g_assert_nonnull(sysctl_pid_max);
4717
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
4719
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
4721
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
4722
g_assert_true(nonexisting_pid > 0); /* Overflow check */
4724
__attribute__((cleanup(cleanup_string)))
4725
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4726
g_assert_nonnull(tempfilename);
4727
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4728
g_assert_cmpint(questionfile, >, 0);
4729
FILE *qf = fdopen(questionfile, "w");
4730
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4731
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
4733
g_assert_cmpint(fclose(qf), ==, 0);
4735
char *const question_filename = strdup(tempfilename);
4736
g_assert_nonnull(question_filename);
4737
task_context task = {
4738
.func=open_and_parse_question,
4739
.question_filename=question_filename,
4741
.password=&password,
4742
.filename=question_filename,
4743
.cancelled_filenames=&cancelled_filenames,
4744
.current_time=¤t_time,
4745
.mandos_client_exited=&mandos_client_exited,
4746
.password_is_read=&password_is_read,
4748
run_task_with_stderr_to_dev_null(task, queue);
4749
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4751
g_assert_cmpint(unlink(tempfilename), ==, 0);
4755
test_open_and_parse_question_no_notafter(__attribute__((unused))
4756
test_fixture *fixture,
4757
__attribute__((unused))
4758
gconstpointer user_data){
4759
__attribute__((cleanup(cleanup_close)))
4760
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4761
g_assert_cmpint(epoll_fd, >=, 0);
4762
__attribute__((cleanup(string_set_clear)))
4763
string_set cancelled_filenames = {};
4764
buffer password = {};
4765
bool mandos_client_exited = false;
4766
bool password_is_read = false;
4767
__attribute__((cleanup(cleanup_queue)))
4768
task_queue *queue = create_queue();
4769
g_assert_nonnull(queue);
4770
const mono_microsecs current_time = 0;
4772
__attribute__((cleanup(cleanup_string)))
4773
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4774
g_assert_nonnull(tempfilename);
4775
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4776
g_assert_cmpint(questionfile, >, 0);
4777
FILE *qf = fdopen(questionfile, "w");
4778
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4779
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
4780
g_assert_cmpint(fclose(qf), ==, 0);
4782
char *const filename = strdup(tempfilename);
4783
g_assert_nonnull(filename);
4784
task_context task = {
4785
.func=open_and_parse_question,
4786
.question_filename=filename,
4788
.password=&password,
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_cmpuint((unsigned int)queue->length, ==, 1);
4798
__attribute__((cleanup(cleanup_string)))
4799
char *socket_filename = strdup("/nonexistent");
4800
g_assert_nonnull(socket_filename);
4801
g_assert_nonnull(find_matching_task(queue, (task_context){
4802
.func=connect_question_socket,
4803
.question_filename=tempfilename,
4804
.filename=socket_filename,
4806
.password=&password,
4807
.current_time=¤t_time,
4808
.mandos_client_exited=&mandos_client_exited,
4809
.password_is_read=&password_is_read,
4812
g_assert_true(queue->next_run != 0);
4814
g_assert_cmpint(unlink(tempfilename), ==, 0);
4818
test_open_and_parse_question_bad_notafter(__attribute__((unused))
4819
test_fixture *fixture,
4820
__attribute__((unused))
4821
gconstpointer user_data){
4822
__attribute__((cleanup(cleanup_close)))
4823
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4824
g_assert_cmpint(epoll_fd, >=, 0);
4825
__attribute__((cleanup(string_set_clear)))
4826
string_set cancelled_filenames = {};
4827
buffer password = {};
4828
bool mandos_client_exited = false;
4829
bool password_is_read = false;
4830
__attribute__((cleanup(cleanup_queue)))
4831
task_queue *queue = create_queue();
4832
g_assert_nonnull(queue);
4833
const mono_microsecs current_time = 0;
4835
__attribute__((cleanup(cleanup_string)))
4836
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4837
g_assert_nonnull(tempfilename);
4838
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4839
g_assert_cmpint(questionfile, >, 0);
4840
FILE *qf = fdopen(questionfile, "w");
4841
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4842
PRIuMAX "\nNotAfter=\n",
4843
(uintmax_t)getpid()), >, 0);
4844
g_assert_cmpint(fclose(qf), ==, 0);
4846
char *const filename = strdup(tempfilename);
4847
g_assert_nonnull(filename);
4848
task_context task = {
4849
.func=open_and_parse_question,
4850
.question_filename=filename,
4852
.password=&password,
4854
.cancelled_filenames=&cancelled_filenames,
4855
.current_time=¤t_time,
4856
.mandos_client_exited=&mandos_client_exited,
4857
.password_is_read=&password_is_read,
4859
run_task_with_stderr_to_dev_null(task, queue);
4860
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4862
__attribute__((cleanup(cleanup_string)))
4863
char *socket_filename = strdup("/nonexistent");
4864
g_assert_nonnull(find_matching_task(queue, (task_context){
4865
.func=connect_question_socket,
4866
.question_filename=tempfilename,
4867
.filename=socket_filename,
4869
.password=&password,
4870
.current_time=¤t_time,
4871
.mandos_client_exited=&mandos_client_exited,
4872
.password_is_read=&password_is_read,
4874
g_assert_true(queue->next_run != 0);
4876
g_assert_cmpint(unlink(tempfilename), ==, 0);
4880
void assert_open_and_parse_question_with_notafter(const mono_microsecs
4882
const mono_microsecs
4884
const mono_microsecs
4886
__attribute__((cleanup(cleanup_close)))
4887
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4888
g_assert_cmpint(epoll_fd, >=, 0);
4889
__attribute__((cleanup(string_set_clear)))
4890
string_set cancelled_filenames = {};
4891
buffer password = {};
4892
bool mandos_client_exited = false;
4893
bool password_is_read = false;
4894
__attribute__((cleanup(cleanup_queue)))
4895
task_queue *queue = create_queue();
4896
g_assert_nonnull(queue);
4897
queue->next_run = next_queue_run;
4899
__attribute__((cleanup(cleanup_string)))
4900
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4901
g_assert_nonnull(tempfilename);
4902
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4903
g_assert_cmpint(questionfile, >, 0);
4904
FILE *qf = fdopen(questionfile, "w");
4905
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4906
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
4907
(uintmax_t)getpid(), notafter), >, 0);
4908
g_assert_cmpint(fclose(qf), ==, 0);
4910
char *const filename = strdup(tempfilename);
4911
g_assert_nonnull(filename);
4912
task_context task = {
4913
.func=open_and_parse_question,
4914
.question_filename=filename,
4916
.password=&password,
4918
.cancelled_filenames=&cancelled_filenames,
4919
.current_time=¤t_time,
4920
.mandos_client_exited=&mandos_client_exited,
4921
.password_is_read=&password_is_read,
4923
task.func(task, queue);
4925
if(queue->length >= 1){
4926
__attribute__((cleanup(cleanup_string)))
4927
char *socket_filename = strdup("/nonexistent");
4928
g_assert_nonnull(find_matching_task(queue, (task_context){
4929
.func=connect_question_socket,
4930
.filename=socket_filename,
4932
.password=&password,
4933
.current_time=¤t_time,
4934
.cancelled_filenames=&cancelled_filenames,
4935
.mandos_client_exited=&mandos_client_exited,
4936
.password_is_read=&password_is_read,
4938
g_assert_true(queue->next_run != 0);
4942
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4943
} else if(current_time >= notafter) {
4944
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4946
g_assert_nonnull(find_matching_task(queue, (task_context){
4947
.func=cancel_old_question,
4948
.question_filename=tempfilename,
4949
.filename=tempfilename,
4951
.cancelled_filenames=&cancelled_filenames,
4952
.current_time=¤t_time,
4955
g_assert_true(queue->next_run == 1);
4957
g_assert_cmpint(unlink(tempfilename), ==, 0);
4961
test_open_and_parse_question_notafter_0(__attribute__((unused))
4962
test_fixture *fixture,
4963
__attribute__((unused))
4964
gconstpointer user_data){
4965
/* current_time, notafter, next_queue_run */
4966
assert_open_and_parse_question_with_notafter(0, 0, 0);
4970
test_open_and_parse_question_notafter_1(__attribute__((unused))
4971
test_fixture *fixture,
4972
__attribute__((unused))
4973
gconstpointer user_data){
4974
/* current_time, notafter, next_queue_run */
4975
assert_open_and_parse_question_with_notafter(0, 1, 0);
4979
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
4980
test_fixture *fixture,
4981
__attribute__((unused))
4982
gconstpointer user_data){
4983
/* current_time, notafter, next_queue_run */
4984
assert_open_and_parse_question_with_notafter(0, 1, 1);
4988
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
4989
test_fixture *fixture,
4990
__attribute__((unused))
4991
gconstpointer user_data){
4992
/* current_time, notafter, next_queue_run */
4993
assert_open_and_parse_question_with_notafter(0, 1, 2);
4997
test_open_and_parse_question_equal_notafter(__attribute__((unused))
4998
test_fixture *fixture,
4999
__attribute__((unused))
5000
gconstpointer user_data){
5001
/* current_time, notafter, next_queue_run */
5002
assert_open_and_parse_question_with_notafter(1, 1, 0);
5006
test_open_and_parse_question_late_notafter(__attribute__((unused))
5007
test_fixture *fixture,
5008
__attribute__((unused))
5009
gconstpointer user_data){
5010
/* current_time, notafter, next_queue_run */
5011
assert_open_and_parse_question_with_notafter(2, 1, 0);
5014
static void assert_cancel_old_question_param(const mono_microsecs
5016
const mono_microsecs
5018
const mono_microsecs
5020
const mono_microsecs
5022
__attribute__((cleanup(string_set_clear)))
5023
string_set cancelled_filenames = {};
5024
__attribute__((cleanup(cleanup_queue)))
5025
task_queue *queue = create_queue();
5026
g_assert_nonnull(queue);
5027
queue->next_run = next_queue_run;
5029
char *const question_filename = strdup("/nonexistent");
5030
g_assert_nonnull(question_filename);
5031
task_context task = {
5032
.func=cancel_old_question,
5033
.question_filename=question_filename,
5034
.filename=question_filename,
5036
.cancelled_filenames=&cancelled_filenames,
5037
.current_time=¤t_time,
5039
task.func(task, queue);
5041
if(current_time >= notafter){
5042
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5043
g_assert_true(string_set_contains(cancelled_filenames,
5046
g_assert_nonnull(find_matching_task(queue, (task_context){
5047
.func=cancel_old_question,
5048
.question_filename=question_filename,
5049
.filename=question_filename,
5051
.cancelled_filenames=&cancelled_filenames,
5052
.current_time=¤t_time,
5055
g_assert_false(string_set_contains(cancelled_filenames,
5056
question_filename));
5058
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5059
(unsigned int)next_set_to);
5062
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5063
test_fixture *fixture,
5064
__attribute__((unused))
5065
gconstpointer user_data){
5066
/* next_queue_run unset,
5067
cancellation should happen because time has come,
5068
next_queue_run should be unchanged */
5069
/* next_queue_run, notafter, current_time, next_set_to */
5070
assert_cancel_old_question_param(0, 1, 2, 0);
5073
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5074
test_fixture *fixture,
5075
__attribute__((unused))
5076
gconstpointer user_data){
5077
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5078
and current_time is not yet notafter or greater,
5079
update value of next_queue_run to value of notafter */
5080
/* next_queue_run, notafter, current_time, next_set_to */
5081
assert_cancel_old_question_param(0, 2, 1, 2);
5084
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5085
test_fixture *fixture,
5086
__attribute__((unused))
5087
gconstpointer user_data){
5088
/* next_queue_run 1,
5089
cancellation should happen because time has come,
5090
next_queue_run should be unchanged */
5091
/* next_queue_run, notafter, current_time, next_set_to */
5092
assert_cancel_old_question_param(1, 2, 3, 1);
5095
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5096
test_fixture *fixture,
5097
__attribute__((unused))
5098
gconstpointer user_data){
5099
/* If next_queue_run is set,
5100
and current_time is not yet notafter or greater,
5101
and notafter is larger than next_queue_run
5102
next_queue_run should be unchanged */
5103
/* next_queue_run, notafter, current_time, next_set_to */
5104
assert_cancel_old_question_param(1, 3, 2, 1);
5107
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5108
test_fixture *fixture,
5109
__attribute__((unused))
5110
gconstpointer user_data){
5111
/* next_queue_run 2,
5112
cancellation should happen because time has come,
5113
next_queue_run should be unchanged */
5114
/* next_queue_run, notafter, current_time, next_set_to */
5115
assert_cancel_old_question_param(2, 1, 3, 2);
5118
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5119
test_fixture *fixture,
5120
__attribute__((unused))
5121
gconstpointer user_data){
5122
/* If next_queue_run is set,
5123
and current_time is not yet notafter or greater,
5124
and notafter is larger than next_queue_run
5125
next_queue_run should be unchanged */
5126
/* next_queue_run, notafter, current_time, next_set_to */
5127
assert_cancel_old_question_param(2, 3, 1, 2);
5130
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5131
test_fixture *fixture,
5132
__attribute__((unused))
5133
gconstpointer user_data){
5134
/* next_queue_run 3,
5135
cancellation should happen because time has come,
5136
next_queue_run should be unchanged */
5137
/* next_queue_run, notafter, current_time, next_set_to */
5138
assert_cancel_old_question_param(3, 1, 2, 3);
5141
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5142
test_fixture *fixture,
5143
__attribute__((unused))
5144
gconstpointer user_data){
5145
/* If next_queue_run is set,
5146
and current_time is not yet notafter or greater,
5147
and notafter is smaller than next_queue_run
5148
update value of next_queue_run to value of notafter */
5149
/* next_queue_run, notafter, current_time, next_set_to */
5150
assert_cancel_old_question_param(3, 2, 1, 2);
5154
test_connect_question_socket_name_too_long(__attribute__((unused))
5155
test_fixture *fixture,
5156
__attribute__((unused))
5157
gconstpointer user_data){
5158
__attribute__((cleanup(cleanup_close)))
5159
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5160
g_assert_cmpint(epoll_fd, >=, 0);
5161
const char question_filename[] = "/nonexistent/question";
5162
__attribute__((cleanup(string_set_clear)))
5163
string_set cancelled_filenames = {};
5164
__attribute__((cleanup(cleanup_queue)))
5165
task_queue *queue = create_queue();
5166
g_assert_nonnull(queue);
5167
__attribute__((cleanup(cleanup_string)))
5168
char *tempdir = make_temporary_directory();
5169
g_assert_nonnull(tempdir);
5170
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5171
char socket_name[sizeof(unix_socket.sun_path)];
5172
memset(socket_name, 'x', sizeof(socket_name));
5173
socket_name[sizeof(socket_name)-1] = '\0';
5174
char *filename = NULL;
5175
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5177
g_assert_nonnull(filename);
5179
task_context task = {
5180
.func=connect_question_socket,
5181
.question_filename=strdup(question_filename),
5183
.password=(buffer[]){{}},
5185
.cancelled_filenames=&cancelled_filenames,
5186
.mandos_client_exited=(bool[]){false},
5187
.password_is_read=(bool[]){false},
5188
.current_time=(mono_microsecs[]){0},
5190
g_assert_nonnull(task.question_filename);
5191
run_task_with_stderr_to_dev_null(task, queue);
5193
g_assert_true(string_set_contains(cancelled_filenames,
5194
question_filename));
5195
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5196
g_assert_true(queue->next_run == 0);
5198
g_assert_cmpint(rmdir(tempdir), ==, 0);
5202
void test_connect_question_socket_connect_fail(__attribute__((unused))
5203
test_fixture *fixture,
5204
__attribute__((unused))
5207
__attribute__((cleanup(cleanup_close)))
5208
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5209
g_assert_cmpint(epoll_fd, >=, 0);
5210
const char question_filename[] = "/nonexistent/question";
5211
__attribute__((cleanup(string_set_clear)))
5212
string_set cancelled_filenames = {};
5213
const mono_microsecs current_time = 3;
5214
__attribute__((cleanup(cleanup_queue)))
5215
task_queue *queue = create_queue();
5216
g_assert_nonnull(queue);
5217
__attribute__((cleanup(cleanup_string)))
5218
char *tempdir = make_temporary_directory();
5219
g_assert_nonnull(tempdir);
5220
char socket_name[] = "nonexistent";
5221
char *filename = NULL;
5222
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5224
g_assert_nonnull(filename);
5226
task_context task = {
5227
.func=connect_question_socket,
5228
.question_filename=strdup(question_filename),
5230
.password=(buffer[]){{}},
5232
.cancelled_filenames=&cancelled_filenames,
5233
.mandos_client_exited=(bool[]){false},
5234
.password_is_read=(bool[]){false},
5235
.current_time=¤t_time,
5237
g_assert_nonnull(task.question_filename);
5238
run_task_with_stderr_to_dev_null(task, queue);
5240
g_assert_nonnull(find_matching_task(queue, task));
5242
g_assert_false(string_set_contains(cancelled_filenames,
5243
question_filename));
5244
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5245
g_assert_true(queue->next_run == 1000000 + current_time);
5247
g_assert_cmpint(rmdir(tempdir), ==, 0);
5251
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5252
test_fixture *fixture,
5253
__attribute__((unused))
5254
gconstpointer user_data){
5255
__attribute__((cleanup(cleanup_close)))
5256
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5257
__attribute__((cleanup(cleanup_string)))
5258
char *const question_filename = strdup("/nonexistent/question");
5259
g_assert_nonnull(question_filename);
5260
__attribute__((cleanup(string_set_clear)))
5261
string_set cancelled_filenames = {};
5262
const mono_microsecs current_time = 5;
5263
__attribute__((cleanup(cleanup_queue)))
5264
task_queue *queue = create_queue();
5265
g_assert_nonnull(queue);
5266
__attribute__((cleanup(cleanup_string)))
5267
char *tempdir = make_temporary_directory();
5268
g_assert_nonnull(tempdir);
5269
__attribute__((cleanup(cleanup_close)))
5270
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5271
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5272
g_assert_cmpint(sock_fd, >=, 0);
5273
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5274
const char socket_name[] = "socket_name";
5275
__attribute__((cleanup(cleanup_string)))
5276
char *filename = NULL;
5277
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5279
g_assert_nonnull(filename);
5280
g_assert_cmpint((int)strlen(filename), <,
5281
(int)sizeof(sock_name.sun_path));
5282
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5283
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5284
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5285
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5286
task_context task = {
5287
.func=connect_question_socket,
5288
.question_filename=strdup(question_filename),
5290
.password=(buffer[]){{}},
5291
.filename=strdup(filename),
5292
.cancelled_filenames=&cancelled_filenames,
5293
.mandos_client_exited=(bool[]){false},
5294
.password_is_read=(bool[]){false},
5295
.current_time=¤t_time,
5297
g_assert_nonnull(task.question_filename);
5298
run_task_with_stderr_to_dev_null(task, queue);
5300
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5301
const task_context *const added_task
5302
= find_matching_task(queue, task);
5303
g_assert_nonnull(added_task);
5304
g_assert_true(queue->next_run == 1000000 + current_time);
5306
g_assert_cmpint(unlink(filename), ==, 0);
5307
g_assert_cmpint(rmdir(tempdir), ==, 0);
5311
void test_connect_question_socket_usable(__attribute__((unused))
5312
test_fixture *fixture,
5313
__attribute__((unused))
5314
gconstpointer user_data){
5315
__attribute__((cleanup(cleanup_close)))
5316
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5317
g_assert_cmpint(epoll_fd, >=, 0);
5318
__attribute__((cleanup(cleanup_string)))
5319
char *const question_filename = strdup("/nonexistent/question");
5320
g_assert_nonnull(question_filename);
5321
__attribute__((cleanup(string_set_clear)))
5322
string_set cancelled_filenames = {};
5323
buffer password = {};
5324
bool mandos_client_exited = false;
5325
bool password_is_read = false;
5326
const mono_microsecs current_time = 0;
5327
__attribute__((cleanup(cleanup_queue)))
5328
task_queue *queue = create_queue();
5329
g_assert_nonnull(queue);
5330
__attribute__((cleanup(cleanup_string)))
5331
char *tempdir = make_temporary_directory();
5332
g_assert_nonnull(tempdir);
5333
__attribute__((cleanup(cleanup_close)))
5334
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5335
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5336
g_assert_cmpint(sock_fd, >=, 0);
5337
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5338
const char socket_name[] = "socket_name";
5339
__attribute__((cleanup(cleanup_string)))
5340
char *filename = NULL;
5341
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5343
g_assert_nonnull(filename);
5344
g_assert_cmpint((int)strlen(filename), <,
5345
(int)sizeof(sock_name.sun_path));
5346
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5347
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5348
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5349
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5350
task_context task = {
5351
.func=connect_question_socket,
5352
.question_filename=strdup(question_filename),
5354
.password=&password,
5355
.filename=strdup(filename),
5356
.cancelled_filenames=&cancelled_filenames,
5357
.mandos_client_exited=&mandos_client_exited,
5358
.password_is_read=&password_is_read,
5359
.current_time=¤t_time,
5361
g_assert_nonnull(task.question_filename);
5362
task.func(task, queue);
5364
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5365
const task_context *const added_task
5366
= find_matching_task(queue, (task_context){
5367
.func=send_password_to_socket,
5368
.question_filename=question_filename,
5371
.password=&password,
5372
.cancelled_filenames=&cancelled_filenames,
5373
.mandos_client_exited=&mandos_client_exited,
5374
.password_is_read=&password_is_read,
5375
.current_time=¤t_time,
5377
g_assert_nonnull(added_task);
5378
g_assert_cmpint(added_task->fd, >, 0);
5380
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5383
const int fd = added_task->fd;
5384
g_assert_cmpint(fd, >, 0);
5385
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5388
char write_data[PIPE_BUF];
5390
/* Construct test password buffer */
5391
/* Start with + since that is what the real procotol uses */
5392
write_data[0] = '+';
5393
/* Set a special character at string end just to mark the end */
5394
write_data[sizeof(write_data)-2] = 'y';
5395
/* Set NUL at buffer end, as suggested by the protocol */
5396
write_data[sizeof(write_data)-1] = '\0';
5397
/* Fill rest of password with 'x' */
5398
memset(write_data+1, 'x', sizeof(write_data)-3);
5399
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5400
MSG_NOSIGNAL), ==, sizeof(write_data));
5403
/* read from sock_fd */
5404
char read_data[sizeof(write_data)];
5405
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5406
==, sizeof(read_data));
5408
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5411
/* writing to sock_fd should fail */
5412
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5413
MSG_NOSIGNAL), <, 0);
5415
/* reading from fd should fail */
5416
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5417
MSG_NOSIGNAL), <, 0);
5419
g_assert_cmpint(unlink(filename), ==, 0);
5420
g_assert_cmpint(rmdir(tempdir), ==, 0);
5424
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5425
test_fixture *fixture,
5426
__attribute__((unused))
5429
__attribute__((cleanup(cleanup_close)))
5430
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5431
g_assert_cmpint(epoll_fd, >=, 0);
5432
__attribute__((cleanup(cleanup_string)))
5433
char *const question_filename = strdup("/nonexistent/question");
5434
g_assert_nonnull(question_filename);
5435
__attribute__((cleanup(cleanup_string)))
5436
char *const filename = strdup("/nonexistent/socket");
5437
g_assert_nonnull(filename);
5438
__attribute__((cleanup(string_set_clear)))
5439
string_set cancelled_filenames = {};
5440
buffer password = {};
5441
bool password_is_read = true;
5442
__attribute__((cleanup(cleanup_queue)))
5443
task_queue *queue = create_queue();
5444
g_assert_nonnull(queue);
5446
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5447
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5449
__attribute__((cleanup(cleanup_close)))
5450
const int read_socket = socketfds[0];
5451
const int write_socket = socketfds[1];
5452
task_context task = {
5453
.func=send_password_to_socket,
5454
.question_filename=strdup(question_filename),
5455
.filename=strdup(filename),
5458
.password=&password,
5459
.cancelled_filenames=&cancelled_filenames,
5460
.mandos_client_exited=(bool[]){false},
5461
.password_is_read=&password_is_read,
5462
.current_time=(mono_microsecs[]){0},
5464
g_assert_nonnull(task.question_filename);
5466
task.func(task, queue);
5468
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5470
const task_context *const added_task
5471
= find_matching_task(queue, task);
5472
g_assert_nonnull(added_task);
5473
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5474
g_assert_true(password_is_read);
5476
g_assert_cmpint(added_task->fd, >, 0);
5477
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5482
test_send_password_to_socket_password_not_read(__attribute__((unused))
5483
test_fixture *fixture,
5484
__attribute__((unused))
5487
__attribute__((cleanup(cleanup_close)))
5488
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5489
g_assert_cmpint(epoll_fd, >=, 0);
5490
__attribute__((cleanup(cleanup_string)))
5491
char *const question_filename = strdup("/nonexistent/question");
5492
g_assert_nonnull(question_filename);
5493
__attribute__((cleanup(cleanup_string)))
5494
char *const filename = strdup("/nonexistent/socket");
5495
__attribute__((cleanup(string_set_clear)))
5496
string_set cancelled_filenames = {};
5497
buffer password = {};
5498
__attribute__((cleanup(cleanup_queue)))
5499
task_queue *queue = create_queue();
5500
g_assert_nonnull(queue);
5502
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5503
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5505
__attribute__((cleanup(cleanup_close)))
5506
const int read_socket = socketfds[0];
5507
const int write_socket = socketfds[1];
5508
task_context task = {
5509
.func=send_password_to_socket,
5510
.question_filename=strdup(question_filename),
5511
.filename=strdup(filename),
5514
.password=&password,
5515
.cancelled_filenames=&cancelled_filenames,
5516
.mandos_client_exited=(bool[]){false},
5517
.password_is_read=(bool[]){false},
5518
.current_time=(mono_microsecs[]){0},
5520
g_assert_nonnull(task.question_filename);
5522
task.func(task, queue);
5524
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5526
const task_context *const added_task = find_matching_task(queue,
5528
g_assert_nonnull(added_task);
5529
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5530
g_assert_true(queue->next_run == 0);
5532
g_assert_cmpint(added_task->fd, >, 0);
5533
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5538
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5539
test_fixture *fixture,
5540
__attribute__((unused))
5541
gconstpointer user_data){
5542
__attribute__((cleanup(cleanup_close)))
5543
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5544
g_assert_cmpint(epoll_fd, >=, 0);
5545
const char question_filename[] = "/nonexistent/question";
5546
char *const filename = strdup("/nonexistent/socket");
5547
__attribute__((cleanup(string_set_clear)))
5548
string_set cancelled_filenames = {};
5549
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5550
__attribute__((cleanup(cleanup_buffer)))
5552
.data=malloc(oversized),
5554
.allocated=oversized,
5556
g_assert_nonnull(password.data);
5557
if(mlock(password.data, password.allocated) != 0){
5558
g_assert_true(errno == EPERM or errno == ENOMEM);
5560
/* Construct test password buffer */
5561
/* Start with + since that is what the real procotol uses */
5562
password.data[0] = '+';
5563
/* Set a special character at string end just to mark the end */
5564
password.data[oversized-3] = 'y';
5565
/* Set NUL at buffer end, as suggested by the protocol */
5566
password.data[oversized-2] = '\0';
5567
/* Fill rest of password with 'x' */
5568
memset(password.data+1, 'x', oversized-3);
5570
__attribute__((cleanup(cleanup_queue)))
5571
task_queue *queue = create_queue();
5572
g_assert_nonnull(queue);
5574
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5575
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5577
__attribute__((cleanup(cleanup_close)))
5578
const int read_socket = socketfds[0];
5579
__attribute__((cleanup(cleanup_close)))
5580
const int write_socket = socketfds[1];
5581
task_context task = {
5582
.func=send_password_to_socket,
5583
.question_filename=strdup(question_filename),
5587
.password=&password,
5588
.cancelled_filenames=&cancelled_filenames,
5589
.mandos_client_exited=(bool[]){true},
5590
.password_is_read=(bool[]){true},
5591
.current_time=(mono_microsecs[]){0},
5593
g_assert_nonnull(task.question_filename);
5595
run_task_with_stderr_to_dev_null(task, queue);
5597
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5598
g_assert_true(string_set_contains(cancelled_filenames,
5599
question_filename));
5602
static void test_send_password_to_socket_retry(__attribute__((unused))
5603
test_fixture *fixture,
5604
__attribute__((unused))
5607
__attribute__((cleanup(cleanup_close)))
5608
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5609
g_assert_cmpint(epoll_fd, >=, 0);
5610
__attribute__((cleanup(cleanup_string)))
5611
char *const question_filename = strdup("/nonexistent/question");
5612
g_assert_nonnull(question_filename);
5613
__attribute__((cleanup(cleanup_string)))
5614
char *const filename = strdup("/nonexistent/socket");
5615
g_assert_nonnull(filename);
5616
__attribute__((cleanup(string_set_clear)))
5617
string_set cancelled_filenames = {};
5618
__attribute__((cleanup(cleanup_buffer)))
5619
buffer password = {};
5621
__attribute__((cleanup(cleanup_queue)))
5622
task_queue *queue = create_queue();
5623
g_assert_nonnull(queue);
5625
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5626
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5628
__attribute__((cleanup(cleanup_close)))
5629
const int read_socket = socketfds[0];
5630
const int write_socket = socketfds[1];
5631
/* Close the server side socket to force ECONNRESET on client */
5632
g_assert_cmpint(close(read_socket), ==, 0);
5633
task_context task = {
5634
.func=send_password_to_socket,
5635
.question_filename=strdup(question_filename),
5636
.filename=strdup(filename),
5639
.password=&password,
5640
.cancelled_filenames=&cancelled_filenames,
5641
.mandos_client_exited=(bool[]){true},
5642
.password_is_read=(bool[]){true},
5643
.current_time=(mono_microsecs[]){0},
5645
g_assert_nonnull(task.question_filename);
5647
task.func(task, queue);
5649
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5651
const task_context *const added_task = find_matching_task(queue,
5653
g_assert_nonnull(added_task);
5654
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5656
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5661
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
5662
test_fixture *fixture,
5663
__attribute__((unused))
5664
gconstpointer user_data){
5665
__attribute__((cleanup(cleanup_close)))
5666
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5667
__attribute__((cleanup(cleanup_string)))
5668
char *const question_filename = strdup("/nonexistent/question");
5669
g_assert_nonnull(question_filename);
5670
__attribute__((cleanup(cleanup_string)))
5671
char *const filename = strdup("/nonexistent/socket");
5672
g_assert_nonnull(filename);
5673
__attribute__((cleanup(string_set_clear)))
5674
string_set cancelled_filenames = {};
5675
__attribute__((cleanup(cleanup_buffer)))
5676
buffer password = {};
5678
const mono_microsecs current_time = 11;
5679
__attribute__((cleanup(cleanup_queue)))
5680
task_queue *queue = create_queue();
5681
g_assert_nonnull(queue);
5683
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5684
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5686
__attribute__((cleanup(cleanup_close)))
5687
const int read_socket = socketfds[0];
5688
const int write_socket = socketfds[1];
5689
/* Close the server side socket to force ECONNRESET on client */
5690
g_assert_cmpint(close(read_socket), ==, 0);
5691
task_context task = {
5692
.func=send_password_to_socket,
5693
.question_filename=strdup(question_filename),
5694
.filename=strdup(filename),
5697
.password=&password,
5698
.cancelled_filenames=&cancelled_filenames,
5699
.mandos_client_exited=(bool[]){true},
5700
.password_is_read=(bool[]){true},
5701
.current_time=¤t_time,
5703
g_assert_nonnull(task.question_filename);
5705
run_task_with_stderr_to_dev_null(task, queue);
5707
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5709
const task_context *const added_task = find_matching_task(queue,
5711
g_assert_nonnull(added_task);
5712
g_assert_true(queue->next_run == current_time + 1000000);
5713
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5716
static void assert_send_password_to_socket_password(buffer password){
5717
__attribute__((cleanup(cleanup_close)))
5718
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5719
g_assert_cmpint(epoll_fd, >=, 0);
5720
char *const question_filename = strdup("/nonexistent/question");
5721
g_assert_nonnull(question_filename);
5722
char *const filename = strdup("/nonexistent/socket");
5723
g_assert_nonnull(filename);
5724
__attribute__((cleanup(string_set_clear)))
5725
string_set cancelled_filenames = {};
5727
__attribute__((cleanup(cleanup_queue)))
5728
task_queue *queue = create_queue();
5729
g_assert_nonnull(queue);
5731
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5732
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5734
__attribute__((cleanup(cleanup_close)))
5735
const int read_socket = socketfds[0];
5736
const int write_socket = socketfds[1];
5737
task_context task = {
5738
.func=send_password_to_socket,
5739
.question_filename=question_filename,
5743
.password=&password,
5744
.cancelled_filenames=&cancelled_filenames,
5745
.mandos_client_exited=(bool[]){true},
5746
.password_is_read=(bool[]){true},
5747
.current_time=(mono_microsecs[]){0},
5750
char *expected_written_data = malloc(password.length + 2);
5751
g_assert_nonnull(expected_written_data);
5752
expected_written_data[0] = '+';
5753
expected_written_data[password.length + 1] = '\0';
5754
if(password.length > 0){
5755
g_assert_nonnull(password.data);
5756
memcpy(expected_written_data + 1, password.data, password.length);
5759
task.func(task, queue);
5762
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
5763
(int)(password.length + 2));
5764
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5766
g_assert_true(memcmp(expected_written_data, buf,
5767
password.length + 2) == 0);
5769
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
5771
free(expected_written_data);
5775
test_send_password_to_socket_null_password(__attribute__((unused))
5776
test_fixture *fixture,
5777
__attribute__((unused))
5778
gconstpointer user_data){
5779
__attribute__((cleanup(cleanup_buffer)))
5780
buffer password = {};
5781
assert_send_password_to_socket_password(password);
5785
test_send_password_to_socket_empty_password(__attribute__((unused))
5786
test_fixture *fixture,
5787
__attribute__((unused))
5788
gconstpointer user_data){
5789
__attribute__((cleanup(cleanup_buffer)))
5791
.data=malloc(1), /* because malloc(0) may return NULL */
5793
.allocated=0, /* deliberate lie */
5795
g_assert_nonnull(password.data);
5796
assert_send_password_to_socket_password(password);
5800
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
5801
test_fixture *fixture,
5802
__attribute__((unused))
5803
gconstpointer user_data){
5804
__attribute__((cleanup(cleanup_buffer)))
5810
if(mlock(password.data, password.allocated) != 0){
5811
g_assert_true(errno == EPERM or errno == ENOMEM);
5813
assert_send_password_to_socket_password(password);
5817
test_send_password_to_socket_text_password(__attribute__((unused))
5818
test_fixture *fixture,
5819
__attribute__((unused))
5820
gconstpointer user_data){
5821
const char dummy_test_password[] = "dummy test password";
5822
__attribute__((cleanup(cleanup_buffer)))
5824
.data = strdup(dummy_test_password),
5825
.length = strlen(dummy_test_password),
5826
.allocated = sizeof(dummy_test_password),
5828
if(mlock(password.data, password.allocated) != 0){
5829
g_assert_true(errno == EPERM or errno == ENOMEM);
5831
assert_send_password_to_socket_password(password);
5835
test_send_password_to_socket_binary_password(__attribute__((unused))
5836
test_fixture *fixture,
5837
__attribute__((unused))
5838
gconstpointer user_data){
5839
__attribute__((cleanup(cleanup_buffer)))
5845
g_assert_nonnull(password.data);
5846
if(mlock(password.data, password.allocated) != 0){
5847
g_assert_true(errno == EPERM or errno == ENOMEM);
5849
char c = 1; /* Start at 1, avoiding NUL */
5850
for(int i=0; i < 255; i++){
5851
password.data[i] = c++;
5853
assert_send_password_to_socket_password(password);
5857
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
5858
test_fixture *fixture,
5859
__attribute__((unused))
5862
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
5863
__attribute__((cleanup(cleanup_buffer)))
5865
.data=malloc(sizeof(test_password)),
5866
.length=sizeof(test_password),
5867
.allocated=sizeof(test_password),
5869
g_assert_nonnull(password.data);
5870
if(mlock(password.data, password.allocated) !=0){
5871
g_assert_true(errno == EPERM or errno == ENOMEM);
5873
memcpy(password.data, test_password, password.allocated);
5874
assert_send_password_to_socket_password(password);
5877
static bool assert_add_existing_questions_to_devnull(task_queue
5890
static void test_add_existing_questions_ENOENT(__attribute__((unused))
5891
test_fixture *fixture,
5892
__attribute__((unused))
5895
__attribute__((cleanup(cleanup_queue)))
5896
task_queue *queue = create_queue();
5897
g_assert_nonnull(queue);
5898
__attribute__((cleanup(cleanup_close)))
5899
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5900
g_assert_cmpint(epoll_fd, >=, 0);
5901
__attribute__((cleanup(string_set_clear)))
5902
string_set cancelled_filenames = {};
5904
g_assert_false(assert_add_existing_questions_to_devnull
5907
(buffer[]){{}}, /* password */
5908
&cancelled_filenames,
5909
(mono_microsecs[]){0}, /* current_time */
5910
(bool[]){false}, /* mandos_client_exited */
5911
(bool[]){false}, /* password_is_read */
5912
"/nonexistent")); /* dirname */
5914
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5918
bool assert_add_existing_questions_to_devnull(task_queue
5925
*cancelled_filenames,
5926
const mono_microsecs
5927
*const current_time,
5929
mandos_client_exited,
5934
__attribute__((cleanup(cleanup_close)))
5935
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5936
g_assert_cmpint(devnull_fd, >=, 0);
5937
__attribute__((cleanup(cleanup_close)))
5938
const int real_stderr_fd = dup(STDERR_FILENO);
5939
g_assert_cmpint(real_stderr_fd, >=, 0);
5940
dup2(devnull_fd, STDERR_FILENO);
5941
const bool ret = add_existing_questions(queue, epoll_fd, password,
5942
cancelled_filenames,
5944
mandos_client_exited,
5945
password_is_read, dirname);
5946
dup2(real_stderr_fd, STDERR_FILENO);
5951
void test_add_existing_questions_no_questions(__attribute__((unused))
5952
test_fixture *fixture,
5953
__attribute__((unused))
5956
__attribute__((cleanup(cleanup_queue)))
5957
task_queue *queue = create_queue();
5958
g_assert_nonnull(queue);
5959
__attribute__((cleanup(cleanup_close)))
5960
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5961
g_assert_cmpint(epoll_fd, >=, 0);
5962
__attribute__((cleanup(string_set_clear)))
5963
string_set cancelled_filenames = {};
5964
__attribute__((cleanup(cleanup_string)))
5965
char *tempdir = make_temporary_directory();
5966
g_assert_nonnull(tempdir);
5968
g_assert_false(assert_add_existing_questions_to_devnull
5971
(buffer[]){{}}, /* password */
5972
&cancelled_filenames,
5973
(mono_microsecs[]){0}, /* current_time */
5974
(bool[]){false}, /* mandos_client_exited */
5975
(bool[]){false}, /* password_is_read */
5978
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5980
g_assert_cmpint(rmdir(tempdir), ==, 0);
5983
static char *make_question_file_in_directory(const char *const);
5986
void test_add_existing_questions_one_question(__attribute__((unused))
5987
test_fixture *fixture,
5988
__attribute__((unused))
5991
__attribute__((cleanup(cleanup_queue)))
5992
task_queue *queue = create_queue();
5993
g_assert_nonnull(queue);
5994
__attribute__((cleanup(cleanup_close)))
5995
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5996
g_assert_cmpint(epoll_fd, >=, 0);
5997
__attribute__((cleanup(cleanup_buffer)))
5998
buffer password = {};
5999
__attribute__((cleanup(string_set_clear)))
6000
string_set cancelled_filenames = {};
6001
const mono_microsecs current_time = 0;
6002
bool mandos_client_exited = false;
6003
bool password_is_read = false;
6004
__attribute__((cleanup(cleanup_string)))
6005
char *tempdir = make_temporary_directory();
6006
g_assert_nonnull(tempdir);
6007
__attribute__((cleanup(cleanup_string)))
6008
char *question_filename
6009
= make_question_file_in_directory(tempdir);
6010
g_assert_nonnull(question_filename);
6012
g_assert_true(assert_add_existing_questions_to_devnull
6016
&cancelled_filenames,
6018
&mandos_client_exited,
6022
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6024
g_assert_nonnull(find_matching_task(queue, (task_context){
6025
.func=open_and_parse_question,
6027
.filename=question_filename,
6028
.question_filename=question_filename,
6029
.password=&password,
6030
.cancelled_filenames=&cancelled_filenames,
6031
.current_time=¤t_time,
6032
.mandos_client_exited=&mandos_client_exited,
6033
.password_is_read=&password_is_read,
6036
g_assert_true(queue->next_run == 1);
6038
g_assert_cmpint(unlink(question_filename), ==, 0);
6039
g_assert_cmpint(rmdir(tempdir), ==, 0);
6042
static char *make_question_file_in_directory(const char
6044
return make_temporary_prefixed_file_in_directory("ask.", dir);
6048
void test_add_existing_questions_two_questions(__attribute__((unused))
6049
test_fixture *fixture,
6050
__attribute__((unused))
6053
__attribute__((cleanup(cleanup_queue)))
6054
task_queue *queue = create_queue();
6055
g_assert_nonnull(queue);
6056
__attribute__((cleanup(cleanup_close)))
6057
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6058
g_assert_cmpint(epoll_fd, >=, 0);
6059
__attribute__((cleanup(cleanup_buffer)))
6060
buffer password = {};
6061
__attribute__((cleanup(string_set_clear)))
6062
string_set cancelled_filenames = {};
6063
const mono_microsecs current_time = 0;
6064
bool mandos_client_exited = false;
6065
bool password_is_read = false;
6066
__attribute__((cleanup(cleanup_string)))
6067
char *tempdir = make_temporary_directory();
6068
g_assert_nonnull(tempdir);
6069
__attribute__((cleanup(cleanup_string)))
6070
char *question_filename1
6071
= make_question_file_in_directory(tempdir);
6072
g_assert_nonnull(question_filename1);
6073
__attribute__((cleanup(cleanup_string)))
6074
char *question_filename2
6075
= make_question_file_in_directory(tempdir);
6076
g_assert_nonnull(question_filename2);
6078
g_assert_true(assert_add_existing_questions_to_devnull
6082
&cancelled_filenames,
6084
&mandos_client_exited,
6088
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6090
g_assert_true(queue->next_run == 1);
6092
__attribute__((cleanup(string_set_clear)))
6093
string_set seen_questions = {};
6095
bool queue_contains_question_opener(char *const question_filename){
6096
return(find_matching_task(queue, (task_context){
6097
.func=open_and_parse_question,
6099
.question_filename=question_filename,
6100
.password=&password,
6101
.cancelled_filenames=&cancelled_filenames,
6102
.current_time=¤t_time,
6103
.mandos_client_exited=&mandos_client_exited,
6104
.password_is_read=&password_is_read,
6108
g_assert_true(queue_contains_question_opener(question_filename1));
6109
g_assert_true(queue_contains_question_opener(question_filename2));
6111
g_assert_true(queue->next_run == 1);
6113
g_assert_cmpint(unlink(question_filename1), ==, 0);
6114
g_assert_cmpint(unlink(question_filename2), ==, 0);
6115
g_assert_cmpint(rmdir(tempdir), ==, 0);
6119
test_add_existing_questions_non_questions(__attribute__((unused))
6120
test_fixture *fixture,
6121
__attribute__((unused))
6122
gconstpointer user_data){
6123
__attribute__((cleanup(cleanup_queue)))
6124
task_queue *queue = create_queue();
6125
g_assert_nonnull(queue);
6126
__attribute__((cleanup(cleanup_close)))
6127
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6128
g_assert_cmpint(epoll_fd, >=, 0);
6129
__attribute__((cleanup(string_set_clear)))
6130
string_set cancelled_filenames = {};
6131
__attribute__((cleanup(cleanup_string)))
6132
char *tempdir = make_temporary_directory();
6133
g_assert_nonnull(tempdir);
6134
__attribute__((cleanup(cleanup_string)))
6135
char *question_filename1
6136
= make_temporary_file_in_directory(tempdir);
6137
g_assert_nonnull(question_filename1);
6138
__attribute__((cleanup(cleanup_string)))
6139
char *question_filename2
6140
= make_temporary_file_in_directory(tempdir);
6141
g_assert_nonnull(question_filename2);
6143
g_assert_false(assert_add_existing_questions_to_devnull
6146
(buffer[]){{}}, /* password */
6147
&cancelled_filenames,
6148
(mono_microsecs[]){0}, /* current_time */
6149
(bool[]){false}, /* mandos_client_exited */
6150
(bool[]){false}, /* password_is_read */
6153
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6155
g_assert_cmpint(unlink(question_filename1), ==, 0);
6156
g_assert_cmpint(unlink(question_filename2), ==, 0);
6157
g_assert_cmpint(rmdir(tempdir), ==, 0);
6161
test_add_existing_questions_both_types(__attribute__((unused))
6162
test_fixture *fixture,
6163
__attribute__((unused))
6164
gconstpointer user_data){
6165
__attribute__((cleanup(cleanup_queue)))
6166
task_queue *queue = create_queue();
6167
g_assert_nonnull(queue);
6168
__attribute__((cleanup(cleanup_close)))
6169
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6170
g_assert_cmpint(epoll_fd, >=, 0);
6171
__attribute__((cleanup(cleanup_buffer)))
6172
buffer password = {};
6173
__attribute__((cleanup(string_set_clear)))
6174
string_set cancelled_filenames = {};
6175
const mono_microsecs current_time = 0;
6176
bool mandos_client_exited = false;
6177
bool password_is_read = false;
6178
__attribute__((cleanup(cleanup_string)))
6179
char *tempdir = make_temporary_directory();
6180
g_assert_nonnull(tempdir);
6181
__attribute__((cleanup(cleanup_string)))
6182
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6183
g_assert_nonnull(tempfilename1);
6184
__attribute__((cleanup(cleanup_string)))
6185
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6186
g_assert_nonnull(tempfilename2);
6187
__attribute__((cleanup(cleanup_string)))
6188
char *question_filename
6189
= make_question_file_in_directory(tempdir);
6190
g_assert_nonnull(question_filename);
6192
g_assert_true(assert_add_existing_questions_to_devnull
6196
&cancelled_filenames,
6198
&mandos_client_exited,
6202
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6204
g_assert_nonnull(find_matching_task(queue, (task_context){
6205
.func=open_and_parse_question,
6207
.filename=question_filename,
6208
.question_filename=question_filename,
6209
.password=&password,
6210
.cancelled_filenames=&cancelled_filenames,
6211
.current_time=¤t_time,
6212
.mandos_client_exited=&mandos_client_exited,
6213
.password_is_read=&password_is_read,
6216
g_assert_true(queue->next_run == 1);
6218
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6219
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6220
g_assert_cmpint(unlink(question_filename), ==, 0);
6221
g_assert_cmpint(rmdir(tempdir), ==, 0);
6224
static void test_wait_for_event_timeout(__attribute__((unused))
6225
test_fixture *fixture,
6226
__attribute__((unused))
6227
gconstpointer user_data){
6228
__attribute__((cleanup(cleanup_close)))
6229
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6230
g_assert_cmpint(epoll_fd, >=, 0);
6232
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6235
static void test_wait_for_event_event(__attribute__((unused))
6236
test_fixture *fixture,
6237
__attribute__((unused))
6238
gconstpointer user_data){
6239
__attribute__((cleanup(cleanup_close)))
6240
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6241
g_assert_cmpint(epoll_fd, >=, 0);
6243
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6244
__attribute__((cleanup(cleanup_close)))
6245
const int read_pipe = pipefds[0];
6246
__attribute__((cleanup(cleanup_close)))
6247
const int write_pipe = pipefds[1];
6248
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6249
&(struct epoll_event)
6250
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6251
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6253
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6256
static void test_wait_for_event_sigchld(test_fixture *fixture,
6257
__attribute__((unused))
6258
gconstpointer user_data){
6259
const pid_t pid = fork();
6260
if(pid == 0){ /* Child */
6261
if(not restore_signal_handler(&fixture->orig_sigaction)){
6262
_exit(EXIT_FAILURE);
6264
if(not restore_sigmask(&fixture->orig_sigmask)){
6265
_exit(EXIT_FAILURE);
6269
g_assert_true(pid != -1);
6270
__attribute__((cleanup(cleanup_close)))
6271
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6272
g_assert_cmpint(epoll_fd, >=, 0);
6274
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6277
g_assert_true(waitpid(pid, &status, 0) == pid);
6278
g_assert_true(WIFEXITED(status));
6279
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6282
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6283
test_fixture *fixture,
6284
__attribute__((unused))
6285
gconstpointer user_data){
6286
__attribute__((cleanup(cleanup_queue)))
6287
task_queue *queue = create_queue();
6288
g_assert_nonnull(queue);
6289
queue->next_run = 1;
6290
__attribute__((cleanup(cleanup_close)))
6291
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6292
__attribute__((cleanup(string_set_clear)))
6293
string_set cancelled_filenames = {};
6294
bool quit_now = false;
6296
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6297
g_assert_false(quit_now);
6298
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6302
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6303
test_fixture *fixture,
6304
__attribute__((unused))
6307
__attribute__((cleanup(cleanup_queue)))
6308
task_queue *queue = create_queue();
6309
g_assert_nonnull(queue);
6310
__attribute__((cleanup(string_set_clear)))
6311
string_set cancelled_filenames = {};
6312
bool quit_now = false;
6313
const char question_filename[] = "/nonexistent/question_filename";
6314
g_assert_true(string_set_add(&cancelled_filenames,
6315
question_filename));
6317
g_assert_true(add_to_queue(queue,
6318
(task_context){ .func=dummy_func }));
6320
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6321
g_assert_false(quit_now);
6322
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6323
g_assert_false(string_set_contains(cancelled_filenames,
6324
question_filename));
6328
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6329
test_fixture *fixture,
6330
__attribute__((unused))
6333
__attribute__((cleanup(cleanup_queue)))
6334
task_queue *queue = create_queue();
6335
g_assert_nonnull(queue);
6336
__attribute__((cleanup(string_set_clear)))
6337
string_set cancelled_filenames = {};
6338
bool quit_now = false;
6340
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6341
__attribute__((cleanup(cleanup_close)))
6342
const int read_pipe = pipefds[0];
6343
g_assert_cmpint(close(pipefds[1]), ==, 0);
6344
const char question_filename[] = "/nonexistent/question_filename";
6345
g_assert_true(string_set_add(&cancelled_filenames,
6346
question_filename));
6347
__attribute__((nonnull))
6348
void quit_func(const task_context task,
6349
__attribute__((unused)) task_queue *const q){
6350
g_assert_nonnull(task.quit_now);
6351
*task.quit_now = true;
6353
task_context task = {
6355
.question_filename=strdup(question_filename),
6356
.quit_now=&quit_now,
6359
g_assert_nonnull(task.question_filename);
6361
g_assert_true(add_to_queue(queue, task));
6363
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6364
g_assert_false(quit_now);
6366
/* read_pipe should be closed already */
6368
bool read_pipe_closed = (close(read_pipe) == -1);
6369
read_pipe_closed &= (errno == EBADF);
6370
g_assert_true(read_pipe_closed);
6373
static void test_run_queue_one_task(__attribute__((unused))
6374
test_fixture *fixture,
6375
__attribute__((unused))
6376
gconstpointer user_data){
6377
__attribute__((cleanup(cleanup_queue)))
6378
task_queue *queue = create_queue();
6379
g_assert_nonnull(queue);
6380
__attribute__((cleanup(string_set_clear)))
6381
string_set cancelled_filenames = {};
6382
bool quit_now = false;
6384
__attribute__((nonnull))
6385
void next_run_func(__attribute__((unused))
6386
const task_context task,
6387
task_queue *const q){
6391
task_context task = {
6392
.func=next_run_func,
6394
g_assert_true(add_to_queue(queue, task));
6396
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6397
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6398
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6401
static void test_run_queue_two_tasks(__attribute__((unused))
6402
test_fixture *fixture,
6403
__attribute__((unused))
6404
gconstpointer user_data){
6405
__attribute__((cleanup(cleanup_queue)))
6406
task_queue *queue = create_queue();
6407
g_assert_nonnull(queue);
6408
queue->next_run = 1;
6409
__attribute__((cleanup(string_set_clear)))
6410
string_set cancelled_filenames = {};
6411
bool quit_now = false;
6412
bool mandos_client_exited = false;
6414
__attribute__((nonnull))
6415
void next_run_func(__attribute__((unused))
6416
const task_context task,
6417
task_queue *const q){
6421
__attribute__((nonnull))
6422
void exited_func(const task_context task,
6423
__attribute__((unused)) task_queue *const q){
6424
*task.mandos_client_exited = true;
6427
task_context task1 = {
6428
.func=next_run_func,
6430
g_assert_true(add_to_queue(queue, task1));
6432
task_context task2 = {
6434
.mandos_client_exited=&mandos_client_exited,
6436
g_assert_true(add_to_queue(queue, task2));
6438
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6439
g_assert_false(quit_now);
6440
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6441
g_assert_true(mandos_client_exited);
6442
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6445
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6446
test_fixture *fixture,
6447
__attribute__((unused))
6448
gconstpointer user_data){
6449
__attribute__((cleanup(cleanup_queue)))
6450
task_queue *queue = create_queue();
6451
g_assert_nonnull(queue);
6452
__attribute__((cleanup(string_set_clear)))
6453
string_set cancelled_filenames = {};
6454
bool quit_now = false;
6455
bool mandos_client_exited = false;
6456
bool password_is_read = false;
6458
__attribute__((nonnull))
6459
void set_exited_func(const task_context task,
6460
__attribute__((unused)) task_queue *const q){
6461
*task.mandos_client_exited = true;
6462
*task.quit_now = true;
6464
task_context task1 = {
6465
.func=set_exited_func,
6466
.quit_now=&quit_now,
6467
.mandos_client_exited=&mandos_client_exited,
6469
g_assert_true(add_to_queue(queue, task1));
6471
__attribute__((nonnull))
6472
void set_read_func(const task_context task,
6473
__attribute__((unused)) task_queue *const q){
6474
*task.quit_now = true;
6475
*task.password_is_read = true;
6477
task_context task2 = {
6478
.func=set_read_func,
6479
.quit_now=&quit_now,
6480
.password_is_read=&password_is_read,
6482
g_assert_true(add_to_queue(queue, task2));
6484
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6485
g_assert_true(quit_now);
6486
g_assert_true(mandos_client_exited xor password_is_read);
6487
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6490
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6491
test_fixture *fixture,
6492
__attribute__((unused))
6493
gconstpointer user_data){
6494
__attribute__((cleanup(cleanup_queue)))
6495
task_queue *queue = create_queue();
6496
g_assert_nonnull(queue);
6497
__attribute__((cleanup(string_set_clear)))
6498
string_set cancelled_filenames = {};
6500
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6501
__attribute__((cleanup(cleanup_close)))
6502
const int read_pipe = pipefds[0];
6503
__attribute__((cleanup(cleanup_close)))
6504
const int write_pipe = pipefds[1];
6505
bool quit_now = false;
6507
__attribute__((nonnull))
6508
void read_func(const task_context task,
6509
__attribute__((unused)) task_queue *const q){
6510
*task.quit_now = true;
6512
task_context task1 = {
6514
.quit_now=&quit_now,
6517
g_assert_true(add_to_queue(queue, task1));
6519
__attribute__((nonnull))
6520
void write_func(const task_context task,
6521
__attribute__((unused)) task_queue *const q){
6522
*task.quit_now = true;
6524
task_context task2 = {
6526
.quit_now=&quit_now,
6529
g_assert_true(add_to_queue(queue, task2));
6531
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6532
g_assert_true(quit_now);
6534
/* Either read_pipe or write_pipe should be closed already */
6536
bool close_read_pipe = (close(read_pipe) == -1);
6537
close_read_pipe &= (errno == EBADF);
6539
bool close_write_pipe = (close(write_pipe) == -1);
6540
close_write_pipe &= (errno == EBADF);
6541
g_assert_true(close_read_pipe xor close_write_pipe);
6542
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6545
static void test_setup_signal_handler(__attribute__((unused))
6546
test_fixture *fixture,
6547
__attribute__((unused))
6548
gconstpointer user_data){
6549
/* Save current SIGCHLD action, whatever it is */
6550
struct sigaction expected_sigchld_action;
6551
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6554
/* Act; i.e. run the setup_signal_handler() function */
6555
struct sigaction actual_old_sigchld_action;
6556
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6558
/* Check that the function correctly set "actual_old_sigchld_action"
6559
to the same values as the previously saved
6560
"expected_sigchld_action" */
6561
/* Check member sa_handler */
6562
g_assert_true(actual_old_sigchld_action.sa_handler
6563
== expected_sigchld_action.sa_handler);
6564
/* Check member sa_mask */
6565
for(int signum = 1; signum < NSIG; signum++){
6566
const int expected_old_block_state
6567
= sigismember(&expected_sigchld_action.sa_mask, signum);
6568
g_assert_cmpint(expected_old_block_state, >=, 0);
6569
const int actual_old_block_state
6570
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6571
g_assert_cmpint(actual_old_block_state, >=, 0);
6572
g_assert_cmpint(actual_old_block_state,
6573
==, expected_old_block_state);
6575
/* Check member sa_flags */
6576
g_assert_true((actual_old_sigchld_action.sa_flags
6577
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6578
== (expected_sigchld_action.sa_flags
6579
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6581
/* Retrieve the current signal handler for SIGCHLD as set by
6582
setup_signal_handler() */
6583
struct sigaction actual_new_sigchld_action;
6584
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6585
&actual_new_sigchld_action), ==, 0);
6586
/* Check that the signal handler (member sa_handler) is correctly
6587
set to the "handle_sigchld" function */
6588
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6589
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6590
g_assert_true(actual_new_sigchld_action.sa_handler
6592
/* Check (in member sa_mask) that at least a handful of signals are
6593
actually blocked during the signal handler */
6594
for(int signum = 1; signum < NSIG; signum++){
6595
int actual_new_block_state;
6601
actual_new_block_state
6602
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6603
g_assert_cmpint(actual_new_block_state, ==, 1);
6605
case SIGKILL: /* non-blockable */
6606
case SIGSTOP: /* non-blockable */
6607
case SIGCHLD: /* always blocked */
6612
/* Check member sa_flags */
6613
g_assert_true((actual_new_sigchld_action.sa_flags
6614
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6615
== (SA_NOCLDSTOP | SA_RESTART));
6617
/* Restore signal handler */
6618
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6622
static void test_restore_signal_handler(__attribute__((unused))
6623
test_fixture *fixture,
6624
__attribute__((unused))
6625
gconstpointer user_data){
6626
/* Save current SIGCHLD action, whatever it is */
6627
struct sigaction expected_sigchld_action;
6628
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6630
/* Since we haven't established a signal handler yet, there should
6631
not be one established. But another test may have relied on
6632
restore_signal_handler() to restore the signal handler, and if
6633
restore_signal_handler() is buggy (which we should be prepared
6634
for in this test) the signal handler may not have been restored
6635
properly; check for this: */
6636
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
6638
/* Establish a signal handler */
6639
struct sigaction sigchld_action = {
6640
.sa_handler=handle_sigchld,
6641
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
6643
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
6644
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
6646
/* Act; i.e. run the restore_signal_handler() function */
6647
g_assert_true(restore_signal_handler(&expected_sigchld_action));
6649
/* Retrieve the restored signal handler data */
6650
struct sigaction actual_restored_sigchld_action;
6651
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6652
&actual_restored_sigchld_action), ==, 0);
6654
/* Check that the function correctly restored the signal action, as
6655
saved in "actual_restored_sigchld_action", to the same values as
6656
the previously saved "expected_sigchld_action" */
6657
/* Check member sa_handler */
6658
g_assert_true(actual_restored_sigchld_action.sa_handler
6659
== expected_sigchld_action.sa_handler);
6660
/* Check member sa_mask */
6661
for(int signum = 1; signum < NSIG; signum++){
6662
const int expected_old_block_state
6663
= sigismember(&expected_sigchld_action.sa_mask, signum);
6664
g_assert_cmpint(expected_old_block_state, >=, 0);
6665
const int actual_restored_block_state
6666
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
6667
g_assert_cmpint(actual_restored_block_state, >=, 0);
6668
g_assert_cmpint(actual_restored_block_state,
6669
==, expected_old_block_state);
6671
/* Check member sa_flags */
6672
g_assert_true((actual_restored_sigchld_action.sa_flags
6673
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6674
== (expected_sigchld_action.sa_flags
6675
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6678
static void test_block_sigchld(__attribute__((unused))
6679
test_fixture *fixture,
6680
__attribute__((unused))
6681
gconstpointer user_data){
6682
/* Save original signal mask */
6683
sigset_t expected_sigmask;
6684
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
6687
/* Make sure SIGCHLD is unblocked for this test */
6688
sigset_t sigchld_sigmask;
6689
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6690
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6691
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
6694
/* Act; i.e. run the block_sigchld() function */
6695
sigset_t actual_old_sigmask;
6696
g_assert_true(block_sigchld(&actual_old_sigmask));
6698
/* Check the actual_old_sigmask; it should be the same as the
6699
previously saved signal mask "expected_sigmask". */
6700
for(int signum = 1; signum < NSIG; signum++){
6701
const int expected_old_block_state
6702
= sigismember(&expected_sigmask, signum);
6703
g_assert_cmpint(expected_old_block_state, >=, 0);
6704
const int actual_old_block_state
6705
= sigismember(&actual_old_sigmask, signum);
6706
g_assert_cmpint(actual_old_block_state, >=, 0);
6707
g_assert_cmpint(actual_old_block_state,
6708
==, expected_old_block_state);
6711
/* Retrieve the newly set signal mask */
6712
sigset_t actual_sigmask;
6713
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
6715
/* SIGCHLD should be blocked */
6716
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
6718
/* Restore signal mask */
6719
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
6723
static void test_restore_sigmask(__attribute__((unused))
6724
test_fixture *fixture,
6725
__attribute__((unused))
6726
gconstpointer user_data){
6727
/* Save original signal mask */
6728
sigset_t orig_sigmask;
6729
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
6731
/* Make sure SIGCHLD is blocked for this test */
6732
sigset_t sigchld_sigmask;
6733
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6734
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6735
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
6738
/* Act; i.e. run the restore_sigmask() function */
6739
g_assert_true(restore_sigmask(&orig_sigmask));
6741
/* Retrieve the newly restored signal mask */
6742
sigset_t restored_sigmask;
6743
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
6746
/* Check the restored_sigmask; it should be the same as the
6747
previously saved signal mask "orig_sigmask". */
6748
for(int signum = 1; signum < NSIG; signum++){
6749
const int orig_block_state = sigismember(&orig_sigmask, signum);
6750
g_assert_cmpint(orig_block_state, >=, 0);
6751
const int restored_block_state = sigismember(&restored_sigmask,
6753
g_assert_cmpint(restored_block_state, >=, 0);
6754
g_assert_cmpint(restored_block_state, ==, orig_block_state);
6757
/* Restore signal mask */
6758
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
6762
static void test_parse_arguments_noargs(__attribute__((unused))
6763
test_fixture *fixture,
6764
__attribute__((unused))
6765
gconstpointer user_data){
6769
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6771
char *agent_directory = NULL;
6772
char *helper_directory = NULL;
6775
char *mandos_argz = NULL;
6776
size_t mandos_argz_length = 0;
6778
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6779
&helper_directory, &user, &group,
6780
&mandos_argz, &mandos_argz_length));
6781
g_assert_null(agent_directory);
6782
g_assert_null(helper_directory);
6783
g_assert_true(user == 0);
6784
g_assert_true(group == 0);
6785
g_assert_null(mandos_argz);
6786
g_assert_true(mandos_argz_length == 0);
6788
for(char **arg = argv; *arg != NULL; arg++){
6793
__attribute__((nonnull))
6794
static bool parse_arguments_devnull(int argc, char *argv[],
6795
const bool exit_failure,
6796
char **agent_directory,
6797
char **helper_directory,
6801
size_t *mandos_argz_length){
6803
FILE *real_stderr = stderr;
6804
FILE *devnull = fopen("/dev/null", "we");
6805
g_assert_nonnull(devnull);
6808
const bool ret = parse_arguments(argc, argv, exit_failure,
6810
helper_directory, user, group,
6811
mandos_argz, mandos_argz_length);
6812
const error_t saved_errno = errno;
6814
stderr = real_stderr;
6815
g_assert_cmpint(fclose(devnull), ==, 0);
6817
errno = saved_errno;
6822
static void test_parse_arguments_invalid(__attribute__((unused))
6823
test_fixture *fixture,
6824
__attribute__((unused))
6825
gconstpointer user_data){
6828
strdup("--invalid"),
6830
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6832
char *agent_directory = NULL;
6833
char *helper_directory = NULL;
6836
char *mandos_argz = NULL;
6837
size_t mandos_argz_length = 0;
6839
g_assert_false(parse_arguments_devnull(argc, argv, false,
6841
&helper_directory, &user,
6842
&group, &mandos_argz,
6843
&mandos_argz_length));
6845
g_assert_true(errno == EINVAL);
6846
g_assert_null(agent_directory);
6847
g_assert_null(helper_directory);
6848
g_assert_null(mandos_argz);
6849
g_assert_true(mandos_argz_length == 0);
6851
for(char **arg = argv; *arg != NULL; arg++){
6856
static void test_parse_arguments_long_dir(__attribute__((unused))
6857
test_fixture *fixture,
6858
__attribute__((unused))
6859
gconstpointer user_data){
6862
strdup("--agent-directory"),
6865
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6867
__attribute__((cleanup(cleanup_string)))
6868
char *agent_directory = NULL;
6869
char *helper_directory = NULL;
6872
__attribute__((cleanup(cleanup_string)))
6873
char *mandos_argz = NULL;
6874
size_t mandos_argz_length = 0;
6876
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6877
&helper_directory, &user, &group,
6878
&mandos_argz, &mandos_argz_length));
6880
g_assert_cmpstr(agent_directory, ==, "/tmp");
6881
g_assert_null(helper_directory);
6882
g_assert_true(user == 0);
6883
g_assert_true(group == 0);
6884
g_assert_null(mandos_argz);
6885
g_assert_true(mandos_argz_length == 0);
6887
for(char **arg = argv; *arg != NULL; arg++){
6892
static void test_parse_arguments_short_dir(__attribute__((unused))
6893
test_fixture *fixture,
6894
__attribute__((unused))
6895
gconstpointer user_data){
6901
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6903
__attribute__((cleanup(cleanup_string)))
6904
char *agent_directory = NULL;
6905
char *helper_directory = NULL;
6908
__attribute__((cleanup(cleanup_string)))
6909
char *mandos_argz = NULL;
6910
size_t mandos_argz_length = 0;
6912
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6913
&helper_directory, &user, &group,
6914
&mandos_argz, &mandos_argz_length));
6916
g_assert_cmpstr(agent_directory, ==, "/tmp");
6917
g_assert_null(helper_directory);
6918
g_assert_true(user == 0);
6919
g_assert_true(group == 0);
6920
g_assert_null(mandos_argz);
6921
g_assert_true(mandos_argz_length == 0);
6923
for(char **arg = argv; *arg != NULL; arg++){
6929
void test_parse_arguments_helper_directory(__attribute__((unused))
6930
test_fixture *fixture,
6931
__attribute__((unused))
6932
gconstpointer user_data){
6935
strdup("--helper-directory"),
6938
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6940
char *agent_directory = NULL;
6941
__attribute__((cleanup(cleanup_string)))
6942
char *helper_directory = NULL;
6945
__attribute__((cleanup(cleanup_string)))
6946
char *mandos_argz = NULL;
6947
size_t mandos_argz_length = 0;
6949
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6950
&helper_directory, &user, &group,
6951
&mandos_argz, &mandos_argz_length));
6953
g_assert_cmpstr(helper_directory, ==, "/tmp");
6954
g_assert_null(agent_directory);
6955
g_assert_true(user == 0);
6956
g_assert_true(group == 0);
6957
g_assert_null(mandos_argz);
6958
g_assert_true(mandos_argz_length == 0);
6960
for(char **arg = argv; *arg != NULL; arg++){
6966
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
6967
test_fixture *fixture,
6968
__attribute__((unused))
6969
gconstpointer user_data){
6972
strdup("--plugin-helper-dir"),
6975
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6977
char *agent_directory = NULL;
6978
__attribute__((cleanup(cleanup_string)))
6979
char *helper_directory = NULL;
6982
__attribute__((cleanup(cleanup_string)))
6983
char *mandos_argz = NULL;
6984
size_t mandos_argz_length = 0;
6986
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6987
&helper_directory, &user, &group,
6988
&mandos_argz, &mandos_argz_length));
6990
g_assert_cmpstr(helper_directory, ==, "/tmp");
6991
g_assert_null(agent_directory);
6992
g_assert_true(user == 0);
6993
g_assert_true(group == 0);
6994
g_assert_null(mandos_argz);
6995
g_assert_true(mandos_argz_length == 0);
6997
for(char **arg = argv; *arg != NULL; arg++){
7002
static void test_parse_arguments_user(__attribute__((unused))
7003
test_fixture *fixture,
7004
__attribute__((unused))
7005
gconstpointer user_data){
7011
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7013
char *agent_directory = NULL;
7014
__attribute__((cleanup(cleanup_string)))
7015
char *helper_directory = NULL;
7018
__attribute__((cleanup(cleanup_string)))
7019
char *mandos_argz = NULL;
7020
size_t mandos_argz_length = 0;
7022
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7023
&helper_directory, &user, &group,
7024
&mandos_argz, &mandos_argz_length));
7026
g_assert_null(helper_directory);
7027
g_assert_null(agent_directory);
7028
g_assert_cmpuint((unsigned int)user, ==, 1000);
7029
g_assert_true(group == 0);
7030
g_assert_null(mandos_argz);
7031
g_assert_true(mandos_argz_length == 0);
7033
for(char **arg = argv; *arg != NULL; arg++){
7038
static void test_parse_arguments_user_invalid(__attribute__((unused))
7039
test_fixture *fixture,
7040
__attribute__((unused))
7048
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7050
char *agent_directory = NULL;
7051
__attribute__((cleanup(cleanup_string)))
7052
char *helper_directory = NULL;
7055
__attribute__((cleanup(cleanup_string)))
7056
char *mandos_argz = NULL;
7057
size_t mandos_argz_length = 0;
7059
g_assert_false(parse_arguments_devnull(argc, argv, false,
7061
&helper_directory, &user,
7062
&group, &mandos_argz,
7063
&mandos_argz_length));
7065
g_assert_null(helper_directory);
7066
g_assert_null(agent_directory);
7067
g_assert_cmpuint((unsigned int)user, ==, 0);
7068
g_assert_true(group == 0);
7069
g_assert_null(mandos_argz);
7070
g_assert_true(mandos_argz_length == 0);
7072
for(char **arg = argv; *arg != NULL; arg++){
7078
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7079
test_fixture *fixture,
7080
__attribute__((unused))
7081
gconstpointer user_data){
7087
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7089
char *agent_directory = NULL;
7090
__attribute__((cleanup(cleanup_string)))
7091
char *helper_directory = NULL;
7094
__attribute__((cleanup(cleanup_string)))
7095
char *mandos_argz = NULL;
7096
size_t mandos_argz_length = 0;
7098
g_assert_false(parse_arguments_devnull(argc, argv, false,
7100
&helper_directory, &user,
7101
&group, &mandos_argz,
7102
&mandos_argz_length));
7104
g_assert_null(helper_directory);
7105
g_assert_null(agent_directory);
7106
g_assert_cmpuint((unsigned int)user, ==, 0);
7107
g_assert_true(group == 0);
7108
g_assert_null(mandos_argz);
7109
g_assert_true(mandos_argz_length == 0);
7111
for(char **arg = argv; *arg != NULL; arg++){
7116
static void test_parse_arguments_group(__attribute__((unused))
7117
test_fixture *fixture,
7118
__attribute__((unused))
7119
gconstpointer user_data){
7125
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7127
char *agent_directory = NULL;
7128
__attribute__((cleanup(cleanup_string)))
7129
char *helper_directory = NULL;
7132
__attribute__((cleanup(cleanup_string)))
7133
char *mandos_argz = NULL;
7134
size_t mandos_argz_length = 0;
7136
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7137
&helper_directory, &user, &group,
7138
&mandos_argz, &mandos_argz_length));
7140
g_assert_null(helper_directory);
7141
g_assert_null(agent_directory);
7142
g_assert_true(user == 0);
7143
g_assert_cmpuint((unsigned int)group, ==, 1000);
7144
g_assert_null(mandos_argz);
7145
g_assert_true(mandos_argz_length == 0);
7147
for(char **arg = argv; *arg != NULL; arg++){
7152
static void test_parse_arguments_group_invalid(__attribute__((unused))
7153
test_fixture *fixture,
7154
__attribute__((unused))
7162
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7164
char *agent_directory = NULL;
7165
__attribute__((cleanup(cleanup_string)))
7166
char *helper_directory = NULL;
7169
__attribute__((cleanup(cleanup_string)))
7170
char *mandos_argz = NULL;
7171
size_t mandos_argz_length = 0;
7173
g_assert_false(parse_arguments_devnull(argc, argv, false,
7175
&helper_directory, &user,
7176
&group, &mandos_argz,
7177
&mandos_argz_length));
7179
g_assert_null(helper_directory);
7180
g_assert_null(agent_directory);
7181
g_assert_true(user == 0);
7182
g_assert_true(group == 0);
7183
g_assert_null(mandos_argz);
7184
g_assert_true(mandos_argz_length == 0);
7186
for(char **arg = argv; *arg != NULL; arg++){
7192
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7193
test_fixture *fixture,
7194
__attribute__((unused))
7195
gconstpointer user_data){
7201
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7203
char *agent_directory = NULL;
7204
__attribute__((cleanup(cleanup_string)))
7205
char *helper_directory = NULL;
7208
__attribute__((cleanup(cleanup_string)))
7209
char *mandos_argz = NULL;
7210
size_t mandos_argz_length = 0;
7212
g_assert_false(parse_arguments_devnull(argc, argv, false,
7214
&helper_directory, &user,
7215
&group, &mandos_argz,
7216
&mandos_argz_length));
7218
g_assert_null(helper_directory);
7219
g_assert_null(agent_directory);
7220
g_assert_cmpuint((unsigned int)group, ==, 0);
7221
g_assert_true(group == 0);
7222
g_assert_null(mandos_argz);
7223
g_assert_true(mandos_argz_length == 0);
7225
for(char **arg = argv; *arg != NULL; arg++){
7230
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7231
test_fixture *fixture,
7232
__attribute__((unused))
7237
strdup("mandos-client"),
7239
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7241
__attribute__((cleanup(cleanup_string)))
7242
char *agent_directory = NULL;
7243
__attribute__((cleanup(cleanup_string)))
7244
char *helper_directory = NULL;
7247
__attribute__((cleanup(cleanup_string)))
7248
char *mandos_argz = NULL;
7249
size_t mandos_argz_length = 0;
7251
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7252
&helper_directory, &user, &group,
7253
&mandos_argz, &mandos_argz_length));
7255
g_assert_null(agent_directory);
7256
g_assert_null(helper_directory);
7257
g_assert_true(user == 0);
7258
g_assert_true(group == 0);
7259
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7260
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7261
mandos_argz_length),
7264
for(char **arg = argv; *arg != NULL; arg++){
7269
static void test_parse_arguments_mandos_args(__attribute__((unused))
7270
test_fixture *fixture,
7271
__attribute__((unused))
7272
gconstpointer user_data){
7275
strdup("mandos-client"),
7280
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7282
__attribute__((cleanup(cleanup_string)))
7283
char *agent_directory = NULL;
7284
__attribute__((cleanup(cleanup_string)))
7285
char *helper_directory = NULL;
7288
__attribute__((cleanup(cleanup_string)))
7289
char *mandos_argz = NULL;
7290
size_t mandos_argz_length = 0;
7292
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7293
&helper_directory, &user, &group,
7294
&mandos_argz, &mandos_argz_length));
7296
g_assert_null(agent_directory);
7297
g_assert_null(helper_directory);
7298
g_assert_true(user == 0);
7299
g_assert_true(group == 0);
7300
char *marg = mandos_argz;
7301
g_assert_cmpstr(marg, ==, "mandos-client");
7302
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7303
g_assert_cmpstr(marg, ==, "one");
7304
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7305
g_assert_cmpstr(marg, ==, "two");
7306
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7307
g_assert_cmpstr(marg, ==, "three");
7308
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7309
mandos_argz_length),
7312
for(char **arg = argv; *arg != NULL; arg++){
7317
static void test_parse_arguments_all_args(__attribute__((unused))
7318
test_fixture *fixture,
7319
__attribute__((unused))
7320
gconstpointer user_data){
7323
strdup("--agent-directory"),
7325
strdup("--helper-directory"),
7331
strdup("mandos-client"),
7336
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7338
__attribute__((cleanup(cleanup_string)))
7339
char *agent_directory = NULL;
7340
__attribute__((cleanup(cleanup_string)))
7341
char *helper_directory = NULL;
7344
__attribute__((cleanup(cleanup_string)))
7345
char *mandos_argz = NULL;
7346
size_t mandos_argz_length = 0;
7348
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7349
&helper_directory, &user, &group,
7350
&mandos_argz, &mandos_argz_length));
7352
g_assert_cmpstr(agent_directory, ==, "/tmp");
7353
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7354
g_assert_true(user == 1);
7355
g_assert_true(group == 2);
7356
char *marg = mandos_argz;
7357
g_assert_cmpstr(marg, ==, "mandos-client");
7358
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7359
g_assert_cmpstr(marg, ==, "one");
7360
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7361
g_assert_cmpstr(marg, ==, "two");
7362
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7363
g_assert_cmpstr(marg, ==, "three");
7364
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7365
mandos_argz_length),
7368
for(char **arg = argv; *arg != NULL; arg++){
7373
static void test_parse_arguments_mixed(__attribute__((unused))
7374
test_fixture *fixture,
7375
__attribute__((unused))
7376
gconstpointer user_data){
7379
strdup("mandos-client"),
7383
strdup("--agent-directory"),
7387
strdup("--helper-directory=/var/tmp"),
7389
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7391
__attribute__((cleanup(cleanup_string)))
7392
char *agent_directory = NULL;
7393
__attribute__((cleanup(cleanup_string)))
7394
char *helper_directory = NULL;
7397
__attribute__((cleanup(cleanup_string)))
7398
char *mandos_argz = NULL;
7399
size_t mandos_argz_length = 0;
7401
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7402
&helper_directory, &user, &group,
7403
&mandos_argz, &mandos_argz_length));
7405
g_assert_cmpstr(agent_directory, ==, "/tmp");
7406
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7407
g_assert_true(user == 1);
7408
g_assert_true(group == 0);
7409
char *marg = mandos_argz;
7410
g_assert_cmpstr(marg, ==, "mandos-client");
7411
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7412
g_assert_cmpstr(marg, ==, "one");
7413
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7414
g_assert_cmpstr(marg, ==, "two");
7415
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7416
g_assert_cmpstr(marg, ==, "three");
7417
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7418
mandos_argz_length),
7421
for(char **arg = argv; *arg != NULL; arg++){
7426
/* End of tests section */
7428
/* Test boilerplate section; New tests should be added to the test
7429
suite definition here, in the "run_tests" function.
7431
Finally, this section also contains the should_only_run_tests()
7432
function used by main() for deciding if tests should be run or to
7435
__attribute__((cold))
7436
static bool run_tests(int argc, char *argv[]){
7437
g_test_init(&argc, &argv, NULL);
7439
/* A macro to add a test with no setup or teardown functions */
7440
#define test_add(testpath, testfunc) \
7442
g_test_add((testpath), test_fixture, NULL, NULL, \
7443
(testfunc), NULL); \
7446
/* Test the signal-related functions first, since some other tests
7447
depend on these functions in their setups and teardowns */
7448
test_add("/signal-handling/setup", test_setup_signal_handler);
7449
test_add("/signal-handling/restore", test_restore_signal_handler);
7450
test_add("/signal-handling/block", test_block_sigchld);
7451
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7453
/* Regular non-signal-related tests; these use no setups or
7455
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7456
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7457
test_add("/parse_arguments/long-dir",
7458
test_parse_arguments_long_dir);
7459
test_add("/parse_arguments/short-dir",
7460
test_parse_arguments_short_dir);
7461
test_add("/parse_arguments/helper-directory",
7462
test_parse_arguments_helper_directory);
7463
test_add("/parse_arguments/plugin-helper-dir",
7464
test_parse_arguments_plugin_helper_dir);
7465
test_add("/parse_arguments/user", test_parse_arguments_user);
7466
test_add("/parse_arguments/user-invalid",
7467
test_parse_arguments_user_invalid);
7468
test_add("/parse_arguments/user-zero-invalid",
7469
test_parse_arguments_user_zero_invalid);
7470
test_add("/parse_arguments/group", test_parse_arguments_group);
7471
test_add("/parse_arguments/group-invalid",
7472
test_parse_arguments_group_invalid);
7473
test_add("/parse_arguments/group-zero-invalid",
7474
test_parse_arguments_group_zero_invalid);
7475
test_add("/parse_arguments/mandos-noargs",
7476
test_parse_arguments_mandos_noargs);
7477
test_add("/parse_arguments/mandos-args",
7478
test_parse_arguments_mandos_args);
7479
test_add("/parse_arguments/all-args",
7480
test_parse_arguments_all_args);
7481
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7482
test_add("/queue/create", test_create_queue);
7483
test_add("/queue/add", test_add_to_queue);
7484
test_add("/queue/has_question/empty",
7485
test_queue_has_question_empty);
7486
test_add("/queue/has_question/false",
7487
test_queue_has_question_false);
7488
test_add("/queue/has_question/true", test_queue_has_question_true);
7489
test_add("/queue/has_question/false2",
7490
test_queue_has_question_false2);
7491
test_add("/queue/has_question/true2",
7492
test_queue_has_question_true2);
7493
test_add("/buffer/cleanup", test_cleanup_buffer);
7494
test_add("/string_set/net-set-contains-nothing",
7495
test_string_set_new_set_contains_nothing);
7496
test_add("/string_set/with-added-string-contains-it",
7497
test_string_set_with_added_string_contains_it);
7498
test_add("/string_set/cleared-does-not-contain-string",
7499
test_string_set_cleared_does_not_contain_str);
7500
test_add("/string_set/swap/one-with-empty",
7501
test_string_set_swap_one_with_empty);
7502
test_add("/string_set/swap/empty-with-one",
7503
test_string_set_swap_empty_with_one);
7504
test_add("/string_set/swap/one-with-one",
7505
test_string_set_swap_one_with_one);
7507
/* A macro to add a test using the setup and teardown functions */
7508
#define test_add_st(path, func) \
7510
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7514
/* Signal-related tests; these use setups and teardowns which
7515
establish, during each test run, a signal handler for, and a
7516
signal mask blocking, the SIGCHLD signal, just like main() */
7517
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7518
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7519
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7520
test_add_st("/run_queue/zeroes-next-run",
7521
test_run_queue_zeroes_next_run);
7522
test_add_st("/run_queue/clears-cancelled_filenames",
7523
test_run_queue_clears_cancelled_filenames);
7524
test_add_st("/run_queue/skips-cancelled-filenames",
7525
test_run_queue_skips_cancelled_filenames);
7526
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7527
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7528
test_add_st("/run_queue/two-tasks/quit",
7529
test_run_queue_two_tasks_quit);
7530
test_add_st("/run_queue/two-tasks-cleanup",
7531
test_run_queue_two_tasks_cleanup);
7532
test_add_st("/task-creators/start_mandos_client",
7533
test_start_mandos_client);
7534
test_add_st("/task-creators/start_mandos_client/execv",
7535
test_start_mandos_client_execv);
7536
test_add_st("/task-creators/start_mandos_client/suid/euid",
7537
test_start_mandos_client_suid_euid);
7538
test_add_st("/task-creators/start_mandos_client/suid/egid",
7539
test_start_mandos_client_suid_egid);
7540
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7541
test_start_mandos_client_suid_ruid);
7542
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7543
test_start_mandos_client_suid_rgid);
7544
test_add_st("/task-creators/start_mandos_client/read",
7545
test_start_mandos_client_read);
7546
test_add_st("/task-creators/start_mandos_client/helper-directory",
7547
test_start_mandos_client_helper_directory);
7548
test_add_st("/task-creators/start_mandos_client/sigmask",
7549
test_start_mandos_client_sigmask);
7550
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7551
test_wait_for_mandos_client_exit_badpid);
7552
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7553
test_wait_for_mandos_client_exit_noexit);
7554
test_add_st("/task/wait_for_mandos_client_exit/success",
7555
test_wait_for_mandos_client_exit_success);
7556
test_add_st("/task/wait_for_mandos_client_exit/failure",
7557
test_wait_for_mandos_client_exit_failure);
7558
test_add_st("/task/wait_for_mandos_client_exit/killed",
7559
test_wait_for_mandos_client_exit_killed);
7560
test_add_st("/task/read_mandos_client_output/readerror",
7561
test_read_mandos_client_output_readerror);
7562
test_add_st("/task/read_mandos_client_output/nodata",
7563
test_read_mandos_client_output_nodata);
7564
test_add_st("/task/read_mandos_client_output/eof",
7565
test_read_mandos_client_output_eof);
7566
test_add_st("/task/read_mandos_client_output/once",
7567
test_read_mandos_client_output_once);
7568
test_add_st("/task/read_mandos_client_output/malloc",
7569
test_read_mandos_client_output_malloc);
7570
test_add_st("/task/read_mandos_client_output/append",
7571
test_read_mandos_client_output_append);
7572
test_add_st("/task-creators/add_inotify_dir_watch",
7573
test_add_inotify_dir_watch);
7574
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7575
test_add_inotify_dir_watch_fail);
7576
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7577
test_add_inotify_dir_watch_EAGAIN);
7578
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7579
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7580
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7581
test_add_inotify_dir_watch_IN_MOVED_TO);
7582
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7583
test_add_inotify_dir_watch_IN_DELETE);
7584
test_add_st("/task/read_inotify_event/readerror",
7585
test_read_inotify_event_readerror);
7586
test_add_st("/task/read_inotify_event/bad-epoll",
7587
test_read_inotify_event_bad_epoll);
7588
test_add_st("/task/read_inotify_event/nodata",
7589
test_read_inotify_event_nodata);
7590
test_add_st("/task/read_inotify_event/eof",
7591
test_read_inotify_event_eof);
7592
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7593
test_read_inotify_event_IN_CLOSE_WRITE);
7594
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7595
test_read_inotify_event_IN_MOVED_TO);
7596
test_add_st("/task/read_inotify_event/IN_DELETE",
7597
test_read_inotify_event_IN_DELETE);
7598
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7599
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7600
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7601
test_read_inotify_event_IN_MOVED_TO_badname);
7602
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7603
test_read_inotify_event_IN_DELETE_badname);
7604
test_add_st("/task/open_and_parse_question/ENOENT",
7605
test_open_and_parse_question_ENOENT);
7606
test_add_st("/task/open_and_parse_question/EIO",
7607
test_open_and_parse_question_EIO);
7608
test_add_st("/task/open_and_parse_question/parse-error",
7609
test_open_and_parse_question_parse_error);
7610
test_add_st("/task/open_and_parse_question/nosocket",
7611
test_open_and_parse_question_nosocket);
7612
test_add_st("/task/open_and_parse_question/badsocket",
7613
test_open_and_parse_question_badsocket);
7614
test_add_st("/task/open_and_parse_question/nopid",
7615
test_open_and_parse_question_nopid);
7616
test_add_st("/task/open_and_parse_question/badpid",
7617
test_open_and_parse_question_badpid);
7618
test_add_st("/task/open_and_parse_question/noexist_pid",
7619
test_open_and_parse_question_noexist_pid);
7620
test_add_st("/task/open_and_parse_question/no-notafter",
7621
test_open_and_parse_question_no_notafter);
7622
test_add_st("/task/open_and_parse_question/bad-notafter",
7623
test_open_and_parse_question_bad_notafter);
7624
test_add_st("/task/open_and_parse_question/notafter-0",
7625
test_open_and_parse_question_notafter_0);
7626
test_add_st("/task/open_and_parse_question/notafter-1",
7627
test_open_and_parse_question_notafter_1);
7628
test_add_st("/task/open_and_parse_question/notafter-1-1",
7629
test_open_and_parse_question_notafter_1_1);
7630
test_add_st("/task/open_and_parse_question/notafter-1-2",
7631
test_open_and_parse_question_notafter_1_2);
7632
test_add_st("/task/open_and_parse_question/equal-notafter",
7633
test_open_and_parse_question_equal_notafter);
7634
test_add_st("/task/open_and_parse_question/late-notafter",
7635
test_open_and_parse_question_late_notafter);
7636
test_add_st("/task/cancel_old_question/0-1-2",
7637
test_cancel_old_question_0_1_2);
7638
test_add_st("/task/cancel_old_question/0-2-1",
7639
test_cancel_old_question_0_2_1);
7640
test_add_st("/task/cancel_old_question/1-2-3",
7641
test_cancel_old_question_1_2_3);
7642
test_add_st("/task/cancel_old_question/1-3-2",
7643
test_cancel_old_question_1_3_2);
7644
test_add_st("/task/cancel_old_question/2-1-3",
7645
test_cancel_old_question_2_1_3);
7646
test_add_st("/task/cancel_old_question/2-3-1",
7647
test_cancel_old_question_2_3_1);
7648
test_add_st("/task/cancel_old_question/3-1-2",
7649
test_cancel_old_question_3_1_2);
7650
test_add_st("/task/cancel_old_question/3-2-1",
7651
test_cancel_old_question_3_2_1);
7652
test_add_st("/task/connect_question_socket/name-too-long",
7653
test_connect_question_socket_name_too_long);
7654
test_add_st("/task/connect_question_socket/connect-fail",
7655
test_connect_question_socket_connect_fail);
7656
test_add_st("/task/connect_question_socket/bad-epoll",
7657
test_connect_question_socket_bad_epoll);
7658
test_add_st("/task/connect_question_socket/usable",
7659
test_connect_question_socket_usable);
7660
test_add_st("/task/send_password_to_socket/client-not-exited",
7661
test_send_password_to_socket_client_not_exited);
7662
test_add_st("/task/send_password_to_socket/password-not-read",
7663
test_send_password_to_socket_password_not_read);
7664
test_add_st("/task/send_password_to_socket/EMSGSIZE",
7665
test_send_password_to_socket_EMSGSIZE);
7666
test_add_st("/task/send_password_to_socket/retry",
7667
test_send_password_to_socket_retry);
7668
test_add_st("/task/send_password_to_socket/bad-epoll",
7669
test_send_password_to_socket_bad_epoll);
7670
test_add_st("/task/send_password_to_socket/null-password",
7671
test_send_password_to_socket_null_password);
7672
test_add_st("/task/send_password_to_socket/empty-password",
7673
test_send_password_to_socket_empty_password);
7674
test_add_st("/task/send_password_to_socket/empty-str-password",
7675
test_send_password_to_socket_empty_str_pass);
7676
test_add_st("/task/send_password_to_socket/text-password",
7677
test_send_password_to_socket_text_password);
7678
test_add_st("/task/send_password_to_socket/binary-password",
7679
test_send_password_to_socket_binary_password);
7680
test_add_st("/task/send_password_to_socket/nuls-in-password",
7681
test_send_password_to_socket_nuls_in_password);
7682
test_add_st("/task-creators/add_existing_questions/ENOENT",
7683
test_add_existing_questions_ENOENT);
7684
test_add_st("/task-creators/add_existing_questions/no-questions",
7685
test_add_existing_questions_no_questions);
7686
test_add_st("/task-creators/add_existing_questions/one-question",
7687
test_add_existing_questions_one_question);
7688
test_add_st("/task-creators/add_existing_questions/two-questions",
7689
test_add_existing_questions_two_questions);
7690
test_add_st("/task-creators/add_existing_questions/non-questions",
7691
test_add_existing_questions_non_questions);
7692
test_add_st("/task-creators/add_existing_questions/both-types",
7693
test_add_existing_questions_both_types);
7695
return g_test_run() == 0;
7698
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
7699
GOptionContext *context = g_option_context_new("");
7701
g_option_context_set_help_enabled(context, FALSE);
7702
g_option_context_set_ignore_unknown_options(context, TRUE);
7704
gboolean run_tests = FALSE;
7705
GOptionEntry entries[] = {
7706
{ "test", 0, 0, G_OPTION_ARG_NONE,
7707
&run_tests, "Run tests", NULL },
7710
g_option_context_add_main_entries(context, entries, NULL);
7712
GError *error = NULL;
7714
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
7715
g_option_context_free(context);
7716
g_error("Failed to parse options: %s", error->message);
7719
g_option_context_free(context);
7720
return run_tests != FALSE;