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)
1075
struct inotify_event event;
1076
char name_buffer[NAME_MAX + 1];
1078
struct inotify_event *const ievent = &ievent_buffer.event;
1080
const ssize_t read_length = read(fd, ievent, ievent_size);
1081
if(read_length == 0){ /* EOF */
1082
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1084
cleanup_task(&task);
1087
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1088
error(0, errno, "Failed to read from inotify fd for directory %s",
1091
cleanup_task(&task);
1094
if(read_length > 0 /* Data has been read */
1095
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1096
char *question_filename = NULL;
1097
const ssize_t question_filename_length
1098
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1099
if(question_filename_length < 0){
1100
error(0, errno, "Failed to create file name from directory name"
1101
" %s and file name %s", filename, ievent->name);
1103
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1104
if(not add_to_queue(queue, (task_context){
1105
.func=open_and_parse_question,
1107
.question_filename=question_filename,
1108
.filename=question_filename,
1110
.cancelled_filenames=cancelled_filenames,
1111
.current_time=current_time,
1112
.mandos_client_exited=mandos_client_exited,
1113
.password_is_read=password_is_read,
1115
error(0, errno, "Failed to add open_and_parse_question task"
1116
" for file name %s to queue", filename);
1118
/* Force the added task (open_and_parse_question) to run
1120
queue->next_run = 1;
1122
} else if(ievent->mask & IN_DELETE){
1123
if(not string_set_add(cancelled_filenames,
1124
question_filename)){
1125
error(0, errno, "Could not add question %s to"
1126
" cancelled_questions", question_filename);
1128
free(question_filename);
1129
cleanup_task(&task);
1132
free(question_filename);
1137
/* Either data was read, or EAGAIN was indicated, meaning no data
1140
/* Re-add myself to the queue */
1141
if(not add_to_queue(queue, task)){
1142
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1143
" queue", filename);
1145
cleanup_task(&task);
1149
/* Re-add the fd to the epoll set */
1150
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1151
&(struct epoll_event)
1152
{ .events=EPOLLIN | EPOLLRDHUP });
1153
if(ret != 0 and errno != EEXIST){
1154
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1155
" directory %s to epoll set", fd, filename);
1156
/* Force the added task (read_inotify_event) to run again, at most
1157
one second from now */
1158
if((queue->next_run == 0)
1159
or (queue->next_run > (*current_time + 1000000))){
1160
queue->next_run = *current_time + 1000000;
1165
__attribute__((nonnull))
1166
void open_and_parse_question(const task_context task,
1167
task_queue *const queue){
1168
__attribute__((cleanup(cleanup_string)))
1169
char *question_filename = task.question_filename;
1170
const int epoll_fd = task.epoll_fd;
1171
buffer *const password = task.password;
1172
string_set *const cancelled_filenames = task.cancelled_filenames;
1173
const mono_microsecs *const current_time = task.current_time;
1174
bool *const mandos_client_exited = task.mandos_client_exited;
1175
bool *const password_is_read = task.password_is_read;
1177
/* We use the GLib "Key-value file parser" functions to parse the
1178
question file. See <https://www.freedesktop.org/wiki/Software
1179
/systemd/PasswordAgents/> for specification of contents */
1180
__attribute__((nonnull))
1181
void cleanup_g_key_file(GKeyFile **key_file){
1182
if(*key_file != NULL){
1183
g_key_file_free(*key_file);
1187
__attribute__((cleanup(cleanup_g_key_file)))
1188
GKeyFile *key_file = g_key_file_new();
1189
if(key_file == NULL){
1190
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1194
GError *glib_error = NULL;
1195
if(g_key_file_load_from_file(key_file, question_filename,
1196
G_KEY_FILE_NONE, &glib_error) != TRUE){
1197
/* If a file was removed, we should ignore it, so */
1198
/* only show error message if file actually existed */
1199
if(glib_error->code != G_FILE_ERROR_NOENT){
1200
error(0, 0, "Failed to load question data from file \"%s\": %s",
1201
question_filename, glib_error->message);
1206
__attribute__((cleanup(cleanup_string)))
1207
char *socket_name = g_key_file_get_string(key_file, "Ask",
1210
if(socket_name == NULL){
1211
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1212
question_filename, glib_error->message);
1216
if(strlen(socket_name) == 0){
1217
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1222
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1224
if(glib_error != NULL){
1225
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1226
question_filename, glib_error->message);
1230
if((pid != (guint64)((pid_t)pid))
1231
or (kill((pid_t)pid, 0) != 0)){
1232
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1233
" does not exist", (uintmax_t)pid, question_filename);
1237
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1238
"NotAfter", &glib_error);
1239
if(glib_error != NULL){
1240
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1241
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1242
" %s", question_filename, glib_error->message);
1247
if(queue->next_run == 0 or (queue->next_run > notafter)){
1248
queue->next_run = notafter;
1250
if(*current_time >= notafter){
1255
const task_context connect_question_socket_task = {
1256
.func=connect_question_socket,
1257
.question_filename=strdup(question_filename),
1260
.filename=strdup(socket_name),
1261
.cancelled_filenames=task.cancelled_filenames,
1262
.mandos_client_exited=mandos_client_exited,
1263
.password_is_read=password_is_read,
1264
.current_time=current_time,
1266
if(connect_question_socket_task.question_filename == NULL
1267
or connect_question_socket_task.filename == NULL
1268
or not add_to_queue(queue, connect_question_socket_task)){
1269
error(0, errno, "Failed to add connect_question_socket for socket"
1270
" %s (from \"%s\") to queue", socket_name,
1272
cleanup_task(&connect_question_socket_task);
1275
/* Force the added task (connect_question_socket) to run
1277
queue->next_run = 1;
1280
char *const dup_filename = strdup(question_filename);
1281
const task_context cancel_old_question_task = {
1282
.func=cancel_old_question,
1283
.question_filename=dup_filename,
1285
.filename=dup_filename,
1286
.cancelled_filenames=cancelled_filenames,
1287
.current_time=current_time,
1289
if(cancel_old_question_task.question_filename == NULL
1290
or not add_to_queue(queue, cancel_old_question_task)){
1291
error(0, errno, "Failed to add cancel_old_question for file "
1292
"\"%s\" to queue", question_filename);
1293
cleanup_task(&cancel_old_question_task);
1299
__attribute__((nonnull))
1300
void cancel_old_question(const task_context task,
1301
task_queue *const queue){
1302
char *const question_filename = task.question_filename;
1303
string_set *const cancelled_filenames = task.cancelled_filenames;
1304
const mono_microsecs notafter = task.notafter;
1305
const mono_microsecs *const current_time = task.current_time;
1307
if(*current_time >= notafter){
1308
if(not string_set_add(cancelled_filenames, question_filename)){
1309
error(0, errno, "Failed to cancel question for file %s",
1312
cleanup_task(&task);
1316
if(not add_to_queue(queue, task)){
1317
error(0, errno, "Failed to add cancel_old_question for file "
1318
"%s to queue", question_filename);
1319
cleanup_task(&task);
1323
if((queue->next_run == 0) or (queue->next_run > notafter)){
1324
queue->next_run = notafter;
1328
__attribute__((nonnull))
1329
void connect_question_socket(const task_context task,
1330
task_queue *const queue){
1331
char *const question_filename = task.question_filename;
1332
char *const filename = task.filename;
1333
const int epoll_fd = task.epoll_fd;
1334
buffer *const password = task.password;
1335
string_set *const cancelled_filenames = task.cancelled_filenames;
1336
bool *const mandos_client_exited = task.mandos_client_exited;
1337
bool *const password_is_read = task.password_is_read;
1338
const mono_microsecs *const current_time = task.current_time;
1340
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1342
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1343
error(0, 0, "Socket filename is larger than"
1344
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1345
(uintmax_t)sizeof(sock_name.sun_path), filename);
1346
if(not string_set_add(cancelled_filenames, question_filename)){
1347
error(0, errno, "Failed to cancel question for file %s",
1350
cleanup_task(&task);
1354
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1355
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1358
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1359
if(not add_to_queue(queue, task)){
1360
error(0, errno, "Failed to add connect_question_socket for file"
1361
" \"%s\" and socket \"%s\" to queue", question_filename,
1363
cleanup_task(&task);
1365
/* Force the added task (connect_question_socket) to run
1367
queue->next_run = 1;
1372
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1373
if(connect(fd, (struct sockaddr *)&sock_name,
1374
(socklen_t)SUN_LEN(&sock_name)) != 0){
1375
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1376
if(not add_to_queue(queue, task)){
1377
error(0, errno, "Failed to add connect_question_socket for file"
1378
" \"%s\" and socket \"%s\" to queue", question_filename,
1380
cleanup_task(&task);
1382
/* Force the added task (connect_question_socket) to run again,
1383
at most one second from now */
1384
if((queue->next_run == 0)
1385
or (queue->next_run > (*current_time + 1000000))){
1386
queue->next_run = *current_time + 1000000;
1392
/* Not necessary, but we can try, and merely warn on failure */
1393
if(shutdown(fd, SHUT_RD) != 0){
1394
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1398
/* Add the fd to the epoll set */
1399
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1400
&(struct epoll_event){ .events=EPOLLOUT })
1402
error(0, errno, "Failed to add inotify file descriptor %d for"
1403
" socket %s to epoll set", fd, filename);
1404
if(not add_to_queue(queue, task)){
1405
error(0, errno, "Failed to add connect_question_socket for file"
1406
" \"%s\" and socket \"%s\" to queue", question_filename,
1408
cleanup_task(&task);
1410
/* Force the added task (connect_question_socket) to run again,
1411
at most one second from now */
1412
if((queue->next_run == 0)
1413
or (queue->next_run > (*current_time + 1000000))){
1414
queue->next_run = *current_time + 1000000;
1420
/* add task send_password_to_socket to queue */
1421
const task_context send_password_to_socket_task = {
1422
.func=send_password_to_socket,
1423
.question_filename=question_filename,
1428
.cancelled_filenames=cancelled_filenames,
1429
.mandos_client_exited=mandos_client_exited,
1430
.password_is_read=password_is_read,
1431
.current_time=current_time,
1434
if(not add_to_queue(queue, send_password_to_socket_task)){
1435
error(0, errno, "Failed to add send_password_to_socket for"
1436
" file \"%s\" and socket \"%s\" to queue",
1437
question_filename, filename);
1438
cleanup_task(&send_password_to_socket_task);
1442
__attribute__((nonnull))
1443
void send_password_to_socket(const task_context task,
1444
task_queue *const queue){
1445
char *const question_filename=task.question_filename;
1446
char *const filename=task.filename;
1447
const int epoll_fd=task.epoll_fd;
1448
const int fd=task.fd;
1449
buffer *const password=task.password;
1450
string_set *const cancelled_filenames=task.cancelled_filenames;
1451
bool *const mandos_client_exited = task.mandos_client_exited;
1452
bool *const password_is_read = task.password_is_read;
1453
const mono_microsecs *const current_time = task.current_time;
1455
if(*mandos_client_exited and *password_is_read){
1457
const size_t send_buffer_length = password->length + 2;
1458
char *send_buffer = malloc(send_buffer_length);
1459
if(send_buffer == NULL){
1460
error(0, errno, "Failed to allocate send_buffer");
1462
if(mlock(send_buffer, send_buffer_length) != 0){
1463
/* Warn but do not treat as fatal error */
1464
if(errno != EPERM and errno != ENOMEM){
1465
error(0, errno, "Failed to lock memory for password"
1469
/* “[…] send a single datagram to the socket consisting of the
1470
password string either prefixed with "+" or with "-"
1471
depending on whether the password entry was successful or
1472
not. You may but don't have to include a final NUL byte in
1475
— <https://www.freedesktop.org/wiki/Software/systemd/
1476
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1478
send_buffer[0] = '+'; /* Prefix with "+" */
1479
/* Always add an extra NUL */
1480
send_buffer[password->length + 1] = '\0';
1481
if(password->length > 0){
1482
memcpy(send_buffer + 1, password->data, password->length);
1485
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1487
const error_t saved_errno = errno;
1488
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1489
explicit_bzero(send_buffer, send_buffer_length);
1491
memset(send_buffer, '\0', send_buffer_length);
1493
if(munlock(send_buffer, send_buffer_length) != 0){
1494
error(0, errno, "Failed to unlock memory of send buffer");
1497
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1498
switch(saved_errno){
1511
error(0, 0, "Password of size %" PRIuMAX " is too big",
1512
(uintmax_t)password->length);
1516
__attribute__((fallthrough));
1519
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1520
error(0, 0, "Password only partially sent to socket");
1525
__attribute__((fallthrough));
1528
error(0, saved_errno, "Failed to send() to socket %s",
1530
if(not string_set_add(cancelled_filenames,
1531
question_filename)){
1532
error(0, errno, "Failed to cancel question for file %s",
1535
cleanup_task(&task);
1540
cleanup_task(&task);
1546
/* We failed or are not ready yet; retry later */
1548
if(not add_to_queue(queue, task)){
1549
error(0, errno, "Failed to add send_password_to_socket for"
1550
" file %s and socket %s to queue", question_filename,
1552
cleanup_task(&task);
1555
/* Add the fd to the epoll set */
1556
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1557
&(struct epoll_event){ .events=EPOLLOUT })
1559
error(0, errno, "Failed to add socket file descriptor %d for"
1560
" socket %s to epoll set", fd, filename);
1561
/* Force the added task (send_password_to_socket) to run again, at
1562
most one second from now */
1563
if((queue->next_run == 0)
1564
or (queue->next_run > (*current_time + 1000000))){
1565
queue->next_run = *current_time + 1000000;
1570
__attribute__((warn_unused_result))
1571
bool add_existing_questions(task_queue *const queue,
1573
buffer *const password,
1574
string_set *cancelled_filenames,
1575
const mono_microsecs *const current_time,
1576
bool *const mandos_client_exited,
1577
bool *const password_is_read,
1578
const char *const dirname){
1579
__attribute__((cleanup(cleanup_string)))
1580
char *dir_pattern = NULL;
1581
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1582
if(ret < 0 or dir_pattern == NULL){
1583
error(0, errno, "Could not create glob pattern for directory %s",
1587
__attribute__((cleanup(globfree)))
1588
glob_t question_filenames = {};
1589
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1590
NULL, &question_filenames)){
1593
error(0, errno, "Failed to open directory %s", dirname);
1596
error(0, errno, "There are no question files in %s", dirname);
1599
error(0, errno, "Could not allocate memory for question file"
1600
" names in %s", dirname);
1604
__attribute__((fallthrough));
1607
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1608
char *const question_filename = strdup(question_filenames
1610
const task_context task = {
1611
.func=open_and_parse_question,
1613
.question_filename=question_filename,
1614
.filename=question_filename,
1616
.cancelled_filenames=cancelled_filenames,
1617
.current_time=current_time,
1618
.mandos_client_exited=mandos_client_exited,
1619
.password_is_read=password_is_read,
1622
if(question_filename == NULL
1623
or not add_to_queue(queue, task)){
1624
error(0, errno, "Failed to add open_and_parse_question for"
1625
" file %s to queue",
1626
question_filenames.gl_pathv[i]);
1627
free(question_filename);
1629
queue->next_run = 1;
1636
__attribute__((nonnull, warn_unused_result))
1637
bool wait_for_event(const int epoll_fd,
1638
const mono_microsecs queue_next_run,
1639
const mono_microsecs current_time){
1640
__attribute__((const))
1641
int milliseconds_to_wait(const mono_microsecs currtime,
1642
const mono_microsecs nextrun){
1643
if(currtime >= nextrun){
1646
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1647
if(wait_time_ms > (uintmax_t)INT_MAX){
1650
return (int)wait_time_ms;
1653
const int wait_time_ms = milliseconds_to_wait(current_time,
1656
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1657
sigset_t temporary_unblocked_sigmask;
1658
/* Get current signal mask */
1659
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1662
/* Remove SIGCHLD from the signal mask */
1663
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1666
struct epoll_event events[8]; /* Ignored */
1667
int ret = epoll_pwait(epoll_fd, events,
1668
sizeof(events) / sizeof(struct epoll_event),
1669
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1670
&temporary_unblocked_sigmask);
1671
if(ret < 0 and errno != EINTR){
1672
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1674
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1677
return clear_all_fds_from_epoll_set(epoll_fd);
1680
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1681
/* Create a new empty epoll set */
1682
__attribute__((cleanup(cleanup_close)))
1683
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1684
if(new_epoll_fd < 0){
1687
/* dup3() the new epoll set fd over the old one, replacing it */
1688
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1694
__attribute__((nonnull, warn_unused_result))
1695
bool run_queue(task_queue **const queue,
1696
string_set *const cancelled_filenames,
1697
bool *const quit_now){
1699
task_queue *new_queue = create_queue();
1700
if(new_queue == NULL){
1704
__attribute__((cleanup(string_set_clear)))
1705
string_set old_cancelled_filenames = {};
1706
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1708
/* Declare i outside the for loop, since we might need i after the
1709
loop in case we aborted in the middle */
1711
for(i=0; i < (*queue)->length and not *quit_now; i++){
1712
task_context *const task = &((*queue)->tasks[i]);
1713
const char *const question_filename = task->question_filename;
1714
/* Skip any task referencing a cancelled question filename */
1715
if(question_filename != NULL
1716
and string_set_contains(old_cancelled_filenames,
1717
question_filename)){
1721
task->func(*task, new_queue);
1725
/* we might be in the middle of the queue, so clean up any
1726
remaining tasks in the current queue */
1727
for(; i < (*queue)->length; i++){
1728
cleanup_task(&((*queue)->tasks[i]));
1742
/* End of regular code section */
1744
/* Start of tests section; here are the tests for the above code */
1746
/* This "fixture" data structure is used by the test setup and
1747
teardown functions */
1749
struct sigaction orig_sigaction;
1750
sigset_t orig_sigmask;
1753
static void test_setup(test_fixture *fixture,
1754
__attribute__((unused))
1755
gconstpointer user_data){
1756
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1757
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1760
static void test_teardown(test_fixture *fixture,
1761
__attribute__((unused))
1762
gconstpointer user_data){
1763
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1764
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1767
/* Utility function used by tests to search queue for matching task */
1768
__attribute__((pure, nonnull, warn_unused_result))
1769
static task_context *find_matching_task(const task_queue *const queue,
1770
const task_context task){
1771
/* The argument "task" structure is a pattern to match; 0 in any
1772
member means any value matches, otherwise the value must match.
1773
The filename strings are compared by strcmp(), not by pointer. */
1774
for(size_t i = 0; i < queue->length; i++){
1775
task_context *const current_task = queue->tasks+i;
1776
/* Check all members of task_context, if set to a non-zero value.
1777
If a member does not match, continue to next task in queue */
1779
/* task_func *const func */
1780
if(task.func != NULL and current_task->func != task.func){
1783
/* char *const question_filename; */
1784
if(task.question_filename != NULL
1785
and (current_task->question_filename == NULL
1786
or strcmp(current_task->question_filename,
1787
task.question_filename) != 0)){
1790
/* const pid_t pid; */
1791
if(task.pid != 0 and current_task->pid != task.pid){
1794
/* const int epoll_fd; */
1795
if(task.epoll_fd != 0
1796
and current_task->epoll_fd != task.epoll_fd){
1799
/* bool *const quit_now; */
1800
if(task.quit_now != NULL
1801
and current_task->quit_now != task.quit_now){
1805
if(task.fd != 0 and current_task->fd != task.fd){
1808
/* bool *const mandos_client_exited; */
1809
if(task.mandos_client_exited != NULL
1810
and current_task->mandos_client_exited
1811
!= task.mandos_client_exited){
1814
/* buffer *const password; */
1815
if(task.password != NULL
1816
and current_task->password != task.password){
1819
/* bool *const password_is_read; */
1820
if(task.password_is_read != NULL
1821
and current_task->password_is_read != task.password_is_read){
1824
/* char *filename; */
1825
if(task.filename != NULL
1826
and (current_task->filename == NULL
1827
or strcmp(current_task->filename, task.filename) != 0)){
1830
/* string_set *const cancelled_filenames; */
1831
if(task.cancelled_filenames != NULL
1832
and current_task->cancelled_filenames
1833
!= task.cancelled_filenames){
1836
/* const mono_microsecs notafter; */
1837
if(task.notafter != 0
1838
and current_task->notafter != task.notafter){
1841
/* const mono_microsecs *const current_time; */
1842
if(task.current_time != NULL
1843
and current_task->current_time != task.current_time){
1846
/* Current task matches all members; return it */
1847
return current_task;
1849
/* No task in queue matches passed pattern task */
1853
static void test_create_queue(__attribute__((unused))
1854
test_fixture *fixture,
1855
__attribute__((unused))
1856
gconstpointer user_data){
1857
__attribute__((cleanup(cleanup_queue)))
1858
task_queue *const queue = create_queue();
1859
g_assert_nonnull(queue);
1860
g_assert_null(queue->tasks);
1861
g_assert_true(queue->length == 0);
1862
g_assert_true(queue->next_run == 0);
1865
static task_func dummy_func;
1867
static void test_add_to_queue(__attribute__((unused))
1868
test_fixture *fixture,
1869
__attribute__((unused))
1870
gconstpointer user_data){
1871
__attribute__((cleanup(cleanup_queue)))
1872
task_queue *queue = create_queue();
1873
g_assert_nonnull(queue);
1875
g_assert_true(add_to_queue(queue,
1876
(task_context){ .func=dummy_func }));
1877
g_assert_true(queue->length == 1);
1878
g_assert_nonnull(queue->tasks);
1879
g_assert_true(queue->tasks[0].func == dummy_func);
1882
static void dummy_func(__attribute__((unused))
1883
const task_context task,
1884
__attribute__((unused))
1885
task_queue *const queue){
1888
static void test_queue_has_question_empty(__attribute__((unused))
1889
test_fixture *fixture,
1890
__attribute__((unused))
1891
gconstpointer user_data){
1892
__attribute__((cleanup(cleanup_queue)))
1893
task_queue *queue = create_queue();
1894
g_assert_nonnull(queue);
1895
g_assert_false(queue_has_question(queue));
1898
static void test_queue_has_question_false(__attribute__((unused))
1899
test_fixture *fixture,
1900
__attribute__((unused))
1901
gconstpointer user_data){
1902
__attribute__((cleanup(cleanup_queue)))
1903
task_queue *queue = create_queue();
1904
g_assert_nonnull(queue);
1905
g_assert_true(add_to_queue(queue,
1906
(task_context){ .func=dummy_func }));
1907
g_assert_false(queue_has_question(queue));
1910
static void test_queue_has_question_true(__attribute__((unused))
1911
test_fixture *fixture,
1912
__attribute__((unused))
1913
gconstpointer user_data){
1914
__attribute__((cleanup(cleanup_queue)))
1915
task_queue *queue = create_queue();
1916
g_assert_nonnull(queue);
1917
char *const question_filename
1918
= strdup("/nonexistent/question_filename");
1919
g_assert_nonnull(question_filename);
1920
task_context task = {
1922
.question_filename=question_filename,
1924
g_assert_true(add_to_queue(queue, task));
1925
g_assert_true(queue_has_question(queue));
1928
static void test_queue_has_question_false2(__attribute__((unused))
1929
test_fixture *fixture,
1930
__attribute__((unused))
1931
gconstpointer user_data){
1932
__attribute__((cleanup(cleanup_queue)))
1933
task_queue *queue = create_queue();
1934
g_assert_nonnull(queue);
1935
task_context task = { .func=dummy_func };
1936
g_assert_true(add_to_queue(queue, task));
1937
g_assert_true(add_to_queue(queue, task));
1938
g_assert_cmpint((int)queue->length, ==, 2);
1939
g_assert_false(queue_has_question(queue));
1942
static void test_queue_has_question_true2(__attribute__((unused))
1943
test_fixture *fixture,
1944
__attribute__((unused))
1945
gconstpointer user_data){
1946
__attribute__((cleanup(cleanup_queue)))
1947
task_queue *queue = create_queue();
1948
g_assert_nonnull(queue);
1949
task_context task1 = { .func=dummy_func };
1950
g_assert_true(add_to_queue(queue, task1));
1951
char *const question_filename
1952
= strdup("/nonexistent/question_filename");
1953
g_assert_nonnull(question_filename);
1954
task_context task2 = {
1956
.question_filename=question_filename,
1958
g_assert_true(add_to_queue(queue, task2));
1959
g_assert_cmpint((int)queue->length, ==, 2);
1960
g_assert_true(queue_has_question(queue));
1963
static void test_cleanup_buffer(__attribute__((unused))
1964
test_fixture *fixture,
1965
__attribute__((unused))
1966
gconstpointer user_data){
1969
const size_t buffersize = 10;
1971
buf.data = malloc(buffersize);
1972
g_assert_nonnull(buf.data);
1973
if(mlock(buf.data, buffersize) != 0){
1974
g_assert_true(errno == EPERM or errno == ENOMEM);
1977
cleanup_buffer(&buf);
1978
g_assert_null(buf.data);
1982
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1983
test_fixture *fixture,
1984
__attribute__((unused))
1987
__attribute__((cleanup(string_set_clear)))
1988
string_set set = {};
1989
g_assert_false(string_set_contains(set, "")); /* Empty string */
1990
g_assert_false(string_set_contains(set, "test_string"));
1994
test_string_set_with_added_string_contains_it(__attribute__((unused))
1995
test_fixture *fixture,
1996
__attribute__((unused))
1999
__attribute__((cleanup(string_set_clear)))
2000
string_set set = {};
2001
g_assert_true(string_set_add(&set, "test_string"));
2002
g_assert_true(string_set_contains(set, "test_string"));
2006
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2007
test_fixture *fixture,
2008
__attribute__((unused))
2009
gconstpointer user_data){
2010
__attribute__((cleanup(string_set_clear)))
2011
string_set set = {};
2012
g_assert_true(string_set_add(&set, "test_string"));
2013
string_set_clear(&set);
2014
g_assert_false(string_set_contains(set, "test_string"));
2018
void test_string_set_swap_one_with_empty(__attribute__((unused))
2019
test_fixture *fixture,
2020
__attribute__((unused))
2021
gconstpointer user_data){
2022
__attribute__((cleanup(string_set_clear)))
2023
string_set set1 = {};
2024
__attribute__((cleanup(string_set_clear)))
2025
string_set set2 = {};
2026
g_assert_true(string_set_add(&set1, "test_string1"));
2027
string_set_swap(&set1, &set2);
2028
g_assert_false(string_set_contains(set1, "test_string1"));
2029
g_assert_true(string_set_contains(set2, "test_string1"));
2033
void test_string_set_swap_empty_with_one(__attribute__((unused))
2034
test_fixture *fixture,
2035
__attribute__((unused))
2036
gconstpointer user_data){
2037
__attribute__((cleanup(string_set_clear)))
2038
string_set set1 = {};
2039
__attribute__((cleanup(string_set_clear)))
2040
string_set set2 = {};
2041
g_assert_true(string_set_add(&set2, "test_string2"));
2042
string_set_swap(&set1, &set2);
2043
g_assert_true(string_set_contains(set1, "test_string2"));
2044
g_assert_false(string_set_contains(set2, "test_string2"));
2047
static void test_string_set_swap_one_with_one(__attribute__((unused))
2048
test_fixture *fixture,
2049
__attribute__((unused))
2052
__attribute__((cleanup(string_set_clear)))
2053
string_set set1 = {};
2054
__attribute__((cleanup(string_set_clear)))
2055
string_set set2 = {};
2056
g_assert_true(string_set_add(&set1, "test_string1"));
2057
g_assert_true(string_set_add(&set2, "test_string2"));
2058
string_set_swap(&set1, &set2);
2059
g_assert_false(string_set_contains(set1, "test_string1"));
2060
g_assert_true(string_set_contains(set1, "test_string2"));
2061
g_assert_false(string_set_contains(set2, "test_string2"));
2062
g_assert_true(string_set_contains(set2, "test_string1"));
2065
static bool fd_has_cloexec_and_nonblock(const int);
2067
static bool epoll_set_contains(int, int, uint32_t);
2069
static void test_start_mandos_client(test_fixture *fixture,
2070
__attribute__((unused))
2071
gconstpointer user_data){
2073
bool mandos_client_exited = false;
2074
bool quit_now = false;
2075
__attribute__((cleanup(cleanup_close)))
2076
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2077
g_assert_cmpint(epoll_fd, >=, 0);
2078
__attribute__((cleanup(cleanup_queue)))
2079
task_queue *queue = create_queue();
2080
g_assert_nonnull(queue);
2081
buffer password = {};
2082
bool password_is_read = false;
2083
const char helper_directory[] = "/nonexistent";
2084
const char *const argv[] = { "/bin/true", NULL };
2086
g_assert_true(start_mandos_client(queue, epoll_fd,
2087
&mandos_client_exited, &quit_now,
2088
&password, &password_is_read,
2089
&fixture->orig_sigaction,
2090
fixture->orig_sigmask,
2091
helper_directory, 0, 0, argv));
2093
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2095
const task_context *const added_wait_task
2096
= find_matching_task(queue, (task_context){
2097
.func=wait_for_mandos_client_exit,
2098
.mandos_client_exited=&mandos_client_exited,
2099
.quit_now=&quit_now,
2101
g_assert_nonnull(added_wait_task);
2102
g_assert_cmpint(added_wait_task->pid, >, 0);
2103
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2104
waitpid(added_wait_task->pid, NULL, 0);
2106
const task_context *const added_read_task
2107
= find_matching_task(queue, (task_context){
2108
.func=read_mandos_client_output,
2110
.password=&password,
2111
.password_is_read=&password_is_read,
2112
.quit_now=&quit_now,
2114
g_assert_nonnull(added_read_task);
2115
g_assert_cmpint(added_read_task->fd, >, 2);
2116
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2117
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2118
EPOLLIN | EPOLLRDHUP));
2121
static bool fd_has_cloexec_and_nonblock(const int fd){
2122
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2123
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2124
return ((socket_fd_flags >= 0)
2125
and (socket_fd_flags & FD_CLOEXEC)
2126
and (socket_file_flags >= 0)
2127
and (socket_file_flags & O_NONBLOCK));
2130
__attribute__((const))
2131
bool is_privileged(void){
2132
uid_t user = getuid() + 1;
2133
if(user == 0){ /* Overflow check */
2136
gid_t group = getuid() + 1;
2137
if(group == 0){ /* Overflow check */
2140
const pid_t pid = fork();
2141
if(pid == 0){ /* Child */
2142
if(setresgid((uid_t)-1, group, group) == -1){
2144
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2145
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2149
if(setresuid((uid_t)-1, user, user) == -1){
2151
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2152
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2159
waitpid(pid, &status, 0);
2160
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2166
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2167
/* Only scan for events in this eventmask */
2168
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2169
__attribute__((cleanup(cleanup_string)))
2170
char *fdinfo_name = NULL;
2171
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2172
g_assert_cmpint(ret, >, 0);
2173
g_assert_nonnull(fdinfo_name);
2175
FILE *fdinfo = fopen(fdinfo_name, "r");
2176
g_assert_nonnull(fdinfo);
2177
uint32_t reported_events;
2182
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2185
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2186
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2187
&found_fd, &reported_events) == 2){
2192
} while(not feof(fdinfo) and not ferror(fdinfo));
2193
g_assert_cmpint(fclose(fdinfo), ==, 0);
2200
/* Don't check events if none are given */
2203
return (reported_events & eventmask) == (events & eventmask);
2206
static void test_start_mandos_client_execv(test_fixture *fixture,
2207
__attribute__((unused))
2208
gconstpointer user_data){
2209
bool mandos_client_exited = false;
2210
bool quit_now = false;
2211
__attribute__((cleanup(cleanup_close)))
2212
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2213
g_assert_cmpint(epoll_fd, >=, 0);
2214
__attribute__((cleanup(cleanup_queue)))
2215
task_queue *queue = create_queue();
2216
g_assert_nonnull(queue);
2217
__attribute__((cleanup(cleanup_buffer)))
2218
buffer password = {};
2219
const char helper_directory[] = "/nonexistent";
2220
/* Can't execv("/", ...), so this should fail */
2221
const char *const argv[] = { "/", NULL };
2224
__attribute__((cleanup(cleanup_close)))
2225
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2226
g_assert_cmpint(devnull_fd, >=, 0);
2227
__attribute__((cleanup(cleanup_close)))
2228
const int real_stderr_fd = dup(STDERR_FILENO);
2229
g_assert_cmpint(real_stderr_fd, >=, 0);
2230
dup2(devnull_fd, STDERR_FILENO);
2232
const bool success = start_mandos_client(queue, epoll_fd,
2233
&mandos_client_exited,
2237
&fixture->orig_sigaction,
2238
fixture->orig_sigmask,
2239
helper_directory, 0, 0,
2241
dup2(real_stderr_fd, STDERR_FILENO);
2242
g_assert_true(success);
2244
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2246
struct timespec starttime, currtime;
2247
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2249
queue->next_run = 0;
2250
string_set cancelled_filenames = {};
2253
__attribute__((cleanup(cleanup_close)))
2254
const int devnull_fd = open("/dev/null",
2255
O_WRONLY | O_CLOEXEC);
2256
g_assert_cmpint(devnull_fd, >=, 0);
2257
__attribute__((cleanup(cleanup_close)))
2258
const int real_stderr_fd = dup(STDERR_FILENO);
2259
g_assert_cmpint(real_stderr_fd, >=, 0);
2260
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2261
dup2(devnull_fd, STDERR_FILENO);
2262
const bool success = run_queue(&queue, &cancelled_filenames,
2264
dup2(real_stderr_fd, STDERR_FILENO);
2269
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2270
} while(((queue->length) > 0)
2272
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2274
g_assert_true(quit_now);
2275
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2276
g_assert_true(mandos_client_exited);
2279
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2280
__attribute__((unused))
2283
if(not is_privileged()){
2284
g_test_skip("Not privileged");
2288
bool mandos_client_exited = false;
2289
bool quit_now = false;
2290
__attribute__((cleanup(cleanup_close)))
2291
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2292
g_assert_cmpint(epoll_fd, >=, 0);
2293
__attribute__((cleanup(cleanup_queue)))
2294
task_queue *queue = create_queue();
2295
g_assert_nonnull(queue);
2296
__attribute__((cleanup(cleanup_buffer)))
2297
buffer password = {};
2298
bool password_is_read = false;
2299
const char helper_directory[] = "/nonexistent";
2300
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2304
const bool success = start_mandos_client(queue, epoll_fd,
2305
&mandos_client_exited,
2306
&quit_now, &password,
2308
&fixture->orig_sigaction,
2309
fixture->orig_sigmask,
2310
helper_directory, user,
2312
g_assert_true(success);
2313
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2315
struct timespec starttime, currtime;
2316
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2318
queue->next_run = 0;
2319
string_set cancelled_filenames = {};
2320
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2321
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2322
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2323
} while(((queue->length) > 0)
2325
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2327
g_assert_false(quit_now);
2328
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2329
g_assert_true(mandos_client_exited);
2331
g_assert_true(password_is_read);
2332
g_assert_nonnull(password.data);
2335
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2337
g_assert_true((uid_t)id == id);
2339
g_assert_cmpuint((unsigned int)id, ==, 0);
2342
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2343
__attribute__((unused))
2346
if(not is_privileged()){
2347
g_test_skip("Not privileged");
2351
bool mandos_client_exited = false;
2352
bool quit_now = false;
2353
__attribute__((cleanup(cleanup_close)))
2354
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2355
g_assert_cmpint(epoll_fd, >=, 0);
2356
__attribute__((cleanup(cleanup_queue)))
2357
task_queue *queue = create_queue();
2358
g_assert_nonnull(queue);
2359
__attribute__((cleanup(cleanup_buffer)))
2360
buffer password = {};
2361
bool password_is_read = false;
2362
const char helper_directory[] = "/nonexistent";
2363
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2367
const bool success = start_mandos_client(queue, epoll_fd,
2368
&mandos_client_exited,
2369
&quit_now, &password,
2371
&fixture->orig_sigaction,
2372
fixture->orig_sigmask,
2373
helper_directory, user,
2375
g_assert_true(success);
2376
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2378
struct timespec starttime, currtime;
2379
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2381
queue->next_run = 0;
2382
string_set cancelled_filenames = {};
2383
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2384
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2385
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2386
} while(((queue->length) > 0)
2388
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2390
g_assert_false(quit_now);
2391
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2392
g_assert_true(mandos_client_exited);
2394
g_assert_true(password_is_read);
2395
g_assert_nonnull(password.data);
2398
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2400
g_assert_true((gid_t)id == id);
2402
g_assert_cmpuint((unsigned int)id, ==, 0);
2405
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2406
__attribute__((unused))
2409
if(not is_privileged()){
2410
g_test_skip("Not privileged");
2414
bool mandos_client_exited = false;
2415
bool quit_now = false;
2416
__attribute__((cleanup(cleanup_close)))
2417
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2418
g_assert_cmpint(epoll_fd, >=, 0);
2419
__attribute__((cleanup(cleanup_queue)))
2420
task_queue *queue = create_queue();
2421
g_assert_nonnull(queue);
2422
__attribute__((cleanup(cleanup_buffer)))
2423
buffer password = {};
2424
bool password_is_read = false;
2425
const char helper_directory[] = "/nonexistent";
2426
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2431
const bool success = start_mandos_client(queue, epoll_fd,
2432
&mandos_client_exited,
2433
&quit_now, &password,
2435
&fixture->orig_sigaction,
2436
fixture->orig_sigmask,
2437
helper_directory, user,
2439
g_assert_true(success);
2440
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2442
struct timespec starttime, currtime;
2443
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2445
queue->next_run = 0;
2446
string_set cancelled_filenames = {};
2447
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2448
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2449
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2450
} while(((queue->length) > 0)
2452
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2454
g_assert_false(quit_now);
2455
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2456
g_assert_true(mandos_client_exited);
2458
g_assert_true(password_is_read);
2459
g_assert_nonnull(password.data);
2462
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2464
g_assert_true((uid_t)id == id);
2466
g_assert_cmpuint((unsigned int)id, ==, user);
2469
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2470
__attribute__((unused))
2473
if(not is_privileged()){
2474
g_test_skip("Not privileged");
2478
bool mandos_client_exited = false;
2479
bool quit_now = false;
2480
__attribute__((cleanup(cleanup_close)))
2481
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2482
g_assert_cmpint(epoll_fd, >=, 0);
2483
__attribute__((cleanup(cleanup_queue)))
2484
task_queue *queue = create_queue();
2485
g_assert_nonnull(queue);
2486
__attribute__((cleanup(cleanup_buffer)))
2487
buffer password = {};
2488
bool password_is_read = false;
2489
const char helper_directory[] = "/nonexistent";
2490
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2495
const bool success = start_mandos_client(queue, epoll_fd,
2496
&mandos_client_exited,
2497
&quit_now, &password,
2499
&fixture->orig_sigaction,
2500
fixture->orig_sigmask,
2501
helper_directory, user,
2503
g_assert_true(success);
2504
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2506
struct timespec starttime, currtime;
2507
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2509
queue->next_run = 0;
2510
string_set cancelled_filenames = {};
2511
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2512
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2513
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2514
} while(((queue->length) > 0)
2516
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2518
g_assert_false(quit_now);
2519
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2520
g_assert_true(mandos_client_exited);
2522
g_assert_true(password_is_read);
2523
g_assert_nonnull(password.data);
2526
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2528
g_assert_true((gid_t)id == id);
2530
g_assert_cmpuint((unsigned int)id, ==, group);
2533
static void test_start_mandos_client_read(test_fixture *fixture,
2534
__attribute__((unused))
2535
gconstpointer user_data){
2536
bool mandos_client_exited = false;
2537
bool quit_now = false;
2538
__attribute__((cleanup(cleanup_close)))
2539
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2540
g_assert_cmpint(epoll_fd, >=, 0);
2541
__attribute__((cleanup(cleanup_queue)))
2542
task_queue *queue = create_queue();
2543
g_assert_nonnull(queue);
2544
__attribute__((cleanup(cleanup_buffer)))
2545
buffer password = {};
2546
bool password_is_read = false;
2547
const char dummy_test_password[] = "dummy test password";
2548
const char helper_directory[] = "/nonexistent";
2549
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2552
const bool success = start_mandos_client(queue, epoll_fd,
2553
&mandos_client_exited,
2554
&quit_now, &password,
2556
&fixture->orig_sigaction,
2557
fixture->orig_sigmask,
2558
helper_directory, 0, 0,
2560
g_assert_true(success);
2561
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2563
struct timespec starttime, currtime;
2564
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2566
queue->next_run = 0;
2567
string_set cancelled_filenames = {};
2568
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2569
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2570
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2571
} while(((queue->length) > 0)
2573
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2575
g_assert_false(quit_now);
2576
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2577
g_assert_true(mandos_client_exited);
2579
g_assert_true(password_is_read);
2580
g_assert_cmpint((int)password.length, ==,
2581
sizeof(dummy_test_password)-1);
2582
g_assert_nonnull(password.data);
2583
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2584
sizeof(dummy_test_password)-1), ==, 0);
2588
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2589
__attribute__((unused))
2592
bool mandos_client_exited = false;
2593
bool quit_now = false;
2594
__attribute__((cleanup(cleanup_close)))
2595
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2596
g_assert_cmpint(epoll_fd, >=, 0);
2597
__attribute__((cleanup(cleanup_queue)))
2598
task_queue *queue = create_queue();
2599
g_assert_nonnull(queue);
2600
__attribute__((cleanup(cleanup_buffer)))
2601
buffer password = {};
2602
bool password_is_read = false;
2603
const char helper_directory[] = "/nonexistent";
2604
const char *const argv[] = { "/bin/sh", "-c",
2605
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2607
const bool success = start_mandos_client(queue, epoll_fd,
2608
&mandos_client_exited,
2609
&quit_now, &password,
2611
&fixture->orig_sigaction,
2612
fixture->orig_sigmask,
2613
helper_directory, 0, 0,
2615
g_assert_true(success);
2616
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2618
struct timespec starttime, currtime;
2619
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2621
queue->next_run = 0;
2622
string_set cancelled_filenames = {};
2623
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2624
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2625
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2626
} while(((queue->length) > 0)
2628
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2630
g_assert_false(quit_now);
2631
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2632
g_assert_true(mandos_client_exited);
2634
g_assert_true(password_is_read);
2635
g_assert_cmpint((int)password.length, ==,
2636
sizeof(helper_directory)-1);
2637
g_assert_nonnull(password.data);
2638
g_assert_cmpint(memcmp(helper_directory, password.data,
2639
sizeof(helper_directory)-1), ==, 0);
2642
__attribute__((nonnull, warn_unused_result))
2643
static bool proc_status_sigblk_to_sigset(const char *const,
2646
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2647
__attribute__((unused))
2648
gconstpointer user_data){
2649
bool mandos_client_exited = false;
2650
bool quit_now = false;
2651
__attribute__((cleanup(cleanup_close)))
2652
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2653
g_assert_cmpint(epoll_fd, >=, 0);
2654
__attribute__((cleanup(cleanup_queue)))
2655
task_queue *queue = create_queue();
2656
g_assert_nonnull(queue);
2657
__attribute__((cleanup(cleanup_buffer)))
2658
buffer password = {};
2659
bool password_is_read = false;
2660
const char helper_directory[] = "/nonexistent";
2661
/* see proc(5) for format of /proc/self/status */
2662
const char *const argv[] = { "/usr/bin/awk",
2663
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2665
g_assert_true(start_mandos_client(queue, epoll_fd,
2666
&mandos_client_exited, &quit_now,
2667
&password, &password_is_read,
2668
&fixture->orig_sigaction,
2669
fixture->orig_sigmask,
2670
helper_directory, 0, 0, argv));
2672
struct timespec starttime, currtime;
2673
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2675
queue->next_run = 0;
2676
string_set cancelled_filenames = {};
2677
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2678
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2679
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2680
} while((not (mandos_client_exited and password_is_read))
2682
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2683
g_assert_true(mandos_client_exited);
2684
g_assert_true(password_is_read);
2686
sigset_t parsed_sigmask;
2687
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2690
for(int signum = 1; signum < NSIG; signum++){
2691
const bool has_signal = sigismember(&parsed_sigmask, signum);
2692
if(sigismember(&fixture->orig_sigmask, signum)){
2693
g_assert_true(has_signal);
2695
g_assert_false(has_signal);
2700
__attribute__((nonnull, warn_unused_result))
2701
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2702
sigset_t *const sigmask){
2703
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2704
uintmax_t scanned_sigmask;
2705
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2708
if(sigemptyset(sigmask) != 0){
2711
for(int signum = 1; signum < NSIG; signum++){
2712
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2713
if(sigaddset(sigmask, signum) != 0){
2721
static void run_task_with_stderr_to_dev_null(const task_context task,
2722
task_queue *const queue);
2725
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2726
test_fixture *fixture,
2727
__attribute__((unused))
2728
gconstpointer user_data){
2730
bool mandos_client_exited = false;
2731
bool quit_now = false;
2733
__attribute__((cleanup(cleanup_queue)))
2734
task_queue *queue = create_queue();
2735
g_assert_nonnull(queue);
2736
const task_context task = {
2737
.func=wait_for_mandos_client_exit,
2739
.mandos_client_exited=&mandos_client_exited,
2740
.quit_now=&quit_now,
2742
run_task_with_stderr_to_dev_null(task, queue);
2744
g_assert_false(mandos_client_exited);
2745
g_assert_true(quit_now);
2746
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2749
static void run_task_with_stderr_to_dev_null(const task_context task,
2750
task_queue *const queue){
2751
FILE *real_stderr = stderr;
2752
FILE *devnull = fopen("/dev/null", "we");
2753
g_assert_nonnull(devnull);
2756
task.func(task, queue);
2757
stderr = real_stderr;
2759
g_assert_cmpint(fclose(devnull), ==, 0);
2763
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2764
__attribute__((unused))
2765
gconstpointer user_data){
2766
bool mandos_client_exited = false;
2767
bool quit_now = false;
2769
pid_t create_eternal_process(void){
2770
const pid_t pid = fork();
2771
if(pid == 0){ /* Child */
2772
if(not restore_signal_handler(&fixture->orig_sigaction)){
2773
_exit(EXIT_FAILURE);
2775
if(not restore_sigmask(&fixture->orig_sigmask)){
2776
_exit(EXIT_FAILURE);
2784
pid_t pid = create_eternal_process();
2785
g_assert_true(pid != -1);
2787
__attribute__((cleanup(cleanup_queue)))
2788
task_queue *queue = create_queue();
2789
g_assert_nonnull(queue);
2790
const task_context task = {
2791
.func=wait_for_mandos_client_exit,
2793
.mandos_client_exited=&mandos_client_exited,
2794
.quit_now=&quit_now,
2796
task.func(task, queue);
2798
g_assert_false(mandos_client_exited);
2799
g_assert_false(quit_now);
2800
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2802
g_assert_nonnull(find_matching_task(queue, (task_context){
2803
.func=wait_for_mandos_client_exit,
2805
.mandos_client_exited=&mandos_client_exited,
2806
.quit_now=&quit_now,
2811
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2812
__attribute__((unused))
2815
bool mandos_client_exited = false;
2816
bool quit_now = false;
2818
pid_t create_successful_process(void){
2819
const pid_t pid = fork();
2820
if(pid == 0){ /* Child */
2821
if(not restore_signal_handler(&fixture->orig_sigaction)){
2822
_exit(EXIT_FAILURE);
2824
if(not restore_sigmask(&fixture->orig_sigmask)){
2825
_exit(EXIT_FAILURE);
2831
const pid_t pid = create_successful_process();
2832
g_assert_true(pid != -1);
2834
__attribute__((cleanup(cleanup_queue)))
2835
task_queue *queue = create_queue();
2836
g_assert_nonnull(queue);
2837
const task_context initial_task = {
2838
.func=wait_for_mandos_client_exit,
2840
.mandos_client_exited=&mandos_client_exited,
2841
.quit_now=&quit_now,
2843
g_assert_true(add_to_queue(queue, initial_task));
2845
struct timespec starttime, currtime;
2846
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2847
__attribute__((cleanup(cleanup_close)))
2848
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2850
queue->next_run = 0;
2851
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2852
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2853
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2854
} while((not mandos_client_exited)
2856
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2858
g_assert_true(mandos_client_exited);
2859
g_assert_false(quit_now);
2860
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2864
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2865
__attribute__((unused))
2868
bool mandos_client_exited = false;
2869
bool quit_now = false;
2871
pid_t create_failing_process(void){
2872
const pid_t pid = fork();
2873
if(pid == 0){ /* Child */
2874
if(not restore_signal_handler(&fixture->orig_sigaction)){
2875
_exit(EXIT_FAILURE);
2877
if(not restore_sigmask(&fixture->orig_sigmask)){
2878
_exit(EXIT_FAILURE);
2884
const pid_t pid = create_failing_process();
2885
g_assert_true(pid != -1);
2887
__attribute__((cleanup(string_set_clear)))
2888
string_set cancelled_filenames = {};
2889
__attribute__((cleanup(cleanup_close)))
2890
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2891
g_assert_cmpint(epoll_fd, >=, 0);
2892
__attribute__((cleanup(cleanup_queue)))
2893
task_queue *queue = create_queue();
2894
g_assert_nonnull(queue);
2895
g_assert_true(add_to_queue(queue, (task_context){
2896
.func=wait_for_mandos_client_exit,
2898
.mandos_client_exited=&mandos_client_exited,
2899
.quit_now=&quit_now,
2902
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2904
__attribute__((cleanup(cleanup_close)))
2905
const int devnull_fd = open("/dev/null",
2906
O_WRONLY | O_CLOEXEC);
2907
g_assert_cmpint(devnull_fd, >=, 0);
2908
__attribute__((cleanup(cleanup_close)))
2909
const int real_stderr_fd = dup(STDERR_FILENO);
2910
g_assert_cmpint(real_stderr_fd, >=, 0);
2912
struct timespec starttime, currtime;
2913
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2915
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2916
dup2(devnull_fd, STDERR_FILENO);
2917
const bool success = run_queue(&queue, &cancelled_filenames,
2919
dup2(real_stderr_fd, STDERR_FILENO);
2924
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2925
} while((not mandos_client_exited)
2927
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2929
g_assert_true(quit_now);
2930
g_assert_true(mandos_client_exited);
2931
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2935
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2936
__attribute__((unused))
2937
gconstpointer user_data){
2938
bool mandos_client_exited = false;
2939
bool quit_now = false;
2941
pid_t create_killed_process(void){
2942
const pid_t pid = fork();
2943
if(pid == 0){ /* Child */
2944
if(not restore_signal_handler(&fixture->orig_sigaction)){
2945
_exit(EXIT_FAILURE);
2947
if(not restore_sigmask(&fixture->orig_sigmask)){
2948
_exit(EXIT_FAILURE);
2957
const pid_t pid = create_killed_process();
2958
g_assert_true(pid != -1);
2960
__attribute__((cleanup(string_set_clear)))
2961
string_set cancelled_filenames = {};
2962
__attribute__((cleanup(cleanup_close)))
2963
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2964
g_assert_cmpint(epoll_fd, >=, 0);
2965
__attribute__((cleanup(cleanup_queue)))
2966
task_queue *queue = create_queue();
2967
g_assert_nonnull(queue);
2968
g_assert_true(add_to_queue(queue, (task_context){
2969
.func=wait_for_mandos_client_exit,
2971
.mandos_client_exited=&mandos_client_exited,
2972
.quit_now=&quit_now,
2975
__attribute__((cleanup(cleanup_close)))
2976
const int devnull_fd = open("/dev/null",
2977
O_WRONLY | O_CLOEXEC);
2978
g_assert_cmpint(devnull_fd, >=, 0);
2979
__attribute__((cleanup(cleanup_close)))
2980
const int real_stderr_fd = dup(STDERR_FILENO);
2981
g_assert_cmpint(real_stderr_fd, >=, 0);
2983
struct timespec starttime, currtime;
2984
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2986
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2987
dup2(devnull_fd, STDERR_FILENO);
2988
const bool success = run_queue(&queue, &cancelled_filenames,
2990
dup2(real_stderr_fd, STDERR_FILENO);
2995
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2996
} while((not mandos_client_exited)
2998
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3000
g_assert_true(mandos_client_exited);
3001
g_assert_true(quit_now);
3002
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3005
static bool epoll_set_does_not_contain(int, int);
3008
void test_read_mandos_client_output_readerror(__attribute__((unused))
3009
test_fixture *fixture,
3010
__attribute__((unused))
3013
__attribute__((cleanup(cleanup_close)))
3014
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3015
g_assert_cmpint(epoll_fd, >=, 0);
3017
__attribute__((cleanup(cleanup_buffer)))
3018
buffer password = {};
3020
/* Reading /proc/self/mem from offset 0 will always give EIO */
3021
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3023
bool password_is_read = false;
3024
bool quit_now = false;
3025
__attribute__((cleanup(cleanup_queue)))
3026
task_queue *queue = create_queue();
3027
g_assert_nonnull(queue);
3029
task_context task = {
3030
.func=read_mandos_client_output,
3033
.password=&password,
3034
.password_is_read=&password_is_read,
3035
.quit_now=&quit_now,
3037
run_task_with_stderr_to_dev_null(task, queue);
3038
g_assert_false(password_is_read);
3039
g_assert_cmpint((int)password.length, ==, 0);
3040
g_assert_true(quit_now);
3041
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3043
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3045
g_assert_cmpint(close(fd), ==, -1);
3048
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3049
return not epoll_set_contains(epoll_fd, fd, 0);
3053
void test_read_mandos_client_output_nodata(__attribute__((unused))
3054
test_fixture *fixture,
3055
__attribute__((unused))
3056
gconstpointer user_data){
3057
__attribute__((cleanup(cleanup_close)))
3058
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3059
g_assert_cmpint(epoll_fd, >=, 0);
3062
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3064
__attribute__((cleanup(cleanup_buffer)))
3065
buffer password = {};
3067
bool password_is_read = false;
3068
bool quit_now = false;
3069
__attribute__((cleanup(cleanup_queue)))
3070
task_queue *queue = create_queue();
3071
g_assert_nonnull(queue);
3073
task_context task = {
3074
.func=read_mandos_client_output,
3077
.password=&password,
3078
.password_is_read=&password_is_read,
3079
.quit_now=&quit_now,
3081
task.func(task, queue);
3082
g_assert_false(password_is_read);
3083
g_assert_cmpint((int)password.length, ==, 0);
3084
g_assert_false(quit_now);
3085
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3087
g_assert_nonnull(find_matching_task(queue, (task_context){
3088
.func=read_mandos_client_output,
3091
.password=&password,
3092
.password_is_read=&password_is_read,
3093
.quit_now=&quit_now,
3096
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3097
EPOLLIN | EPOLLRDHUP));
3099
g_assert_cmpint(close(pipefds[1]), ==, 0);
3102
static void test_read_mandos_client_output_eof(__attribute__((unused))
3103
test_fixture *fixture,
3104
__attribute__((unused))
3107
__attribute__((cleanup(cleanup_close)))
3108
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3109
g_assert_cmpint(epoll_fd, >=, 0);
3112
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3113
g_assert_cmpint(close(pipefds[1]), ==, 0);
3115
__attribute__((cleanup(cleanup_buffer)))
3116
buffer password = {};
3118
bool password_is_read = false;
3119
bool quit_now = false;
3120
__attribute__((cleanup(cleanup_queue)))
3121
task_queue *queue = create_queue();
3122
g_assert_nonnull(queue);
3124
task_context task = {
3125
.func=read_mandos_client_output,
3128
.password=&password,
3129
.password_is_read=&password_is_read,
3130
.quit_now=&quit_now,
3132
task.func(task, queue);
3133
g_assert_true(password_is_read);
3134
g_assert_cmpint((int)password.length, ==, 0);
3135
g_assert_false(quit_now);
3136
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3138
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3140
g_assert_cmpint(close(pipefds[0]), ==, -1);
3144
void test_read_mandos_client_output_once(__attribute__((unused))
3145
test_fixture *fixture,
3146
__attribute__((unused))
3147
gconstpointer user_data){
3148
__attribute__((cleanup(cleanup_close)))
3149
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3150
g_assert_cmpint(epoll_fd, >=, 0);
3153
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3155
const char dummy_test_password[] = "dummy test password";
3156
/* Start with a pre-allocated buffer */
3157
__attribute__((cleanup(cleanup_buffer)))
3159
.data=malloc(sizeof(dummy_test_password)),
3161
.allocated=sizeof(dummy_test_password),
3163
g_assert_nonnull(password.data);
3164
if(mlock(password.data, password.allocated) != 0){
3165
g_assert_true(errno == EPERM or errno == ENOMEM);
3168
bool password_is_read = false;
3169
bool quit_now = false;
3170
__attribute__((cleanup(cleanup_queue)))
3171
task_queue *queue = create_queue();
3172
g_assert_nonnull(queue);
3174
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3175
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3176
sizeof(dummy_test_password)),
3177
==, (int)sizeof(dummy_test_password));
3179
task_context task = {
3180
.func=read_mandos_client_output,
3183
.password=&password,
3184
.password_is_read=&password_is_read,
3185
.quit_now=&quit_now,
3187
task.func(task, queue);
3189
g_assert_false(password_is_read);
3190
g_assert_cmpint((int)password.length, ==,
3191
(int)sizeof(dummy_test_password));
3192
g_assert_nonnull(password.data);
3193
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3194
sizeof(dummy_test_password)), ==, 0);
3196
g_assert_false(quit_now);
3197
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3199
g_assert_nonnull(find_matching_task(queue, (task_context){
3200
.func=read_mandos_client_output,
3203
.password=&password,
3204
.password_is_read=&password_is_read,
3205
.quit_now=&quit_now,
3208
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3209
EPOLLIN | EPOLLRDHUP));
3211
g_assert_cmpint(close(pipefds[1]), ==, 0);
3215
void test_read_mandos_client_output_malloc(__attribute__((unused))
3216
test_fixture *fixture,
3217
__attribute__((unused))
3218
gconstpointer user_data){
3219
__attribute__((cleanup(cleanup_close)))
3220
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3221
g_assert_cmpint(epoll_fd, >=, 0);
3224
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3226
const char dummy_test_password[] = "dummy test password";
3227
/* Start with an empty buffer */
3228
__attribute__((cleanup(cleanup_buffer)))
3229
buffer password = {};
3231
bool password_is_read = false;
3232
bool quit_now = false;
3233
__attribute__((cleanup(cleanup_queue)))
3234
task_queue *queue = create_queue();
3235
g_assert_nonnull(queue);
3237
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3238
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3239
sizeof(dummy_test_password)),
3240
==, (int)sizeof(dummy_test_password));
3242
task_context task = {
3243
.func=read_mandos_client_output,
3246
.password=&password,
3247
.password_is_read=&password_is_read,
3248
.quit_now=&quit_now,
3250
task.func(task, queue);
3252
g_assert_false(password_is_read);
3253
g_assert_cmpint((int)password.length, ==,
3254
(int)sizeof(dummy_test_password));
3255
g_assert_nonnull(password.data);
3256
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3257
sizeof(dummy_test_password)), ==, 0);
3259
g_assert_false(quit_now);
3260
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3262
g_assert_nonnull(find_matching_task(queue, (task_context){
3263
.func=read_mandos_client_output,
3266
.password=&password,
3267
.password_is_read=&password_is_read,
3268
.quit_now=&quit_now,
3271
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3272
EPOLLIN | EPOLLRDHUP));
3274
g_assert_cmpint(close(pipefds[1]), ==, 0);
3278
void test_read_mandos_client_output_append(__attribute__((unused))
3279
test_fixture *fixture,
3280
__attribute__((unused))
3281
gconstpointer user_data){
3282
__attribute__((cleanup(cleanup_close)))
3283
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3284
g_assert_cmpint(epoll_fd, >=, 0);
3287
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3289
const char dummy_test_password[] = "dummy test password";
3290
__attribute__((cleanup(cleanup_buffer)))
3292
.data=malloc(PIPE_BUF),
3294
.allocated=PIPE_BUF,
3296
g_assert_nonnull(password.data);
3297
if(mlock(password.data, password.allocated) != 0){
3298
g_assert_true(errno == EPERM or errno == ENOMEM);
3301
memset(password.data, 'x', PIPE_BUF);
3302
char password_expected[PIPE_BUF];
3303
memcpy(password_expected, password.data, PIPE_BUF);
3305
bool password_is_read = false;
3306
bool quit_now = false;
3307
__attribute__((cleanup(cleanup_queue)))
3308
task_queue *queue = create_queue();
3309
g_assert_nonnull(queue);
3311
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3312
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3313
sizeof(dummy_test_password)),
3314
==, (int)sizeof(dummy_test_password));
3316
task_context task = {
3317
.func=read_mandos_client_output,
3320
.password=&password,
3321
.password_is_read=&password_is_read,
3322
.quit_now=&quit_now,
3324
task.func(task, queue);
3326
g_assert_false(password_is_read);
3327
g_assert_cmpint((int)password.length, ==,
3328
PIPE_BUF + sizeof(dummy_test_password));
3329
g_assert_nonnull(password.data);
3330
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3332
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3333
dummy_test_password,
3334
sizeof(dummy_test_password)), ==, 0);
3335
g_assert_false(quit_now);
3336
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3338
g_assert_nonnull(find_matching_task(queue, (task_context){
3339
.func=read_mandos_client_output,
3342
.password=&password,
3343
.password_is_read=&password_is_read,
3344
.quit_now=&quit_now,
3347
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3348
EPOLLIN | EPOLLRDHUP));
3351
static char *make_temporary_directory(void);
3353
static void test_add_inotify_dir_watch(__attribute__((unused))
3354
test_fixture *fixture,
3355
__attribute__((unused))
3356
gconstpointer user_data){
3357
__attribute__((cleanup(cleanup_close)))
3358
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3359
g_assert_cmpint(epoll_fd, >=, 0);
3360
__attribute__((cleanup(cleanup_queue)))
3361
task_queue *queue = create_queue();
3362
g_assert_nonnull(queue);
3363
__attribute__((cleanup(string_set_clear)))
3364
string_set cancelled_filenames = {};
3365
const mono_microsecs current_time = 0;
3367
bool quit_now = false;
3368
buffer password = {};
3369
bool mandos_client_exited = false;
3370
bool password_is_read = false;
3372
__attribute__((cleanup(cleanup_string)))
3373
char *tempdir = make_temporary_directory();
3374
g_assert_nonnull(tempdir);
3376
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3378
&cancelled_filenames,
3380
&mandos_client_exited,
3381
&password_is_read));
3383
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3385
const task_context *const added_read_task
3386
= find_matching_task(queue, (task_context){
3387
.func=read_inotify_event,
3389
.quit_now=&quit_now,
3390
.password=&password,
3392
.cancelled_filenames=&cancelled_filenames,
3393
.current_time=¤t_time,
3394
.mandos_client_exited=&mandos_client_exited,
3395
.password_is_read=&password_is_read,
3397
g_assert_nonnull(added_read_task);
3399
g_assert_cmpint(added_read_task->fd, >, 2);
3400
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3401
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3402
added_read_task->fd,
3403
EPOLLIN | EPOLLRDHUP));
3405
g_assert_cmpint(rmdir(tempdir), ==, 0);
3408
static char *make_temporary_directory(void){
3409
char *name = strdup("/tmp/mandosXXXXXX");
3410
g_assert_nonnull(name);
3411
char *result = mkdtemp(name);
3418
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3419
test_fixture *fixture,
3420
__attribute__((unused))
3421
gconstpointer user_data){
3422
__attribute__((cleanup(cleanup_close)))
3423
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3424
g_assert_cmpint(epoll_fd, >=, 0);
3425
__attribute__((cleanup(cleanup_queue)))
3426
task_queue *queue = create_queue();
3427
g_assert_nonnull(queue);
3428
__attribute__((cleanup(string_set_clear)))
3429
string_set cancelled_filenames = {};
3430
const mono_microsecs current_time = 0;
3432
bool quit_now = false;
3433
buffer password = {};
3434
bool mandos_client_exited = false;
3435
bool password_is_read = false;
3437
const char nonexistent_dir[] = "/nonexistent";
3439
FILE *real_stderr = stderr;
3440
FILE *devnull = fopen("/dev/null", "we");
3441
g_assert_nonnull(devnull);
3443
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3444
&password, nonexistent_dir,
3445
&cancelled_filenames,
3447
&mandos_client_exited,
3448
&password_is_read));
3449
stderr = real_stderr;
3450
g_assert_cmpint(fclose(devnull), ==, 0);
3452
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3456
test_fixture *fixture,
3457
__attribute__((unused))
3460
__attribute__((cleanup(cleanup_close)))
3461
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3462
g_assert_cmpint(epoll_fd, >=, 0);
3463
__attribute__((cleanup(cleanup_queue)))
3464
task_queue *queue = create_queue();
3465
g_assert_nonnull(queue);
3466
__attribute__((cleanup(string_set_clear)))
3467
string_set cancelled_filenames = {};
3468
const mono_microsecs current_time = 0;
3470
bool quit_now = false;
3471
buffer password = {};
3472
bool mandos_client_exited = false;
3473
bool password_is_read = false;
3475
__attribute__((cleanup(cleanup_string)))
3476
char *tempdir = make_temporary_directory();
3477
g_assert_nonnull(tempdir);
3479
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3481
&cancelled_filenames,
3483
&mandos_client_exited,
3484
&password_is_read));
3486
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3488
const task_context *const added_read_task
3489
= find_matching_task(queue,
3490
(task_context){ .func=read_inotify_event });
3491
g_assert_nonnull(added_read_task);
3493
g_assert_cmpint(added_read_task->fd, >, 2);
3494
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3496
/* "sufficient to read at least one event." - inotify(7) */
3497
const size_t ievent_size = (sizeof(struct inotify_event)
3499
struct inotify_event *ievent = malloc(ievent_size);
3500
g_assert_nonnull(ievent);
3502
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3504
g_assert_cmpint(errno, ==, EAGAIN);
3508
g_assert_cmpint(rmdir(tempdir), ==, 0);
3511
static char *make_temporary_file_in_directory(const char
3515
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3516
test_fixture *fixture,
3517
__attribute__((unused))
3520
__attribute__((cleanup(cleanup_close)))
3521
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3522
g_assert_cmpint(epoll_fd, >=, 0);
3523
__attribute__((cleanup(cleanup_queue)))
3524
task_queue *queue = create_queue();
3525
g_assert_nonnull(queue);
3526
__attribute__((cleanup(string_set_clear)))
3527
string_set cancelled_filenames = {};
3528
const mono_microsecs current_time = 0;
3530
bool quit_now = false;
3531
buffer password = {};
3532
bool mandos_client_exited = false;
3533
bool password_is_read = false;
3535
__attribute__((cleanup(cleanup_string)))
3536
char *tempdir = make_temporary_directory();
3537
g_assert_nonnull(tempdir);
3539
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3541
&cancelled_filenames,
3543
&mandos_client_exited,
3544
&password_is_read));
3546
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3548
const task_context *const added_read_task
3549
= find_matching_task(queue,
3550
(task_context){ .func=read_inotify_event });
3551
g_assert_nonnull(added_read_task);
3553
g_assert_cmpint(added_read_task->fd, >, 2);
3554
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3556
__attribute__((cleanup(cleanup_string)))
3557
char *filename = make_temporary_file_in_directory(tempdir);
3558
g_assert_nonnull(filename);
3560
/* "sufficient to read at least one event." - inotify(7) */
3561
const size_t ievent_size = (sizeof(struct inotify_event)
3563
struct inotify_event *ievent = malloc(ievent_size);
3564
g_assert_nonnull(ievent);
3566
ssize_t read_size = 0;
3567
read_size = read(added_read_task->fd, ievent, ievent_size);
3569
g_assert_cmpint((int)read_size, >, 0);
3570
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3571
g_assert_cmpstr(ievent->name, ==, basename(filename));
3575
g_assert_cmpint(unlink(filename), ==, 0);
3576
g_assert_cmpint(rmdir(tempdir), ==, 0);
3579
static char *make_temporary_prefixed_file_in_directory(const char
3583
char *filename = NULL;
3584
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3586
g_assert_nonnull(filename);
3587
const int fd = mkostemp(filename, O_CLOEXEC);
3588
g_assert_cmpint(fd, >=, 0);
3589
g_assert_cmpint(close(fd), ==, 0);
3593
static char *make_temporary_file_in_directory(const char
3595
return make_temporary_prefixed_file_in_directory("temp", dir);
3599
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3600
test_fixture *fixture,
3601
__attribute__((unused))
3602
gconstpointer user_data){
3603
__attribute__((cleanup(cleanup_close)))
3604
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3605
g_assert_cmpint(epoll_fd, >=, 0);
3606
__attribute__((cleanup(cleanup_queue)))
3607
task_queue *queue = create_queue();
3608
g_assert_nonnull(queue);
3609
__attribute__((cleanup(string_set_clear)))
3610
string_set cancelled_filenames = {};
3611
const mono_microsecs current_time = 0;
3613
bool quit_now = false;
3614
buffer password = {};
3615
bool mandos_client_exited = false;
3616
bool password_is_read = false;
3618
__attribute__((cleanup(cleanup_string)))
3619
char *watchdir = make_temporary_directory();
3620
g_assert_nonnull(watchdir);
3622
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3623
&password, watchdir,
3624
&cancelled_filenames,
3626
&mandos_client_exited,
3627
&password_is_read));
3629
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3631
const task_context *const added_read_task
3632
= find_matching_task(queue,
3633
(task_context){ .func=read_inotify_event });
3634
g_assert_nonnull(added_read_task);
3636
g_assert_cmpint(added_read_task->fd, >, 2);
3637
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3639
char *sourcedir = make_temporary_directory();
3640
g_assert_nonnull(sourcedir);
3642
__attribute__((cleanup(cleanup_string)))
3643
char *filename = make_temporary_file_in_directory(sourcedir);
3644
g_assert_nonnull(filename);
3646
__attribute__((cleanup(cleanup_string)))
3647
char *targetfilename = NULL;
3648
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3649
basename(filename)), >, 0);
3650
g_assert_nonnull(targetfilename);
3652
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3653
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3656
/* "sufficient to read at least one event." - inotify(7) */
3657
const size_t ievent_size = (sizeof(struct inotify_event)
3659
struct inotify_event *ievent = malloc(ievent_size);
3660
g_assert_nonnull(ievent);
3662
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3664
g_assert_cmpint((int)read_size, >, 0);
3665
g_assert_true(ievent->mask & IN_MOVED_TO);
3666
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3670
g_assert_cmpint(unlink(targetfilename), ==, 0);
3671
g_assert_cmpint(rmdir(watchdir), ==, 0);
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3676
test_fixture *fixture,
3677
__attribute__((unused))
3678
gconstpointer user_data){
3679
__attribute__((cleanup(cleanup_close)))
3680
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3681
g_assert_cmpint(epoll_fd, >=, 0);
3682
__attribute__((cleanup(cleanup_queue)))
3683
task_queue *queue = create_queue();
3684
g_assert_nonnull(queue);
3685
__attribute__((cleanup(string_set_clear)))
3686
string_set cancelled_filenames = {};
3687
const mono_microsecs current_time = 0;
3689
bool quit_now = false;
3690
buffer password = {};
3691
bool mandos_client_exited = false;
3692
bool password_is_read = false;
3694
__attribute__((cleanup(cleanup_string)))
3695
char *tempdir = make_temporary_directory();
3696
g_assert_nonnull(tempdir);
3698
__attribute__((cleanup(cleanup_string)))
3699
char *tempfile = make_temporary_file_in_directory(tempdir);
3700
g_assert_nonnull(tempfile);
3702
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3704
&cancelled_filenames,
3706
&mandos_client_exited,
3707
&password_is_read));
3708
g_assert_cmpint(unlink(tempfile), ==, 0);
3710
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3712
const task_context *const added_read_task
3713
= find_matching_task(queue,
3714
(task_context){ .func=read_inotify_event });
3715
g_assert_nonnull(added_read_task);
3717
g_assert_cmpint(added_read_task->fd, >, 2);
3718
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3720
/* "sufficient to read at least one event." - inotify(7) */
3721
const size_t ievent_size = (sizeof(struct inotify_event)
3723
struct inotify_event *ievent = malloc(ievent_size);
3724
g_assert_nonnull(ievent);
3726
ssize_t read_size = 0;
3727
read_size = read(added_read_task->fd, ievent, ievent_size);
3729
g_assert_cmpint((int)read_size, >, 0);
3730
g_assert_true(ievent->mask & IN_DELETE);
3731
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3735
g_assert_cmpint(rmdir(tempdir), ==, 0);
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3739
test_fixture *fixture,
3740
__attribute__((unused))
3743
__attribute__((cleanup(cleanup_close)))
3744
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3745
g_assert_cmpint(epoll_fd, >=, 0);
3746
const mono_microsecs current_time = 0;
3748
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3749
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3751
bool quit_now = false;
3752
__attribute__((cleanup(cleanup_queue)))
3753
task_queue *queue = create_queue();
3754
g_assert_nonnull(queue);
3756
task_context task = {
3757
.func=read_inotify_event,
3760
.quit_now=&quit_now,
3761
.filename=strdup("/nonexistent"),
3762
.cancelled_filenames = &(string_set){},
3764
.current_time=¤t_time,
3766
g_assert_nonnull(task.filename);
3767
run_task_with_stderr_to_dev_null(task, queue);
3768
g_assert_true(quit_now);
3769
g_assert_true(queue->next_run == 0);
3770
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3772
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3774
g_assert_cmpint(close(fd), ==, -1);
3777
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3778
test_fixture *fixture,
3779
__attribute__((unused))
3782
const mono_microsecs current_time = 17;
3785
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3786
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3788
bool quit_now = false;
3789
buffer password = {};
3790
bool mandos_client_exited = false;
3791
bool password_is_read = false;
3792
__attribute__((cleanup(cleanup_queue)))
3793
task_queue *queue = create_queue();
3794
g_assert_nonnull(queue);
3796
task_context task = {
3797
.func=read_inotify_event,
3800
.quit_now=&quit_now,
3801
.password=&password,
3802
.filename=strdup("/nonexistent"),
3803
.cancelled_filenames = &(string_set){},
3805
.current_time=¤t_time,
3806
.mandos_client_exited=&mandos_client_exited,
3807
.password_is_read=&password_is_read,
3809
g_assert_nonnull(task.filename);
3810
run_task_with_stderr_to_dev_null(task, queue);
3812
g_assert_nonnull(find_matching_task(queue, task));
3813
g_assert_true(queue->next_run == 1000000 + current_time);
3815
g_assert_cmpint(close(pipefds[0]), ==, 0);
3816
g_assert_cmpint(close(pipefds[1]), ==, 0);
3819
static void test_read_inotify_event_nodata(__attribute__((unused))
3820
test_fixture *fixture,
3821
__attribute__((unused))
3822
gconstpointer user_data){
3823
__attribute__((cleanup(cleanup_close)))
3824
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3825
g_assert_cmpint(epoll_fd, >=, 0);
3826
const mono_microsecs current_time = 0;
3829
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3831
bool quit_now = false;
3832
buffer password = {};
3833
bool mandos_client_exited = false;
3834
bool password_is_read = false;
3835
__attribute__((cleanup(cleanup_queue)))
3836
task_queue *queue = create_queue();
3837
g_assert_nonnull(queue);
3839
task_context task = {
3840
.func=read_inotify_event,
3843
.quit_now=&quit_now,
3844
.password=&password,
3845
.filename=strdup("/nonexistent"),
3846
.cancelled_filenames = &(string_set){},
3848
.current_time=¤t_time,
3849
.mandos_client_exited=&mandos_client_exited,
3850
.password_is_read=&password_is_read,
3852
g_assert_nonnull(task.filename);
3853
task.func(task, queue);
3854
g_assert_false(quit_now);
3855
g_assert_true(queue->next_run == 0);
3856
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3858
g_assert_nonnull(find_matching_task(queue, (task_context){
3859
.func=read_inotify_event,
3862
.quit_now=&quit_now,
3863
.password=&password,
3864
.filename=task.filename,
3865
.cancelled_filenames=task.cancelled_filenames,
3866
.current_time=¤t_time,
3867
.mandos_client_exited=&mandos_client_exited,
3868
.password_is_read=&password_is_read,
3871
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3872
EPOLLIN | EPOLLRDHUP));
3874
g_assert_cmpint(close(pipefds[1]), ==, 0);
3877
static void test_read_inotify_event_eof(__attribute__((unused))
3878
test_fixture *fixture,
3879
__attribute__((unused))
3880
gconstpointer user_data){
3881
__attribute__((cleanup(cleanup_close)))
3882
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3883
g_assert_cmpint(epoll_fd, >=, 0);
3884
const mono_microsecs current_time = 0;
3887
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3888
g_assert_cmpint(close(pipefds[1]), ==, 0);
3890
bool quit_now = false;
3891
buffer password = {};
3892
__attribute__((cleanup(cleanup_queue)))
3893
task_queue *queue = create_queue();
3894
g_assert_nonnull(queue);
3896
task_context task = {
3897
.func=read_inotify_event,
3900
.quit_now=&quit_now,
3901
.password=&password,
3902
.filename=strdup("/nonexistent"),
3903
.cancelled_filenames = &(string_set){},
3905
.current_time=¤t_time,
3907
run_task_with_stderr_to_dev_null(task, queue);
3908
g_assert_true(quit_now);
3909
g_assert_true(queue->next_run == 0);
3910
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3912
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3914
g_assert_cmpint(close(pipefds[0]), ==, -1);
3918
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
3919
test_fixture *fixture,
3920
__attribute__((unused))
3921
gconstpointer user_data){
3922
__attribute__((cleanup(cleanup_close)))
3923
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3924
g_assert_cmpint(epoll_fd, >=, 0);
3925
const mono_microsecs current_time = 0;
3928
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3930
/* "sufficient to read at least one event." - inotify(7) */
3931
const size_t ievent_max_size = (sizeof(struct inotify_event)
3933
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
3935
struct inotify_event event;
3936
char name_buffer[NAME_MAX + 1];
3938
struct inotify_event *const ievent = &ievent_buffer.event;
3940
const char dummy_file_name[] = "ask.dummy_file_name";
3941
ievent->mask = IN_CLOSE_WRITE;
3942
ievent->len = sizeof(dummy_file_name);
3943
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
3944
const size_t ievent_size = (sizeof(struct inotify_event)
3945
+ sizeof(dummy_file_name));
3946
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
3948
g_assert_cmpint(close(pipefds[1]), ==, 0);
3950
bool quit_now = false;
3951
buffer password = {};
3952
bool mandos_client_exited = false;
3953
bool password_is_read = false;
3954
__attribute__((cleanup(cleanup_queue)))
3955
task_queue *queue = create_queue();
3956
g_assert_nonnull(queue);
3958
task_context task = {
3959
.func=read_inotify_event,
3962
.quit_now=&quit_now,
3963
.password=&password,
3964
.filename=strdup("/nonexistent"),
3965
.cancelled_filenames = &(string_set){},
3967
.current_time=¤t_time,
3968
.mandos_client_exited=&mandos_client_exited,
3969
.password_is_read=&password_is_read,
3971
task.func(task, queue);
3972
g_assert_false(quit_now);
3973
g_assert_true(queue->next_run != 0);
3974
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
3976
g_assert_nonnull(find_matching_task(queue, (task_context){
3977
.func=read_inotify_event,
3980
.quit_now=&quit_now,
3981
.password=&password,
3982
.filename=task.filename,
3983
.cancelled_filenames=task.cancelled_filenames,
3984
.current_time=¤t_time,
3985
.mandos_client_exited=&mandos_client_exited,
3986
.password_is_read=&password_is_read,
3989
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3990
EPOLLIN | EPOLLRDHUP));
3992
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
3994
__attribute__((cleanup(cleanup_string)))
3995
char *filename = NULL;
3996
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
3997
dummy_file_name), >, 0);
3998
g_assert_nonnull(filename);
3999
g_assert_nonnull(find_matching_task(queue, (task_context){
4000
.func=open_and_parse_question,
4003
.question_filename=filename,
4004
.password=&password,
4005
.cancelled_filenames=task.cancelled_filenames,
4006
.current_time=¤t_time,
4007
.mandos_client_exited=&mandos_client_exited,
4008
.password_is_read=&password_is_read,
4013
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4014
test_fixture *fixture,
4015
__attribute__((unused))
4016
gconstpointer user_data){
4017
__attribute__((cleanup(cleanup_close)))
4018
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4019
g_assert_cmpint(epoll_fd, >=, 0);
4020
const mono_microsecs current_time = 0;
4023
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4025
/* "sufficient to read at least one event." - inotify(7) */
4026
const size_t ievent_max_size = (sizeof(struct inotify_event)
4028
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4030
struct inotify_event event;
4031
char name_buffer[NAME_MAX + 1];
4033
struct inotify_event *const ievent = &ievent_buffer.event;
4035
const char dummy_file_name[] = "ask.dummy_file_name";
4036
ievent->mask = IN_MOVED_TO;
4037
ievent->len = sizeof(dummy_file_name);
4038
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4039
const size_t ievent_size = (sizeof(struct inotify_event)
4040
+ sizeof(dummy_file_name));
4041
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4043
g_assert_cmpint(close(pipefds[1]), ==, 0);
4045
bool quit_now = false;
4046
buffer password = {};
4047
bool mandos_client_exited = false;
4048
bool password_is_read = false;
4049
__attribute__((cleanup(cleanup_queue)))
4050
task_queue *queue = create_queue();
4051
g_assert_nonnull(queue);
4053
task_context task = {
4054
.func=read_inotify_event,
4057
.quit_now=&quit_now,
4058
.password=&password,
4059
.filename=strdup("/nonexistent"),
4060
.cancelled_filenames = &(string_set){},
4062
.current_time=¤t_time,
4063
.mandos_client_exited=&mandos_client_exited,
4064
.password_is_read=&password_is_read,
4066
task.func(task, queue);
4067
g_assert_false(quit_now);
4068
g_assert_true(queue->next_run != 0);
4069
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4071
g_assert_nonnull(find_matching_task(queue, (task_context){
4072
.func=read_inotify_event,
4075
.quit_now=&quit_now,
4076
.password=&password,
4077
.filename=task.filename,
4078
.cancelled_filenames=task.cancelled_filenames,
4079
.current_time=¤t_time,
4080
.mandos_client_exited=&mandos_client_exited,
4081
.password_is_read=&password_is_read,
4084
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4085
EPOLLIN | EPOLLRDHUP));
4087
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4089
__attribute__((cleanup(cleanup_string)))
4090
char *filename = NULL;
4091
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4092
dummy_file_name), >, 0);
4093
g_assert_nonnull(filename);
4094
g_assert_nonnull(find_matching_task(queue, (task_context){
4095
.func=open_and_parse_question,
4098
.question_filename=filename,
4099
.password=&password,
4100
.cancelled_filenames=task.cancelled_filenames,
4101
.current_time=¤t_time,
4102
.mandos_client_exited=&mandos_client_exited,
4103
.password_is_read=&password_is_read,
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4108
test_fixture *fixture,
4109
__attribute__((unused))
4112
__attribute__((cleanup(cleanup_close)))
4113
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4114
g_assert_cmpint(epoll_fd, >=, 0);
4115
__attribute__((cleanup(string_set_clear)))
4116
string_set cancelled_filenames = {};
4117
const mono_microsecs current_time = 0;
4120
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4122
/* "sufficient to read at least one event." - inotify(7) */
4123
const size_t ievent_max_size = (sizeof(struct inotify_event)
4125
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4127
struct inotify_event event;
4128
char name_buffer[NAME_MAX + 1];
4130
struct inotify_event *const ievent = &ievent_buffer.event;
4132
const char dummy_file_name[] = "ask.dummy_file_name";
4133
ievent->mask = IN_DELETE;
4134
ievent->len = sizeof(dummy_file_name);
4135
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4136
const size_t ievent_size = (sizeof(struct inotify_event)
4137
+ sizeof(dummy_file_name));
4138
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4140
g_assert_cmpint(close(pipefds[1]), ==, 0);
4142
bool quit_now = false;
4143
buffer password = {};
4144
bool mandos_client_exited = false;
4145
bool password_is_read = false;
4146
__attribute__((cleanup(cleanup_queue)))
4147
task_queue *queue = create_queue();
4148
g_assert_nonnull(queue);
4150
task_context task = {
4151
.func=read_inotify_event,
4154
.quit_now=&quit_now,
4155
.password=&password,
4156
.filename=strdup("/nonexistent"),
4157
.cancelled_filenames=&cancelled_filenames,
4158
.current_time=¤t_time,
4159
.mandos_client_exited=&mandos_client_exited,
4160
.password_is_read=&password_is_read,
4162
task.func(task, queue);
4163
g_assert_false(quit_now);
4164
g_assert_true(queue->next_run == 0);
4165
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4167
g_assert_nonnull(find_matching_task(queue, (task_context){
4168
.func=read_inotify_event,
4171
.quit_now=&quit_now,
4172
.password=&password,
4173
.filename=task.filename,
4174
.cancelled_filenames=&cancelled_filenames,
4175
.current_time=¤t_time,
4176
.mandos_client_exited=&mandos_client_exited,
4177
.password_is_read=&password_is_read,
4180
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4181
EPOLLIN | EPOLLRDHUP));
4183
__attribute__((cleanup(cleanup_string)))
4184
char *filename = NULL;
4185
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4186
dummy_file_name), >, 0);
4187
g_assert_nonnull(filename);
4188
g_assert_true(string_set_contains(*task.cancelled_filenames,
4193
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4194
test_fixture *fixture,
4195
__attribute__((unused))
4198
__attribute__((cleanup(cleanup_close)))
4199
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4200
g_assert_cmpint(epoll_fd, >=, 0);
4201
const mono_microsecs current_time = 0;
4204
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4206
/* "sufficient to read at least one event." - inotify(7) */
4207
const size_t ievent_max_size = (sizeof(struct inotify_event)
4209
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4211
struct inotify_event event;
4212
char name_buffer[NAME_MAX + 1];
4214
struct inotify_event *const ievent = &ievent_buffer.event;
4216
const char dummy_file_name[] = "ignored.dummy_file_name";
4217
ievent->mask = IN_CLOSE_WRITE;
4218
ievent->len = sizeof(dummy_file_name);
4219
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4220
const size_t ievent_size = (sizeof(struct inotify_event)
4221
+ sizeof(dummy_file_name));
4222
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4224
g_assert_cmpint(close(pipefds[1]), ==, 0);
4226
bool quit_now = false;
4227
buffer password = {};
4228
bool mandos_client_exited = false;
4229
bool password_is_read = false;
4230
__attribute__((cleanup(cleanup_queue)))
4231
task_queue *queue = create_queue();
4232
g_assert_nonnull(queue);
4234
task_context task = {
4235
.func=read_inotify_event,
4238
.quit_now=&quit_now,
4239
.password=&password,
4240
.filename=strdup("/nonexistent"),
4241
.cancelled_filenames = &(string_set){},
4243
.current_time=¤t_time,
4244
.mandos_client_exited=&mandos_client_exited,
4245
.password_is_read=&password_is_read,
4247
task.func(task, queue);
4248
g_assert_false(quit_now);
4249
g_assert_true(queue->next_run == 0);
4250
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4252
g_assert_nonnull(find_matching_task(queue, (task_context){
4253
.func=read_inotify_event,
4256
.quit_now=&quit_now,
4257
.password=&password,
4258
.filename=task.filename,
4259
.cancelled_filenames=task.cancelled_filenames,
4260
.current_time=¤t_time,
4261
.mandos_client_exited=&mandos_client_exited,
4262
.password_is_read=&password_is_read,
4265
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4266
EPOLLIN | EPOLLRDHUP));
4270
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4271
test_fixture *fixture,
4272
__attribute__((unused))
4273
gconstpointer user_data){
4274
__attribute__((cleanup(cleanup_close)))
4275
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4276
g_assert_cmpint(epoll_fd, >=, 0);
4277
const mono_microsecs current_time = 0;
4280
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4282
/* "sufficient to read at least one event." - inotify(7) */
4283
const size_t ievent_max_size = (sizeof(struct inotify_event)
4285
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4287
struct inotify_event event;
4288
char name_buffer[NAME_MAX + 1];
4290
struct inotify_event *const ievent = &ievent_buffer.event;
4292
const char dummy_file_name[] = "ignored.dummy_file_name";
4293
ievent->mask = IN_MOVED_TO;
4294
ievent->len = sizeof(dummy_file_name);
4295
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4296
const size_t ievent_size = (sizeof(struct inotify_event)
4297
+ sizeof(dummy_file_name));
4298
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4300
g_assert_cmpint(close(pipefds[1]), ==, 0);
4302
bool quit_now = false;
4303
buffer password = {};
4304
bool mandos_client_exited = false;
4305
bool password_is_read = false;
4306
__attribute__((cleanup(cleanup_queue)))
4307
task_queue *queue = create_queue();
4308
g_assert_nonnull(queue);
4310
task_context task = {
4311
.func=read_inotify_event,
4314
.quit_now=&quit_now,
4315
.password=&password,
4316
.filename=strdup("/nonexistent"),
4317
.cancelled_filenames = &(string_set){},
4319
.current_time=¤t_time,
4320
.mandos_client_exited=&mandos_client_exited,
4321
.password_is_read=&password_is_read,
4323
task.func(task, queue);
4324
g_assert_false(quit_now);
4325
g_assert_true(queue->next_run == 0);
4326
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4328
g_assert_nonnull(find_matching_task(queue, (task_context){
4329
.func=read_inotify_event,
4332
.quit_now=&quit_now,
4333
.password=&password,
4334
.filename=task.filename,
4335
.cancelled_filenames=task.cancelled_filenames,
4336
.current_time=¤t_time,
4337
.mandos_client_exited=&mandos_client_exited,
4338
.password_is_read=&password_is_read,
4341
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4342
EPOLLIN | EPOLLRDHUP));
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4347
test_fixture *fixture,
4348
__attribute__((unused))
4351
__attribute__((cleanup(cleanup_close)))
4352
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4353
g_assert_cmpint(epoll_fd, >=, 0);
4354
__attribute__((cleanup(string_set_clear)))
4355
string_set cancelled_filenames = {};
4356
const mono_microsecs current_time = 0;
4359
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4361
/* "sufficient to read at least one event." - inotify(7) */
4362
const size_t ievent_max_size = (sizeof(struct inotify_event)
4364
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4366
struct inotify_event event;
4367
char name_buffer[NAME_MAX + 1];
4369
struct inotify_event *const ievent = &ievent_buffer.event;
4371
const char dummy_file_name[] = "ignored.dummy_file_name";
4372
ievent->mask = IN_DELETE;
4373
ievent->len = sizeof(dummy_file_name);
4374
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4375
const size_t ievent_size = (sizeof(struct inotify_event)
4376
+ sizeof(dummy_file_name));
4377
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4379
g_assert_cmpint(close(pipefds[1]), ==, 0);
4381
bool quit_now = false;
4382
buffer password = {};
4383
bool mandos_client_exited = false;
4384
bool password_is_read = false;
4385
__attribute__((cleanup(cleanup_queue)))
4386
task_queue *queue = create_queue();
4387
g_assert_nonnull(queue);
4389
task_context task = {
4390
.func=read_inotify_event,
4393
.quit_now=&quit_now,
4394
.password=&password,
4395
.filename=strdup("/nonexistent"),
4396
.cancelled_filenames=&cancelled_filenames,
4397
.current_time=¤t_time,
4398
.mandos_client_exited=&mandos_client_exited,
4399
.password_is_read=&password_is_read,
4401
task.func(task, queue);
4402
g_assert_false(quit_now);
4403
g_assert_true(queue->next_run == 0);
4404
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4406
g_assert_nonnull(find_matching_task(queue, (task_context){
4407
.func=read_inotify_event,
4410
.quit_now=&quit_now,
4411
.password=&password,
4412
.filename=task.filename,
4413
.cancelled_filenames=&cancelled_filenames,
4414
.current_time=¤t_time,
4415
.mandos_client_exited=&mandos_client_exited,
4416
.password_is_read=&password_is_read,
4419
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4420
EPOLLIN | EPOLLRDHUP));
4422
__attribute__((cleanup(cleanup_string)))
4423
char *filename = NULL;
4424
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4425
dummy_file_name), >, 0);
4426
g_assert_nonnull(filename);
4427
g_assert_false(string_set_contains(cancelled_filenames, filename));
4431
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4432
test_fixture *fixture,
4433
__attribute__((unused))
4434
gconstpointer user_data){
4435
__attribute__((cleanup(cleanup_close)))
4436
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4437
g_assert_cmpint(epoll_fd, >=, 0);
4438
__attribute__((cleanup(string_set_clear)))
4439
string_set cancelled_filenames = {};
4440
bool mandos_client_exited = false;
4441
bool password_is_read = false;
4442
__attribute__((cleanup(cleanup_queue)))
4443
task_queue *queue = create_queue();
4444
g_assert_nonnull(queue);
4446
char *const filename = strdup("/nonexistent");
4447
g_assert_nonnull(filename);
4448
task_context task = {
4449
.func=open_and_parse_question,
4450
.question_filename=filename,
4452
.password=(buffer[]){{}},
4454
.cancelled_filenames=&cancelled_filenames,
4455
.current_time=(mono_microsecs[]){0},
4456
.mandos_client_exited=&mandos_client_exited,
4457
.password_is_read=&password_is_read,
4459
task.func(task, queue);
4460
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4463
static void test_open_and_parse_question_EIO(__attribute__((unused))
4464
test_fixture *fixture,
4465
__attribute__((unused))
4466
gconstpointer user_data){
4467
__attribute__((cleanup(cleanup_close)))
4468
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4469
g_assert_cmpint(epoll_fd, >=, 0);
4470
__attribute__((cleanup(string_set_clear)))
4471
string_set cancelled_filenames = {};
4472
buffer password = {};
4473
bool mandos_client_exited = false;
4474
bool password_is_read = false;
4475
__attribute__((cleanup(cleanup_queue)))
4476
task_queue *queue = create_queue();
4477
g_assert_nonnull(queue);
4478
const mono_microsecs current_time = 0;
4480
char *filename = strdup("/proc/self/mem");
4481
task_context task = {
4482
.func=open_and_parse_question,
4483
.question_filename=filename,
4485
.password=&password,
4487
.cancelled_filenames=&cancelled_filenames,
4488
.current_time=¤t_time,
4489
.mandos_client_exited=&mandos_client_exited,
4490
.password_is_read=&password_is_read,
4492
run_task_with_stderr_to_dev_null(task, queue);
4493
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4497
test_open_and_parse_question_parse_error(__attribute__((unused))
4498
test_fixture *fixture,
4499
__attribute__((unused))
4500
gconstpointer user_data){
4501
__attribute__((cleanup(cleanup_close)))
4502
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4503
g_assert_cmpint(epoll_fd, >=, 0);
4504
__attribute__((cleanup(string_set_clear)))
4505
string_set cancelled_filenames = {};
4506
__attribute__((cleanup(cleanup_queue)))
4507
task_queue *queue = create_queue();
4508
g_assert_nonnull(queue);
4510
__attribute__((cleanup(cleanup_string)))
4511
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4512
g_assert_nonnull(tempfilename);
4513
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4514
g_assert_cmpint(tempfile, >, 0);
4515
const char bad_data[] = "this is bad syntax\n";
4516
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4517
==, sizeof(bad_data));
4518
g_assert_cmpint(close(tempfile), ==, 0);
4520
char *const filename = strdup(tempfilename);
4521
g_assert_nonnull(filename);
4522
task_context task = {
4523
.func=open_and_parse_question,
4524
.question_filename=filename,
4526
.password=(buffer[]){{}},
4528
.cancelled_filenames=&cancelled_filenames,
4529
.current_time=(mono_microsecs[]){0},
4530
.mandos_client_exited=(bool[]){false},
4531
.password_is_read=(bool[]){false},
4533
run_task_with_stderr_to_dev_null(task, queue);
4535
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4537
g_assert_cmpint(unlink(tempfilename), ==, 0);
4541
void test_open_and_parse_question_nosocket(__attribute__((unused))
4542
test_fixture *fixture,
4543
__attribute__((unused))
4544
gconstpointer user_data){
4545
__attribute__((cleanup(cleanup_close)))
4546
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4547
g_assert_cmpint(epoll_fd, >=, 0);
4548
__attribute__((cleanup(string_set_clear)))
4549
string_set cancelled_filenames = {};
4550
__attribute__((cleanup(cleanup_queue)))
4551
task_queue *queue = create_queue();
4552
g_assert_nonnull(queue);
4554
__attribute__((cleanup(cleanup_string)))
4555
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4556
g_assert_nonnull(tempfilename);
4557
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4558
g_assert_cmpint(questionfile, >, 0);
4559
FILE *qf = fdopen(questionfile, "w");
4560
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4561
g_assert_cmpint(fclose(qf), ==, 0);
4563
char *const filename = strdup(tempfilename);
4564
g_assert_nonnull(filename);
4565
task_context task = {
4566
.func=open_and_parse_question,
4567
.question_filename=filename,
4569
.password=(buffer[]){{}},
4571
.cancelled_filenames=&cancelled_filenames,
4572
.current_time=(mono_microsecs[]){0},
4573
.mandos_client_exited=(bool[]){false},
4574
.password_is_read=(bool[]){false},
4576
run_task_with_stderr_to_dev_null(task, queue);
4577
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4579
g_assert_cmpint(unlink(tempfilename), ==, 0);
4583
void test_open_and_parse_question_badsocket(__attribute__((unused))
4584
test_fixture *fixture,
4585
__attribute__((unused))
4586
gconstpointer user_data){
4587
__attribute__((cleanup(cleanup_close)))
4588
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4589
g_assert_cmpint(epoll_fd, >=, 0);
4590
__attribute__((cleanup(string_set_clear)))
4591
string_set cancelled_filenames = {};
4592
__attribute__((cleanup(cleanup_queue)))
4593
task_queue *queue = create_queue();
4594
g_assert_nonnull(queue);
4596
__attribute__((cleanup(cleanup_string)))
4597
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4598
g_assert_nonnull(tempfilename);
4599
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4600
g_assert_cmpint(questionfile, >, 0);
4601
FILE *qf = fdopen(questionfile, "w");
4602
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4603
g_assert_cmpint(fclose(qf), ==, 0);
4605
char *const filename = strdup(tempfilename);
4606
g_assert_nonnull(filename);
4607
task_context task = {
4608
.func=open_and_parse_question,
4609
.question_filename=filename,
4611
.password=(buffer[]){{}},
4613
.cancelled_filenames=&cancelled_filenames,
4614
.current_time=(mono_microsecs[]){0},
4615
.mandos_client_exited=(bool[]){false},
4616
.password_is_read=(bool[]){false},
4618
run_task_with_stderr_to_dev_null(task, queue);
4619
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4621
g_assert_cmpint(unlink(tempfilename), ==, 0);
4625
void test_open_and_parse_question_nopid(__attribute__((unused))
4626
test_fixture *fixture,
4627
__attribute__((unused))
4628
gconstpointer user_data){
4629
__attribute__((cleanup(cleanup_close)))
4630
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4631
g_assert_cmpint(epoll_fd, >=, 0);
4632
__attribute__((cleanup(string_set_clear)))
4633
string_set cancelled_filenames = {};
4634
__attribute__((cleanup(cleanup_queue)))
4635
task_queue *queue = create_queue();
4636
g_assert_nonnull(queue);
4638
__attribute__((cleanup(cleanup_string)))
4639
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4640
g_assert_nonnull(tempfilename);
4641
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4642
g_assert_cmpint(questionfile, >, 0);
4643
FILE *qf = fdopen(questionfile, "w");
4644
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
4645
g_assert_cmpint(fclose(qf), ==, 0);
4647
char *const filename = strdup(tempfilename);
4648
g_assert_nonnull(filename);
4649
task_context task = {
4650
.func=open_and_parse_question,
4651
.question_filename=filename,
4653
.password=(buffer[]){{}},
4655
.cancelled_filenames=&cancelled_filenames,
4656
.current_time=(mono_microsecs[]){0},
4657
.mandos_client_exited=(bool[]){false},
4658
.password_is_read=(bool[]){false},
4660
run_task_with_stderr_to_dev_null(task, queue);
4661
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4663
g_assert_cmpint(unlink(tempfilename), ==, 0);
4667
void test_open_and_parse_question_badpid(__attribute__((unused))
4668
test_fixture *fixture,
4669
__attribute__((unused))
4670
gconstpointer user_data){
4671
__attribute__((cleanup(cleanup_close)))
4672
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4673
g_assert_cmpint(epoll_fd, >=, 0);
4674
__attribute__((cleanup(string_set_clear)))
4675
string_set cancelled_filenames = {};
4676
__attribute__((cleanup(cleanup_queue)))
4677
task_queue *queue = create_queue();
4678
g_assert_nonnull(queue);
4680
__attribute__((cleanup(cleanup_string)))
4681
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4682
g_assert_nonnull(tempfilename);
4683
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4684
g_assert_cmpint(questionfile, >, 0);
4685
FILE *qf = fdopen(questionfile, "w");
4686
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
4688
g_assert_cmpint(fclose(qf), ==, 0);
4690
char *const filename = strdup(tempfilename);
4691
g_assert_nonnull(filename);
4692
task_context task = {
4693
.func=open_and_parse_question,
4694
.question_filename=filename,
4696
.password=(buffer[]){{}},
4698
.cancelled_filenames=&cancelled_filenames,
4699
.current_time=(mono_microsecs[]){0},
4700
.mandos_client_exited=(bool[]){false},
4701
.password_is_read=(bool[]){false},
4703
run_task_with_stderr_to_dev_null(task, queue);
4704
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4706
g_assert_cmpint(unlink(tempfilename), ==, 0);
4710
test_open_and_parse_question_noexist_pid(__attribute__((unused))
4711
test_fixture *fixture,
4712
__attribute__((unused))
4713
gconstpointer user_data){
4714
__attribute__((cleanup(cleanup_close)))
4715
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4716
g_assert_cmpint(epoll_fd, >=, 0);
4717
__attribute__((cleanup(string_set_clear)))
4718
string_set cancelled_filenames = {};
4719
buffer password = {};
4720
bool mandos_client_exited = false;
4721
bool password_is_read = false;
4722
__attribute__((cleanup(cleanup_queue)))
4723
task_queue *queue = create_queue();
4724
g_assert_nonnull(queue);
4725
const mono_microsecs current_time = 0;
4727
/* Find value of sysctl kernel.pid_max */
4728
uintmax_t pid_max = 0;
4729
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
4730
g_assert_nonnull(sysctl_pid_max);
4731
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
4733
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
4735
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
4736
g_assert_true(nonexisting_pid > 0); /* Overflow check */
4738
__attribute__((cleanup(cleanup_string)))
4739
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4740
g_assert_nonnull(tempfilename);
4741
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4742
g_assert_cmpint(questionfile, >, 0);
4743
FILE *qf = fdopen(questionfile, "w");
4744
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4745
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
4747
g_assert_cmpint(fclose(qf), ==, 0);
4749
char *const question_filename = strdup(tempfilename);
4750
g_assert_nonnull(question_filename);
4751
task_context task = {
4752
.func=open_and_parse_question,
4753
.question_filename=question_filename,
4755
.password=&password,
4756
.filename=question_filename,
4757
.cancelled_filenames=&cancelled_filenames,
4758
.current_time=¤t_time,
4759
.mandos_client_exited=&mandos_client_exited,
4760
.password_is_read=&password_is_read,
4762
run_task_with_stderr_to_dev_null(task, queue);
4763
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4765
g_assert_cmpint(unlink(tempfilename), ==, 0);
4769
test_open_and_parse_question_no_notafter(__attribute__((unused))
4770
test_fixture *fixture,
4771
__attribute__((unused))
4772
gconstpointer user_data){
4773
__attribute__((cleanup(cleanup_close)))
4774
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4775
g_assert_cmpint(epoll_fd, >=, 0);
4776
__attribute__((cleanup(string_set_clear)))
4777
string_set cancelled_filenames = {};
4778
buffer password = {};
4779
bool mandos_client_exited = false;
4780
bool password_is_read = false;
4781
__attribute__((cleanup(cleanup_queue)))
4782
task_queue *queue = create_queue();
4783
g_assert_nonnull(queue);
4784
const mono_microsecs current_time = 0;
4786
__attribute__((cleanup(cleanup_string)))
4787
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4788
g_assert_nonnull(tempfilename);
4789
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4790
g_assert_cmpint(questionfile, >, 0);
4791
FILE *qf = fdopen(questionfile, "w");
4792
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4793
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
4794
g_assert_cmpint(fclose(qf), ==, 0);
4796
char *const filename = strdup(tempfilename);
4797
g_assert_nonnull(filename);
4798
task_context task = {
4799
.func=open_and_parse_question,
4800
.question_filename=filename,
4802
.password=&password,
4804
.cancelled_filenames=&cancelled_filenames,
4805
.current_time=¤t_time,
4806
.mandos_client_exited=&mandos_client_exited,
4807
.password_is_read=&password_is_read,
4809
task.func(task, queue);
4810
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4812
__attribute__((cleanup(cleanup_string)))
4813
char *socket_filename = strdup("/nonexistent");
4814
g_assert_nonnull(socket_filename);
4815
g_assert_nonnull(find_matching_task(queue, (task_context){
4816
.func=connect_question_socket,
4817
.question_filename=tempfilename,
4818
.filename=socket_filename,
4820
.password=&password,
4821
.current_time=¤t_time,
4822
.mandos_client_exited=&mandos_client_exited,
4823
.password_is_read=&password_is_read,
4826
g_assert_true(queue->next_run != 0);
4828
g_assert_cmpint(unlink(tempfilename), ==, 0);
4832
test_open_and_parse_question_bad_notafter(__attribute__((unused))
4833
test_fixture *fixture,
4834
__attribute__((unused))
4835
gconstpointer user_data){
4836
__attribute__((cleanup(cleanup_close)))
4837
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4838
g_assert_cmpint(epoll_fd, >=, 0);
4839
__attribute__((cleanup(string_set_clear)))
4840
string_set cancelled_filenames = {};
4841
buffer password = {};
4842
bool mandos_client_exited = false;
4843
bool password_is_read = false;
4844
__attribute__((cleanup(cleanup_queue)))
4845
task_queue *queue = create_queue();
4846
g_assert_nonnull(queue);
4847
const mono_microsecs current_time = 0;
4849
__attribute__((cleanup(cleanup_string)))
4850
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4851
g_assert_nonnull(tempfilename);
4852
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4853
g_assert_cmpint(questionfile, >, 0);
4854
FILE *qf = fdopen(questionfile, "w");
4855
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4856
PRIuMAX "\nNotAfter=\n",
4857
(uintmax_t)getpid()), >, 0);
4858
g_assert_cmpint(fclose(qf), ==, 0);
4860
char *const filename = strdup(tempfilename);
4861
g_assert_nonnull(filename);
4862
task_context task = {
4863
.func=open_and_parse_question,
4864
.question_filename=filename,
4866
.password=&password,
4868
.cancelled_filenames=&cancelled_filenames,
4869
.current_time=¤t_time,
4870
.mandos_client_exited=&mandos_client_exited,
4871
.password_is_read=&password_is_read,
4873
run_task_with_stderr_to_dev_null(task, queue);
4874
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4876
__attribute__((cleanup(cleanup_string)))
4877
char *socket_filename = strdup("/nonexistent");
4878
g_assert_nonnull(find_matching_task(queue, (task_context){
4879
.func=connect_question_socket,
4880
.question_filename=tempfilename,
4881
.filename=socket_filename,
4883
.password=&password,
4884
.current_time=¤t_time,
4885
.mandos_client_exited=&mandos_client_exited,
4886
.password_is_read=&password_is_read,
4888
g_assert_true(queue->next_run != 0);
4890
g_assert_cmpint(unlink(tempfilename), ==, 0);
4894
void assert_open_and_parse_question_with_notafter(const mono_microsecs
4896
const mono_microsecs
4898
const mono_microsecs
4900
__attribute__((cleanup(cleanup_close)))
4901
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4902
g_assert_cmpint(epoll_fd, >=, 0);
4903
__attribute__((cleanup(string_set_clear)))
4904
string_set cancelled_filenames = {};
4905
buffer password = {};
4906
bool mandos_client_exited = false;
4907
bool password_is_read = false;
4908
__attribute__((cleanup(cleanup_queue)))
4909
task_queue *queue = create_queue();
4910
g_assert_nonnull(queue);
4911
queue->next_run = next_queue_run;
4913
__attribute__((cleanup(cleanup_string)))
4914
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4915
g_assert_nonnull(tempfilename);
4916
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4917
g_assert_cmpint(questionfile, >, 0);
4918
FILE *qf = fdopen(questionfile, "w");
4919
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4920
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
4921
(uintmax_t)getpid(), notafter), >, 0);
4922
g_assert_cmpint(fclose(qf), ==, 0);
4924
char *const filename = strdup(tempfilename);
4925
g_assert_nonnull(filename);
4926
task_context task = {
4927
.func=open_and_parse_question,
4928
.question_filename=filename,
4930
.password=&password,
4932
.cancelled_filenames=&cancelled_filenames,
4933
.current_time=¤t_time,
4934
.mandos_client_exited=&mandos_client_exited,
4935
.password_is_read=&password_is_read,
4937
task.func(task, queue);
4939
if(queue->length >= 1){
4940
__attribute__((cleanup(cleanup_string)))
4941
char *socket_filename = strdup("/nonexistent");
4942
g_assert_nonnull(find_matching_task(queue, (task_context){
4943
.func=connect_question_socket,
4944
.filename=socket_filename,
4946
.password=&password,
4947
.current_time=¤t_time,
4948
.cancelled_filenames=&cancelled_filenames,
4949
.mandos_client_exited=&mandos_client_exited,
4950
.password_is_read=&password_is_read,
4952
g_assert_true(queue->next_run != 0);
4956
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4957
} else if(current_time >= notafter) {
4958
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4960
g_assert_nonnull(find_matching_task(queue, (task_context){
4961
.func=cancel_old_question,
4962
.question_filename=tempfilename,
4963
.filename=tempfilename,
4965
.cancelled_filenames=&cancelled_filenames,
4966
.current_time=¤t_time,
4969
g_assert_true(queue->next_run == 1);
4971
g_assert_cmpint(unlink(tempfilename), ==, 0);
4975
test_open_and_parse_question_notafter_0(__attribute__((unused))
4976
test_fixture *fixture,
4977
__attribute__((unused))
4978
gconstpointer user_data){
4979
/* current_time, notafter, next_queue_run */
4980
assert_open_and_parse_question_with_notafter(0, 0, 0);
4984
test_open_and_parse_question_notafter_1(__attribute__((unused))
4985
test_fixture *fixture,
4986
__attribute__((unused))
4987
gconstpointer user_data){
4988
/* current_time, notafter, next_queue_run */
4989
assert_open_and_parse_question_with_notafter(0, 1, 0);
4993
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
4994
test_fixture *fixture,
4995
__attribute__((unused))
4996
gconstpointer user_data){
4997
/* current_time, notafter, next_queue_run */
4998
assert_open_and_parse_question_with_notafter(0, 1, 1);
5002
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5003
test_fixture *fixture,
5004
__attribute__((unused))
5005
gconstpointer user_data){
5006
/* current_time, notafter, next_queue_run */
5007
assert_open_and_parse_question_with_notafter(0, 1, 2);
5011
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5012
test_fixture *fixture,
5013
__attribute__((unused))
5014
gconstpointer user_data){
5015
/* current_time, notafter, next_queue_run */
5016
assert_open_and_parse_question_with_notafter(1, 1, 0);
5020
test_open_and_parse_question_late_notafter(__attribute__((unused))
5021
test_fixture *fixture,
5022
__attribute__((unused))
5023
gconstpointer user_data){
5024
/* current_time, notafter, next_queue_run */
5025
assert_open_and_parse_question_with_notafter(2, 1, 0);
5028
static void assert_cancel_old_question_param(const mono_microsecs
5030
const mono_microsecs
5032
const mono_microsecs
5034
const mono_microsecs
5036
__attribute__((cleanup(string_set_clear)))
5037
string_set cancelled_filenames = {};
5038
__attribute__((cleanup(cleanup_queue)))
5039
task_queue *queue = create_queue();
5040
g_assert_nonnull(queue);
5041
queue->next_run = next_queue_run;
5043
char *const question_filename = strdup("/nonexistent");
5044
g_assert_nonnull(question_filename);
5045
task_context task = {
5046
.func=cancel_old_question,
5047
.question_filename=question_filename,
5048
.filename=question_filename,
5050
.cancelled_filenames=&cancelled_filenames,
5051
.current_time=¤t_time,
5053
task.func(task, queue);
5055
if(current_time >= notafter){
5056
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5057
g_assert_true(string_set_contains(cancelled_filenames,
5060
g_assert_nonnull(find_matching_task(queue, (task_context){
5061
.func=cancel_old_question,
5062
.question_filename=question_filename,
5063
.filename=question_filename,
5065
.cancelled_filenames=&cancelled_filenames,
5066
.current_time=¤t_time,
5069
g_assert_false(string_set_contains(cancelled_filenames,
5070
question_filename));
5072
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5073
(unsigned int)next_set_to);
5076
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5077
test_fixture *fixture,
5078
__attribute__((unused))
5079
gconstpointer user_data){
5080
/* next_queue_run unset,
5081
cancellation should happen because time has come,
5082
next_queue_run should be unchanged */
5083
/* next_queue_run, notafter, current_time, next_set_to */
5084
assert_cancel_old_question_param(0, 1, 2, 0);
5087
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5088
test_fixture *fixture,
5089
__attribute__((unused))
5090
gconstpointer user_data){
5091
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5092
and current_time is not yet notafter or greater,
5093
update value of next_queue_run to value of notafter */
5094
/* next_queue_run, notafter, current_time, next_set_to */
5095
assert_cancel_old_question_param(0, 2, 1, 2);
5098
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5099
test_fixture *fixture,
5100
__attribute__((unused))
5101
gconstpointer user_data){
5102
/* next_queue_run 1,
5103
cancellation should happen because time has come,
5104
next_queue_run should be unchanged */
5105
/* next_queue_run, notafter, current_time, next_set_to */
5106
assert_cancel_old_question_param(1, 2, 3, 1);
5109
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5110
test_fixture *fixture,
5111
__attribute__((unused))
5112
gconstpointer user_data){
5113
/* If next_queue_run is set,
5114
and current_time is not yet notafter or greater,
5115
and notafter is larger than next_queue_run
5116
next_queue_run should be unchanged */
5117
/* next_queue_run, notafter, current_time, next_set_to */
5118
assert_cancel_old_question_param(1, 3, 2, 1);
5121
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5122
test_fixture *fixture,
5123
__attribute__((unused))
5124
gconstpointer user_data){
5125
/* next_queue_run 2,
5126
cancellation should happen because time has come,
5127
next_queue_run should be unchanged */
5128
/* next_queue_run, notafter, current_time, next_set_to */
5129
assert_cancel_old_question_param(2, 1, 3, 2);
5132
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5133
test_fixture *fixture,
5134
__attribute__((unused))
5135
gconstpointer user_data){
5136
/* If next_queue_run is set,
5137
and current_time is not yet notafter or greater,
5138
and notafter is larger than next_queue_run
5139
next_queue_run should be unchanged */
5140
/* next_queue_run, notafter, current_time, next_set_to */
5141
assert_cancel_old_question_param(2, 3, 1, 2);
5144
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5145
test_fixture *fixture,
5146
__attribute__((unused))
5147
gconstpointer user_data){
5148
/* next_queue_run 3,
5149
cancellation should happen because time has come,
5150
next_queue_run should be unchanged */
5151
/* next_queue_run, notafter, current_time, next_set_to */
5152
assert_cancel_old_question_param(3, 1, 2, 3);
5155
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5156
test_fixture *fixture,
5157
__attribute__((unused))
5158
gconstpointer user_data){
5159
/* If next_queue_run is set,
5160
and current_time is not yet notafter or greater,
5161
and notafter is smaller than next_queue_run
5162
update value of next_queue_run to value of notafter */
5163
/* next_queue_run, notafter, current_time, next_set_to */
5164
assert_cancel_old_question_param(3, 2, 1, 2);
5168
test_connect_question_socket_name_too_long(__attribute__((unused))
5169
test_fixture *fixture,
5170
__attribute__((unused))
5171
gconstpointer user_data){
5172
__attribute__((cleanup(cleanup_close)))
5173
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5174
g_assert_cmpint(epoll_fd, >=, 0);
5175
const char question_filename[] = "/nonexistent/question";
5176
__attribute__((cleanup(string_set_clear)))
5177
string_set cancelled_filenames = {};
5178
__attribute__((cleanup(cleanup_queue)))
5179
task_queue *queue = create_queue();
5180
g_assert_nonnull(queue);
5181
__attribute__((cleanup(cleanup_string)))
5182
char *tempdir = make_temporary_directory();
5183
g_assert_nonnull(tempdir);
5184
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5185
char socket_name[sizeof(unix_socket.sun_path)];
5186
memset(socket_name, 'x', sizeof(socket_name));
5187
socket_name[sizeof(socket_name)-1] = '\0';
5188
char *filename = NULL;
5189
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5191
g_assert_nonnull(filename);
5193
task_context task = {
5194
.func=connect_question_socket,
5195
.question_filename=strdup(question_filename),
5197
.password=(buffer[]){{}},
5199
.cancelled_filenames=&cancelled_filenames,
5200
.mandos_client_exited=(bool[]){false},
5201
.password_is_read=(bool[]){false},
5202
.current_time=(mono_microsecs[]){0},
5204
g_assert_nonnull(task.question_filename);
5205
run_task_with_stderr_to_dev_null(task, queue);
5207
g_assert_true(string_set_contains(cancelled_filenames,
5208
question_filename));
5209
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5210
g_assert_true(queue->next_run == 0);
5212
g_assert_cmpint(rmdir(tempdir), ==, 0);
5216
void test_connect_question_socket_connect_fail(__attribute__((unused))
5217
test_fixture *fixture,
5218
__attribute__((unused))
5221
__attribute__((cleanup(cleanup_close)))
5222
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5223
g_assert_cmpint(epoll_fd, >=, 0);
5224
const char question_filename[] = "/nonexistent/question";
5225
__attribute__((cleanup(string_set_clear)))
5226
string_set cancelled_filenames = {};
5227
const mono_microsecs current_time = 3;
5228
__attribute__((cleanup(cleanup_queue)))
5229
task_queue *queue = create_queue();
5230
g_assert_nonnull(queue);
5231
__attribute__((cleanup(cleanup_string)))
5232
char *tempdir = make_temporary_directory();
5233
g_assert_nonnull(tempdir);
5234
char socket_name[] = "nonexistent";
5235
char *filename = NULL;
5236
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5238
g_assert_nonnull(filename);
5240
task_context task = {
5241
.func=connect_question_socket,
5242
.question_filename=strdup(question_filename),
5244
.password=(buffer[]){{}},
5246
.cancelled_filenames=&cancelled_filenames,
5247
.mandos_client_exited=(bool[]){false},
5248
.password_is_read=(bool[]){false},
5249
.current_time=¤t_time,
5251
g_assert_nonnull(task.question_filename);
5252
run_task_with_stderr_to_dev_null(task, queue);
5254
g_assert_nonnull(find_matching_task(queue, task));
5256
g_assert_false(string_set_contains(cancelled_filenames,
5257
question_filename));
5258
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5259
g_assert_true(queue->next_run == 1000000 + current_time);
5261
g_assert_cmpint(rmdir(tempdir), ==, 0);
5265
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5266
test_fixture *fixture,
5267
__attribute__((unused))
5268
gconstpointer user_data){
5269
__attribute__((cleanup(cleanup_close)))
5270
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5271
__attribute__((cleanup(cleanup_string)))
5272
char *const question_filename = strdup("/nonexistent/question");
5273
g_assert_nonnull(question_filename);
5274
__attribute__((cleanup(string_set_clear)))
5275
string_set cancelled_filenames = {};
5276
const mono_microsecs current_time = 5;
5277
__attribute__((cleanup(cleanup_queue)))
5278
task_queue *queue = create_queue();
5279
g_assert_nonnull(queue);
5280
__attribute__((cleanup(cleanup_string)))
5281
char *tempdir = make_temporary_directory();
5282
g_assert_nonnull(tempdir);
5283
__attribute__((cleanup(cleanup_close)))
5284
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5285
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5286
g_assert_cmpint(sock_fd, >=, 0);
5287
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5288
const char socket_name[] = "socket_name";
5289
__attribute__((cleanup(cleanup_string)))
5290
char *filename = NULL;
5291
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5293
g_assert_nonnull(filename);
5294
g_assert_cmpint((int)strlen(filename), <,
5295
(int)sizeof(sock_name.sun_path));
5296
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5297
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5298
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5299
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5300
task_context task = {
5301
.func=connect_question_socket,
5302
.question_filename=strdup(question_filename),
5304
.password=(buffer[]){{}},
5305
.filename=strdup(filename),
5306
.cancelled_filenames=&cancelled_filenames,
5307
.mandos_client_exited=(bool[]){false},
5308
.password_is_read=(bool[]){false},
5309
.current_time=¤t_time,
5311
g_assert_nonnull(task.question_filename);
5312
run_task_with_stderr_to_dev_null(task, queue);
5314
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5315
const task_context *const added_task
5316
= find_matching_task(queue, task);
5317
g_assert_nonnull(added_task);
5318
g_assert_true(queue->next_run == 1000000 + current_time);
5320
g_assert_cmpint(unlink(filename), ==, 0);
5321
g_assert_cmpint(rmdir(tempdir), ==, 0);
5325
void test_connect_question_socket_usable(__attribute__((unused))
5326
test_fixture *fixture,
5327
__attribute__((unused))
5328
gconstpointer user_data){
5329
__attribute__((cleanup(cleanup_close)))
5330
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5331
g_assert_cmpint(epoll_fd, >=, 0);
5332
__attribute__((cleanup(cleanup_string)))
5333
char *const question_filename = strdup("/nonexistent/question");
5334
g_assert_nonnull(question_filename);
5335
__attribute__((cleanup(string_set_clear)))
5336
string_set cancelled_filenames = {};
5337
buffer password = {};
5338
bool mandos_client_exited = false;
5339
bool password_is_read = false;
5340
const mono_microsecs current_time = 0;
5341
__attribute__((cleanup(cleanup_queue)))
5342
task_queue *queue = create_queue();
5343
g_assert_nonnull(queue);
5344
__attribute__((cleanup(cleanup_string)))
5345
char *tempdir = make_temporary_directory();
5346
g_assert_nonnull(tempdir);
5347
__attribute__((cleanup(cleanup_close)))
5348
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5349
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5350
g_assert_cmpint(sock_fd, >=, 0);
5351
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5352
const char socket_name[] = "socket_name";
5353
__attribute__((cleanup(cleanup_string)))
5354
char *filename = NULL;
5355
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5357
g_assert_nonnull(filename);
5358
g_assert_cmpint((int)strlen(filename), <,
5359
(int)sizeof(sock_name.sun_path));
5360
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5361
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5362
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5363
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5364
task_context task = {
5365
.func=connect_question_socket,
5366
.question_filename=strdup(question_filename),
5368
.password=&password,
5369
.filename=strdup(filename),
5370
.cancelled_filenames=&cancelled_filenames,
5371
.mandos_client_exited=&mandos_client_exited,
5372
.password_is_read=&password_is_read,
5373
.current_time=¤t_time,
5375
g_assert_nonnull(task.question_filename);
5376
task.func(task, queue);
5378
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5379
const task_context *const added_task
5380
= find_matching_task(queue, (task_context){
5381
.func=send_password_to_socket,
5382
.question_filename=question_filename,
5385
.password=&password,
5386
.cancelled_filenames=&cancelled_filenames,
5387
.mandos_client_exited=&mandos_client_exited,
5388
.password_is_read=&password_is_read,
5389
.current_time=¤t_time,
5391
g_assert_nonnull(added_task);
5392
g_assert_cmpint(added_task->fd, >, 0);
5394
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5397
const int fd = added_task->fd;
5398
g_assert_cmpint(fd, >, 0);
5399
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5402
char write_data[PIPE_BUF];
5404
/* Construct test password buffer */
5405
/* Start with + since that is what the real procotol uses */
5406
write_data[0] = '+';
5407
/* Set a special character at string end just to mark the end */
5408
write_data[sizeof(write_data)-2] = 'y';
5409
/* Set NUL at buffer end, as suggested by the protocol */
5410
write_data[sizeof(write_data)-1] = '\0';
5411
/* Fill rest of password with 'x' */
5412
memset(write_data+1, 'x', sizeof(write_data)-3);
5413
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5414
MSG_NOSIGNAL), ==, sizeof(write_data));
5417
/* read from sock_fd */
5418
char read_data[sizeof(write_data)];
5419
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5420
==, sizeof(read_data));
5422
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5425
/* writing to sock_fd should fail */
5426
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5427
MSG_NOSIGNAL), <, 0);
5429
/* reading from fd should fail */
5430
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5431
MSG_NOSIGNAL), <, 0);
5433
g_assert_cmpint(unlink(filename), ==, 0);
5434
g_assert_cmpint(rmdir(tempdir), ==, 0);
5438
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5439
test_fixture *fixture,
5440
__attribute__((unused))
5443
__attribute__((cleanup(cleanup_close)))
5444
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5445
g_assert_cmpint(epoll_fd, >=, 0);
5446
__attribute__((cleanup(cleanup_string)))
5447
char *const question_filename = strdup("/nonexistent/question");
5448
g_assert_nonnull(question_filename);
5449
__attribute__((cleanup(cleanup_string)))
5450
char *const filename = strdup("/nonexistent/socket");
5451
g_assert_nonnull(filename);
5452
__attribute__((cleanup(string_set_clear)))
5453
string_set cancelled_filenames = {};
5454
buffer password = {};
5455
bool password_is_read = true;
5456
__attribute__((cleanup(cleanup_queue)))
5457
task_queue *queue = create_queue();
5458
g_assert_nonnull(queue);
5460
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5461
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5463
__attribute__((cleanup(cleanup_close)))
5464
const int read_socket = socketfds[0];
5465
const int write_socket = socketfds[1];
5466
task_context task = {
5467
.func=send_password_to_socket,
5468
.question_filename=strdup(question_filename),
5469
.filename=strdup(filename),
5472
.password=&password,
5473
.cancelled_filenames=&cancelled_filenames,
5474
.mandos_client_exited=(bool[]){false},
5475
.password_is_read=&password_is_read,
5476
.current_time=(mono_microsecs[]){0},
5478
g_assert_nonnull(task.question_filename);
5480
task.func(task, queue);
5482
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5484
const task_context *const added_task
5485
= find_matching_task(queue, task);
5486
g_assert_nonnull(added_task);
5487
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5488
g_assert_true(password_is_read);
5490
g_assert_cmpint(added_task->fd, >, 0);
5491
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5496
test_send_password_to_socket_password_not_read(__attribute__((unused))
5497
test_fixture *fixture,
5498
__attribute__((unused))
5501
__attribute__((cleanup(cleanup_close)))
5502
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5503
g_assert_cmpint(epoll_fd, >=, 0);
5504
__attribute__((cleanup(cleanup_string)))
5505
char *const question_filename = strdup("/nonexistent/question");
5506
g_assert_nonnull(question_filename);
5507
__attribute__((cleanup(cleanup_string)))
5508
char *const filename = strdup("/nonexistent/socket");
5509
__attribute__((cleanup(string_set_clear)))
5510
string_set cancelled_filenames = {};
5511
buffer password = {};
5512
__attribute__((cleanup(cleanup_queue)))
5513
task_queue *queue = create_queue();
5514
g_assert_nonnull(queue);
5516
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5517
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5519
__attribute__((cleanup(cleanup_close)))
5520
const int read_socket = socketfds[0];
5521
const int write_socket = socketfds[1];
5522
task_context task = {
5523
.func=send_password_to_socket,
5524
.question_filename=strdup(question_filename),
5525
.filename=strdup(filename),
5528
.password=&password,
5529
.cancelled_filenames=&cancelled_filenames,
5530
.mandos_client_exited=(bool[]){false},
5531
.password_is_read=(bool[]){false},
5532
.current_time=(mono_microsecs[]){0},
5534
g_assert_nonnull(task.question_filename);
5536
task.func(task, queue);
5538
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5540
const task_context *const added_task = find_matching_task(queue,
5542
g_assert_nonnull(added_task);
5543
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5544
g_assert_true(queue->next_run == 0);
5546
g_assert_cmpint(added_task->fd, >, 0);
5547
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5552
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5553
test_fixture *fixture,
5554
__attribute__((unused))
5555
gconstpointer user_data){
5556
__attribute__((cleanup(cleanup_close)))
5557
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5558
g_assert_cmpint(epoll_fd, >=, 0);
5559
const char question_filename[] = "/nonexistent/question";
5560
char *const filename = strdup("/nonexistent/socket");
5561
__attribute__((cleanup(string_set_clear)))
5562
string_set cancelled_filenames = {};
5563
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5564
__attribute__((cleanup(cleanup_buffer)))
5566
.data=malloc(oversized),
5568
.allocated=oversized,
5570
g_assert_nonnull(password.data);
5571
if(mlock(password.data, password.allocated) != 0){
5572
g_assert_true(errno == EPERM or errno == ENOMEM);
5574
/* Construct test password buffer */
5575
/* Start with + since that is what the real procotol uses */
5576
password.data[0] = '+';
5577
/* Set a special character at string end just to mark the end */
5578
password.data[oversized-3] = 'y';
5579
/* Set NUL at buffer end, as suggested by the protocol */
5580
password.data[oversized-2] = '\0';
5581
/* Fill rest of password with 'x' */
5582
memset(password.data+1, 'x', oversized-3);
5584
__attribute__((cleanup(cleanup_queue)))
5585
task_queue *queue = create_queue();
5586
g_assert_nonnull(queue);
5588
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5589
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5591
__attribute__((cleanup(cleanup_close)))
5592
const int read_socket = socketfds[0];
5593
__attribute__((cleanup(cleanup_close)))
5594
const int write_socket = socketfds[1];
5595
task_context task = {
5596
.func=send_password_to_socket,
5597
.question_filename=strdup(question_filename),
5601
.password=&password,
5602
.cancelled_filenames=&cancelled_filenames,
5603
.mandos_client_exited=(bool[]){true},
5604
.password_is_read=(bool[]){true},
5605
.current_time=(mono_microsecs[]){0},
5607
g_assert_nonnull(task.question_filename);
5609
run_task_with_stderr_to_dev_null(task, queue);
5611
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5612
g_assert_true(string_set_contains(cancelled_filenames,
5613
question_filename));
5616
static void test_send_password_to_socket_retry(__attribute__((unused))
5617
test_fixture *fixture,
5618
__attribute__((unused))
5621
__attribute__((cleanup(cleanup_close)))
5622
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5623
g_assert_cmpint(epoll_fd, >=, 0);
5624
__attribute__((cleanup(cleanup_string)))
5625
char *const question_filename = strdup("/nonexistent/question");
5626
g_assert_nonnull(question_filename);
5627
__attribute__((cleanup(cleanup_string)))
5628
char *const filename = strdup("/nonexistent/socket");
5629
g_assert_nonnull(filename);
5630
__attribute__((cleanup(string_set_clear)))
5631
string_set cancelled_filenames = {};
5632
__attribute__((cleanup(cleanup_buffer)))
5633
buffer password = {};
5635
__attribute__((cleanup(cleanup_queue)))
5636
task_queue *queue = create_queue();
5637
g_assert_nonnull(queue);
5639
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5640
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5642
__attribute__((cleanup(cleanup_close)))
5643
const int read_socket = socketfds[0];
5644
const int write_socket = socketfds[1];
5645
/* Close the server side socket to force ECONNRESET on client */
5646
g_assert_cmpint(close(read_socket), ==, 0);
5647
task_context task = {
5648
.func=send_password_to_socket,
5649
.question_filename=strdup(question_filename),
5650
.filename=strdup(filename),
5653
.password=&password,
5654
.cancelled_filenames=&cancelled_filenames,
5655
.mandos_client_exited=(bool[]){true},
5656
.password_is_read=(bool[]){true},
5657
.current_time=(mono_microsecs[]){0},
5659
g_assert_nonnull(task.question_filename);
5661
task.func(task, queue);
5663
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5665
const task_context *const added_task = find_matching_task(queue,
5667
g_assert_nonnull(added_task);
5668
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5670
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5675
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
5676
test_fixture *fixture,
5677
__attribute__((unused))
5678
gconstpointer user_data){
5679
__attribute__((cleanup(cleanup_close)))
5680
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5681
__attribute__((cleanup(cleanup_string)))
5682
char *const question_filename = strdup("/nonexistent/question");
5683
g_assert_nonnull(question_filename);
5684
__attribute__((cleanup(cleanup_string)))
5685
char *const filename = strdup("/nonexistent/socket");
5686
g_assert_nonnull(filename);
5687
__attribute__((cleanup(string_set_clear)))
5688
string_set cancelled_filenames = {};
5689
__attribute__((cleanup(cleanup_buffer)))
5690
buffer password = {};
5692
const mono_microsecs current_time = 11;
5693
__attribute__((cleanup(cleanup_queue)))
5694
task_queue *queue = create_queue();
5695
g_assert_nonnull(queue);
5697
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5698
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5700
__attribute__((cleanup(cleanup_close)))
5701
const int read_socket = socketfds[0];
5702
const int write_socket = socketfds[1];
5703
/* Close the server side socket to force ECONNRESET on client */
5704
g_assert_cmpint(close(read_socket), ==, 0);
5705
task_context task = {
5706
.func=send_password_to_socket,
5707
.question_filename=strdup(question_filename),
5708
.filename=strdup(filename),
5711
.password=&password,
5712
.cancelled_filenames=&cancelled_filenames,
5713
.mandos_client_exited=(bool[]){true},
5714
.password_is_read=(bool[]){true},
5715
.current_time=¤t_time,
5717
g_assert_nonnull(task.question_filename);
5719
run_task_with_stderr_to_dev_null(task, queue);
5721
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5723
const task_context *const added_task = find_matching_task(queue,
5725
g_assert_nonnull(added_task);
5726
g_assert_true(queue->next_run == current_time + 1000000);
5727
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5730
static void assert_send_password_to_socket_password(buffer password){
5731
__attribute__((cleanup(cleanup_close)))
5732
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5733
g_assert_cmpint(epoll_fd, >=, 0);
5734
char *const question_filename = strdup("/nonexistent/question");
5735
g_assert_nonnull(question_filename);
5736
char *const filename = strdup("/nonexistent/socket");
5737
g_assert_nonnull(filename);
5738
__attribute__((cleanup(string_set_clear)))
5739
string_set cancelled_filenames = {};
5741
__attribute__((cleanup(cleanup_queue)))
5742
task_queue *queue = create_queue();
5743
g_assert_nonnull(queue);
5745
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5746
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5748
__attribute__((cleanup(cleanup_close)))
5749
const int read_socket = socketfds[0];
5750
const int write_socket = socketfds[1];
5751
task_context task = {
5752
.func=send_password_to_socket,
5753
.question_filename=question_filename,
5757
.password=&password,
5758
.cancelled_filenames=&cancelled_filenames,
5759
.mandos_client_exited=(bool[]){true},
5760
.password_is_read=(bool[]){true},
5761
.current_time=(mono_microsecs[]){0},
5764
char *expected_written_data = malloc(password.length + 2);
5765
g_assert_nonnull(expected_written_data);
5766
expected_written_data[0] = '+';
5767
expected_written_data[password.length + 1] = '\0';
5768
if(password.length > 0){
5769
g_assert_nonnull(password.data);
5770
memcpy(expected_written_data + 1, password.data, password.length);
5773
task.func(task, queue);
5776
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
5777
(int)(password.length + 2));
5778
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5780
g_assert_true(memcmp(expected_written_data, buf,
5781
password.length + 2) == 0);
5783
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
5785
free(expected_written_data);
5789
test_send_password_to_socket_null_password(__attribute__((unused))
5790
test_fixture *fixture,
5791
__attribute__((unused))
5792
gconstpointer user_data){
5793
__attribute__((cleanup(cleanup_buffer)))
5794
buffer password = {};
5795
assert_send_password_to_socket_password(password);
5799
test_send_password_to_socket_empty_password(__attribute__((unused))
5800
test_fixture *fixture,
5801
__attribute__((unused))
5802
gconstpointer user_data){
5803
__attribute__((cleanup(cleanup_buffer)))
5805
.data=malloc(1), /* because malloc(0) may return NULL */
5807
.allocated=0, /* deliberate lie */
5809
g_assert_nonnull(password.data);
5810
assert_send_password_to_socket_password(password);
5814
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
5815
test_fixture *fixture,
5816
__attribute__((unused))
5817
gconstpointer user_data){
5818
__attribute__((cleanup(cleanup_buffer)))
5824
if(mlock(password.data, password.allocated) != 0){
5825
g_assert_true(errno == EPERM or errno == ENOMEM);
5827
assert_send_password_to_socket_password(password);
5831
test_send_password_to_socket_text_password(__attribute__((unused))
5832
test_fixture *fixture,
5833
__attribute__((unused))
5834
gconstpointer user_data){
5835
const char dummy_test_password[] = "dummy test password";
5836
__attribute__((cleanup(cleanup_buffer)))
5838
.data = strdup(dummy_test_password),
5839
.length = strlen(dummy_test_password),
5840
.allocated = sizeof(dummy_test_password),
5842
if(mlock(password.data, password.allocated) != 0){
5843
g_assert_true(errno == EPERM or errno == ENOMEM);
5845
assert_send_password_to_socket_password(password);
5849
test_send_password_to_socket_binary_password(__attribute__((unused))
5850
test_fixture *fixture,
5851
__attribute__((unused))
5852
gconstpointer user_data){
5853
__attribute__((cleanup(cleanup_buffer)))
5859
g_assert_nonnull(password.data);
5860
if(mlock(password.data, password.allocated) != 0){
5861
g_assert_true(errno == EPERM or errno == ENOMEM);
5863
char c = 1; /* Start at 1, avoiding NUL */
5864
for(int i=0; i < 255; i++){
5865
password.data[i] = c++;
5867
assert_send_password_to_socket_password(password);
5871
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
5872
test_fixture *fixture,
5873
__attribute__((unused))
5876
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
5877
__attribute__((cleanup(cleanup_buffer)))
5879
.data=malloc(sizeof(test_password)),
5880
.length=sizeof(test_password),
5881
.allocated=sizeof(test_password),
5883
g_assert_nonnull(password.data);
5884
if(mlock(password.data, password.allocated) !=0){
5885
g_assert_true(errno == EPERM or errno == ENOMEM);
5887
memcpy(password.data, test_password, password.allocated);
5888
assert_send_password_to_socket_password(password);
5891
static bool assert_add_existing_questions_to_devnull(task_queue
5904
static void test_add_existing_questions_ENOENT(__attribute__((unused))
5905
test_fixture *fixture,
5906
__attribute__((unused))
5909
__attribute__((cleanup(cleanup_queue)))
5910
task_queue *queue = create_queue();
5911
g_assert_nonnull(queue);
5912
__attribute__((cleanup(cleanup_close)))
5913
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5914
g_assert_cmpint(epoll_fd, >=, 0);
5915
__attribute__((cleanup(string_set_clear)))
5916
string_set cancelled_filenames = {};
5918
g_assert_false(assert_add_existing_questions_to_devnull
5921
(buffer[]){{}}, /* password */
5922
&cancelled_filenames,
5923
(mono_microsecs[]){0}, /* current_time */
5924
(bool[]){false}, /* mandos_client_exited */
5925
(bool[]){false}, /* password_is_read */
5926
"/nonexistent")); /* dirname */
5928
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5932
bool assert_add_existing_questions_to_devnull(task_queue
5939
*cancelled_filenames,
5940
const mono_microsecs
5941
*const current_time,
5943
mandos_client_exited,
5948
__attribute__((cleanup(cleanup_close)))
5949
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5950
g_assert_cmpint(devnull_fd, >=, 0);
5951
__attribute__((cleanup(cleanup_close)))
5952
const int real_stderr_fd = dup(STDERR_FILENO);
5953
g_assert_cmpint(real_stderr_fd, >=, 0);
5954
dup2(devnull_fd, STDERR_FILENO);
5955
const bool ret = add_existing_questions(queue, epoll_fd, password,
5956
cancelled_filenames,
5958
mandos_client_exited,
5959
password_is_read, dirname);
5960
dup2(real_stderr_fd, STDERR_FILENO);
5965
void test_add_existing_questions_no_questions(__attribute__((unused))
5966
test_fixture *fixture,
5967
__attribute__((unused))
5970
__attribute__((cleanup(cleanup_queue)))
5971
task_queue *queue = create_queue();
5972
g_assert_nonnull(queue);
5973
__attribute__((cleanup(cleanup_close)))
5974
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5975
g_assert_cmpint(epoll_fd, >=, 0);
5976
__attribute__((cleanup(string_set_clear)))
5977
string_set cancelled_filenames = {};
5978
__attribute__((cleanup(cleanup_string)))
5979
char *tempdir = make_temporary_directory();
5980
g_assert_nonnull(tempdir);
5982
g_assert_false(assert_add_existing_questions_to_devnull
5985
(buffer[]){{}}, /* password */
5986
&cancelled_filenames,
5987
(mono_microsecs[]){0}, /* current_time */
5988
(bool[]){false}, /* mandos_client_exited */
5989
(bool[]){false}, /* password_is_read */
5992
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5994
g_assert_cmpint(rmdir(tempdir), ==, 0);
5997
static char *make_question_file_in_directory(const char *const);
6000
void test_add_existing_questions_one_question(__attribute__((unused))
6001
test_fixture *fixture,
6002
__attribute__((unused))
6005
__attribute__((cleanup(cleanup_queue)))
6006
task_queue *queue = create_queue();
6007
g_assert_nonnull(queue);
6008
__attribute__((cleanup(cleanup_close)))
6009
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6010
g_assert_cmpint(epoll_fd, >=, 0);
6011
__attribute__((cleanup(cleanup_buffer)))
6012
buffer password = {};
6013
__attribute__((cleanup(string_set_clear)))
6014
string_set cancelled_filenames = {};
6015
const mono_microsecs current_time = 0;
6016
bool mandos_client_exited = false;
6017
bool password_is_read = false;
6018
__attribute__((cleanup(cleanup_string)))
6019
char *tempdir = make_temporary_directory();
6020
g_assert_nonnull(tempdir);
6021
__attribute__((cleanup(cleanup_string)))
6022
char *question_filename
6023
= make_question_file_in_directory(tempdir);
6024
g_assert_nonnull(question_filename);
6026
g_assert_true(assert_add_existing_questions_to_devnull
6030
&cancelled_filenames,
6032
&mandos_client_exited,
6036
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6038
g_assert_nonnull(find_matching_task(queue, (task_context){
6039
.func=open_and_parse_question,
6041
.filename=question_filename,
6042
.question_filename=question_filename,
6043
.password=&password,
6044
.cancelled_filenames=&cancelled_filenames,
6045
.current_time=¤t_time,
6046
.mandos_client_exited=&mandos_client_exited,
6047
.password_is_read=&password_is_read,
6050
g_assert_true(queue->next_run == 1);
6052
g_assert_cmpint(unlink(question_filename), ==, 0);
6053
g_assert_cmpint(rmdir(tempdir), ==, 0);
6056
static char *make_question_file_in_directory(const char
6058
return make_temporary_prefixed_file_in_directory("ask.", dir);
6062
void test_add_existing_questions_two_questions(__attribute__((unused))
6063
test_fixture *fixture,
6064
__attribute__((unused))
6067
__attribute__((cleanup(cleanup_queue)))
6068
task_queue *queue = create_queue();
6069
g_assert_nonnull(queue);
6070
__attribute__((cleanup(cleanup_close)))
6071
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6072
g_assert_cmpint(epoll_fd, >=, 0);
6073
__attribute__((cleanup(cleanup_buffer)))
6074
buffer password = {};
6075
__attribute__((cleanup(string_set_clear)))
6076
string_set cancelled_filenames = {};
6077
const mono_microsecs current_time = 0;
6078
bool mandos_client_exited = false;
6079
bool password_is_read = false;
6080
__attribute__((cleanup(cleanup_string)))
6081
char *tempdir = make_temporary_directory();
6082
g_assert_nonnull(tempdir);
6083
__attribute__((cleanup(cleanup_string)))
6084
char *question_filename1
6085
= make_question_file_in_directory(tempdir);
6086
g_assert_nonnull(question_filename1);
6087
__attribute__((cleanup(cleanup_string)))
6088
char *question_filename2
6089
= make_question_file_in_directory(tempdir);
6090
g_assert_nonnull(question_filename2);
6092
g_assert_true(assert_add_existing_questions_to_devnull
6096
&cancelled_filenames,
6098
&mandos_client_exited,
6102
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6104
g_assert_true(queue->next_run == 1);
6106
__attribute__((cleanup(string_set_clear)))
6107
string_set seen_questions = {};
6109
bool queue_contains_question_opener(char *const question_filename){
6110
return(find_matching_task(queue, (task_context){
6111
.func=open_and_parse_question,
6113
.question_filename=question_filename,
6114
.password=&password,
6115
.cancelled_filenames=&cancelled_filenames,
6116
.current_time=¤t_time,
6117
.mandos_client_exited=&mandos_client_exited,
6118
.password_is_read=&password_is_read,
6122
g_assert_true(queue_contains_question_opener(question_filename1));
6123
g_assert_true(queue_contains_question_opener(question_filename2));
6125
g_assert_true(queue->next_run == 1);
6127
g_assert_cmpint(unlink(question_filename1), ==, 0);
6128
g_assert_cmpint(unlink(question_filename2), ==, 0);
6129
g_assert_cmpint(rmdir(tempdir), ==, 0);
6133
test_add_existing_questions_non_questions(__attribute__((unused))
6134
test_fixture *fixture,
6135
__attribute__((unused))
6136
gconstpointer user_data){
6137
__attribute__((cleanup(cleanup_queue)))
6138
task_queue *queue = create_queue();
6139
g_assert_nonnull(queue);
6140
__attribute__((cleanup(cleanup_close)))
6141
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6142
g_assert_cmpint(epoll_fd, >=, 0);
6143
__attribute__((cleanup(string_set_clear)))
6144
string_set cancelled_filenames = {};
6145
__attribute__((cleanup(cleanup_string)))
6146
char *tempdir = make_temporary_directory();
6147
g_assert_nonnull(tempdir);
6148
__attribute__((cleanup(cleanup_string)))
6149
char *question_filename1
6150
= make_temporary_file_in_directory(tempdir);
6151
g_assert_nonnull(question_filename1);
6152
__attribute__((cleanup(cleanup_string)))
6153
char *question_filename2
6154
= make_temporary_file_in_directory(tempdir);
6155
g_assert_nonnull(question_filename2);
6157
g_assert_false(assert_add_existing_questions_to_devnull
6160
(buffer[]){{}}, /* password */
6161
&cancelled_filenames,
6162
(mono_microsecs[]){0}, /* current_time */
6163
(bool[]){false}, /* mandos_client_exited */
6164
(bool[]){false}, /* password_is_read */
6167
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6169
g_assert_cmpint(unlink(question_filename1), ==, 0);
6170
g_assert_cmpint(unlink(question_filename2), ==, 0);
6171
g_assert_cmpint(rmdir(tempdir), ==, 0);
6175
test_add_existing_questions_both_types(__attribute__((unused))
6176
test_fixture *fixture,
6177
__attribute__((unused))
6178
gconstpointer user_data){
6179
__attribute__((cleanup(cleanup_queue)))
6180
task_queue *queue = create_queue();
6181
g_assert_nonnull(queue);
6182
__attribute__((cleanup(cleanup_close)))
6183
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6184
g_assert_cmpint(epoll_fd, >=, 0);
6185
__attribute__((cleanup(cleanup_buffer)))
6186
buffer password = {};
6187
__attribute__((cleanup(string_set_clear)))
6188
string_set cancelled_filenames = {};
6189
const mono_microsecs current_time = 0;
6190
bool mandos_client_exited = false;
6191
bool password_is_read = false;
6192
__attribute__((cleanup(cleanup_string)))
6193
char *tempdir = make_temporary_directory();
6194
g_assert_nonnull(tempdir);
6195
__attribute__((cleanup(cleanup_string)))
6196
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6197
g_assert_nonnull(tempfilename1);
6198
__attribute__((cleanup(cleanup_string)))
6199
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6200
g_assert_nonnull(tempfilename2);
6201
__attribute__((cleanup(cleanup_string)))
6202
char *question_filename
6203
= make_question_file_in_directory(tempdir);
6204
g_assert_nonnull(question_filename);
6206
g_assert_true(assert_add_existing_questions_to_devnull
6210
&cancelled_filenames,
6212
&mandos_client_exited,
6216
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6218
g_assert_nonnull(find_matching_task(queue, (task_context){
6219
.func=open_and_parse_question,
6221
.filename=question_filename,
6222
.question_filename=question_filename,
6223
.password=&password,
6224
.cancelled_filenames=&cancelled_filenames,
6225
.current_time=¤t_time,
6226
.mandos_client_exited=&mandos_client_exited,
6227
.password_is_read=&password_is_read,
6230
g_assert_true(queue->next_run == 1);
6232
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6233
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6234
g_assert_cmpint(unlink(question_filename), ==, 0);
6235
g_assert_cmpint(rmdir(tempdir), ==, 0);
6238
static void test_wait_for_event_timeout(__attribute__((unused))
6239
test_fixture *fixture,
6240
__attribute__((unused))
6241
gconstpointer user_data){
6242
__attribute__((cleanup(cleanup_close)))
6243
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6244
g_assert_cmpint(epoll_fd, >=, 0);
6246
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6249
static void test_wait_for_event_event(__attribute__((unused))
6250
test_fixture *fixture,
6251
__attribute__((unused))
6252
gconstpointer user_data){
6253
__attribute__((cleanup(cleanup_close)))
6254
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6255
g_assert_cmpint(epoll_fd, >=, 0);
6257
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6258
__attribute__((cleanup(cleanup_close)))
6259
const int read_pipe = pipefds[0];
6260
__attribute__((cleanup(cleanup_close)))
6261
const int write_pipe = pipefds[1];
6262
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6263
&(struct epoll_event)
6264
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6265
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6267
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6270
static void test_wait_for_event_sigchld(test_fixture *fixture,
6271
__attribute__((unused))
6272
gconstpointer user_data){
6273
const pid_t pid = fork();
6274
if(pid == 0){ /* Child */
6275
if(not restore_signal_handler(&fixture->orig_sigaction)){
6276
_exit(EXIT_FAILURE);
6278
if(not restore_sigmask(&fixture->orig_sigmask)){
6279
_exit(EXIT_FAILURE);
6283
g_assert_true(pid != -1);
6284
__attribute__((cleanup(cleanup_close)))
6285
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6286
g_assert_cmpint(epoll_fd, >=, 0);
6288
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6291
g_assert_true(waitpid(pid, &status, 0) == pid);
6292
g_assert_true(WIFEXITED(status));
6293
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6296
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6297
test_fixture *fixture,
6298
__attribute__((unused))
6299
gconstpointer user_data){
6300
__attribute__((cleanup(cleanup_queue)))
6301
task_queue *queue = create_queue();
6302
g_assert_nonnull(queue);
6303
queue->next_run = 1;
6304
__attribute__((cleanup(cleanup_close)))
6305
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6306
__attribute__((cleanup(string_set_clear)))
6307
string_set cancelled_filenames = {};
6308
bool quit_now = false;
6310
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6311
g_assert_false(quit_now);
6312
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6316
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6317
test_fixture *fixture,
6318
__attribute__((unused))
6321
__attribute__((cleanup(cleanup_queue)))
6322
task_queue *queue = create_queue();
6323
g_assert_nonnull(queue);
6324
__attribute__((cleanup(string_set_clear)))
6325
string_set cancelled_filenames = {};
6326
bool quit_now = false;
6327
const char question_filename[] = "/nonexistent/question_filename";
6328
g_assert_true(string_set_add(&cancelled_filenames,
6329
question_filename));
6331
g_assert_true(add_to_queue(queue,
6332
(task_context){ .func=dummy_func }));
6334
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6335
g_assert_false(quit_now);
6336
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6337
g_assert_false(string_set_contains(cancelled_filenames,
6338
question_filename));
6342
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6343
test_fixture *fixture,
6344
__attribute__((unused))
6347
__attribute__((cleanup(cleanup_queue)))
6348
task_queue *queue = create_queue();
6349
g_assert_nonnull(queue);
6350
__attribute__((cleanup(string_set_clear)))
6351
string_set cancelled_filenames = {};
6352
bool quit_now = false;
6354
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6355
__attribute__((cleanup(cleanup_close)))
6356
const int read_pipe = pipefds[0];
6357
g_assert_cmpint(close(pipefds[1]), ==, 0);
6358
const char question_filename[] = "/nonexistent/question_filename";
6359
g_assert_true(string_set_add(&cancelled_filenames,
6360
question_filename));
6361
__attribute__((nonnull))
6362
void quit_func(const task_context task,
6363
__attribute__((unused)) task_queue *const q){
6364
g_assert_nonnull(task.quit_now);
6365
*task.quit_now = true;
6367
task_context task = {
6369
.question_filename=strdup(question_filename),
6370
.quit_now=&quit_now,
6373
g_assert_nonnull(task.question_filename);
6375
g_assert_true(add_to_queue(queue, task));
6377
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6378
g_assert_false(quit_now);
6380
/* read_pipe should be closed already */
6382
bool read_pipe_closed = (close(read_pipe) == -1);
6383
read_pipe_closed &= (errno == EBADF);
6384
g_assert_true(read_pipe_closed);
6387
static void test_run_queue_one_task(__attribute__((unused))
6388
test_fixture *fixture,
6389
__attribute__((unused))
6390
gconstpointer user_data){
6391
__attribute__((cleanup(cleanup_queue)))
6392
task_queue *queue = create_queue();
6393
g_assert_nonnull(queue);
6394
__attribute__((cleanup(string_set_clear)))
6395
string_set cancelled_filenames = {};
6396
bool quit_now = false;
6398
__attribute__((nonnull))
6399
void next_run_func(__attribute__((unused))
6400
const task_context task,
6401
task_queue *const q){
6405
task_context task = {
6406
.func=next_run_func,
6408
g_assert_true(add_to_queue(queue, task));
6410
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6411
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6412
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6415
static void test_run_queue_two_tasks(__attribute__((unused))
6416
test_fixture *fixture,
6417
__attribute__((unused))
6418
gconstpointer user_data){
6419
__attribute__((cleanup(cleanup_queue)))
6420
task_queue *queue = create_queue();
6421
g_assert_nonnull(queue);
6422
queue->next_run = 1;
6423
__attribute__((cleanup(string_set_clear)))
6424
string_set cancelled_filenames = {};
6425
bool quit_now = false;
6426
bool mandos_client_exited = false;
6428
__attribute__((nonnull))
6429
void next_run_func(__attribute__((unused))
6430
const task_context task,
6431
task_queue *const q){
6435
__attribute__((nonnull))
6436
void exited_func(const task_context task,
6437
__attribute__((unused)) task_queue *const q){
6438
*task.mandos_client_exited = true;
6441
task_context task1 = {
6442
.func=next_run_func,
6444
g_assert_true(add_to_queue(queue, task1));
6446
task_context task2 = {
6448
.mandos_client_exited=&mandos_client_exited,
6450
g_assert_true(add_to_queue(queue, task2));
6452
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6453
g_assert_false(quit_now);
6454
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6455
g_assert_true(mandos_client_exited);
6456
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6459
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6460
test_fixture *fixture,
6461
__attribute__((unused))
6462
gconstpointer user_data){
6463
__attribute__((cleanup(cleanup_queue)))
6464
task_queue *queue = create_queue();
6465
g_assert_nonnull(queue);
6466
__attribute__((cleanup(string_set_clear)))
6467
string_set cancelled_filenames = {};
6468
bool quit_now = false;
6469
bool mandos_client_exited = false;
6470
bool password_is_read = false;
6472
__attribute__((nonnull))
6473
void set_exited_func(const task_context task,
6474
__attribute__((unused)) task_queue *const q){
6475
*task.mandos_client_exited = true;
6476
*task.quit_now = true;
6478
task_context task1 = {
6479
.func=set_exited_func,
6480
.quit_now=&quit_now,
6481
.mandos_client_exited=&mandos_client_exited,
6483
g_assert_true(add_to_queue(queue, task1));
6485
__attribute__((nonnull))
6486
void set_read_func(const task_context task,
6487
__attribute__((unused)) task_queue *const q){
6488
*task.quit_now = true;
6489
*task.password_is_read = true;
6491
task_context task2 = {
6492
.func=set_read_func,
6493
.quit_now=&quit_now,
6494
.password_is_read=&password_is_read,
6496
g_assert_true(add_to_queue(queue, task2));
6498
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6499
g_assert_true(quit_now);
6500
g_assert_true(mandos_client_exited xor password_is_read);
6501
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6504
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6505
test_fixture *fixture,
6506
__attribute__((unused))
6507
gconstpointer user_data){
6508
__attribute__((cleanup(cleanup_queue)))
6509
task_queue *queue = create_queue();
6510
g_assert_nonnull(queue);
6511
__attribute__((cleanup(string_set_clear)))
6512
string_set cancelled_filenames = {};
6514
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6515
__attribute__((cleanup(cleanup_close)))
6516
const int read_pipe = pipefds[0];
6517
__attribute__((cleanup(cleanup_close)))
6518
const int write_pipe = pipefds[1];
6519
bool quit_now = false;
6521
__attribute__((nonnull))
6522
void read_func(const task_context task,
6523
__attribute__((unused)) task_queue *const q){
6524
*task.quit_now = true;
6526
task_context task1 = {
6528
.quit_now=&quit_now,
6531
g_assert_true(add_to_queue(queue, task1));
6533
__attribute__((nonnull))
6534
void write_func(const task_context task,
6535
__attribute__((unused)) task_queue *const q){
6536
*task.quit_now = true;
6538
task_context task2 = {
6540
.quit_now=&quit_now,
6543
g_assert_true(add_to_queue(queue, task2));
6545
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6546
g_assert_true(quit_now);
6548
/* Either read_pipe or write_pipe should be closed already */
6550
bool close_read_pipe = (close(read_pipe) == -1);
6551
close_read_pipe &= (errno == EBADF);
6553
bool close_write_pipe = (close(write_pipe) == -1);
6554
close_write_pipe &= (errno == EBADF);
6555
g_assert_true(close_read_pipe xor close_write_pipe);
6556
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6559
static void test_setup_signal_handler(__attribute__((unused))
6560
test_fixture *fixture,
6561
__attribute__((unused))
6562
gconstpointer user_data){
6563
/* Save current SIGCHLD action, whatever it is */
6564
struct sigaction expected_sigchld_action;
6565
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6568
/* Act; i.e. run the setup_signal_handler() function */
6569
struct sigaction actual_old_sigchld_action;
6570
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6572
/* Check that the function correctly set "actual_old_sigchld_action"
6573
to the same values as the previously saved
6574
"expected_sigchld_action" */
6575
/* Check member sa_handler */
6576
g_assert_true(actual_old_sigchld_action.sa_handler
6577
== expected_sigchld_action.sa_handler);
6578
/* Check member sa_mask */
6579
for(int signum = 1; signum < NSIG; signum++){
6580
const int expected_old_block_state
6581
= sigismember(&expected_sigchld_action.sa_mask, signum);
6582
g_assert_cmpint(expected_old_block_state, >=, 0);
6583
const int actual_old_block_state
6584
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6585
g_assert_cmpint(actual_old_block_state, >=, 0);
6586
g_assert_cmpint(actual_old_block_state,
6587
==, expected_old_block_state);
6589
/* Check member sa_flags */
6590
g_assert_true((actual_old_sigchld_action.sa_flags
6591
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6592
== (expected_sigchld_action.sa_flags
6593
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6595
/* Retrieve the current signal handler for SIGCHLD as set by
6596
setup_signal_handler() */
6597
struct sigaction actual_new_sigchld_action;
6598
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6599
&actual_new_sigchld_action), ==, 0);
6600
/* Check that the signal handler (member sa_handler) is correctly
6601
set to the "handle_sigchld" function */
6602
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6603
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6604
g_assert_true(actual_new_sigchld_action.sa_handler
6606
/* Check (in member sa_mask) that at least a handful of signals are
6607
actually blocked during the signal handler */
6608
for(int signum = 1; signum < NSIG; signum++){
6609
int actual_new_block_state;
6615
actual_new_block_state
6616
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6617
g_assert_cmpint(actual_new_block_state, ==, 1);
6619
case SIGKILL: /* non-blockable */
6620
case SIGSTOP: /* non-blockable */
6621
case SIGCHLD: /* always blocked */
6626
/* Check member sa_flags */
6627
g_assert_true((actual_new_sigchld_action.sa_flags
6628
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6629
== (SA_NOCLDSTOP | SA_RESTART));
6631
/* Restore signal handler */
6632
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6636
static void test_restore_signal_handler(__attribute__((unused))
6637
test_fixture *fixture,
6638
__attribute__((unused))
6639
gconstpointer user_data){
6640
/* Save current SIGCHLD action, whatever it is */
6641
struct sigaction expected_sigchld_action;
6642
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6644
/* Since we haven't established a signal handler yet, there should
6645
not be one established. But another test may have relied on
6646
restore_signal_handler() to restore the signal handler, and if
6647
restore_signal_handler() is buggy (which we should be prepared
6648
for in this test) the signal handler may not have been restored
6649
properly; check for this: */
6650
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
6652
/* Establish a signal handler */
6653
struct sigaction sigchld_action = {
6654
.sa_handler=handle_sigchld,
6655
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
6657
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
6658
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
6660
/* Act; i.e. run the restore_signal_handler() function */
6661
g_assert_true(restore_signal_handler(&expected_sigchld_action));
6663
/* Retrieve the restored signal handler data */
6664
struct sigaction actual_restored_sigchld_action;
6665
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6666
&actual_restored_sigchld_action), ==, 0);
6668
/* Check that the function correctly restored the signal action, as
6669
saved in "actual_restored_sigchld_action", to the same values as
6670
the previously saved "expected_sigchld_action" */
6671
/* Check member sa_handler */
6672
g_assert_true(actual_restored_sigchld_action.sa_handler
6673
== expected_sigchld_action.sa_handler);
6674
/* Check member sa_mask */
6675
for(int signum = 1; signum < NSIG; signum++){
6676
const int expected_old_block_state
6677
= sigismember(&expected_sigchld_action.sa_mask, signum);
6678
g_assert_cmpint(expected_old_block_state, >=, 0);
6679
const int actual_restored_block_state
6680
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
6681
g_assert_cmpint(actual_restored_block_state, >=, 0);
6682
g_assert_cmpint(actual_restored_block_state,
6683
==, expected_old_block_state);
6685
/* Check member sa_flags */
6686
g_assert_true((actual_restored_sigchld_action.sa_flags
6687
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6688
== (expected_sigchld_action.sa_flags
6689
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6692
static void test_block_sigchld(__attribute__((unused))
6693
test_fixture *fixture,
6694
__attribute__((unused))
6695
gconstpointer user_data){
6696
/* Save original signal mask */
6697
sigset_t expected_sigmask;
6698
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
6701
/* Make sure SIGCHLD is unblocked for this test */
6702
sigset_t sigchld_sigmask;
6703
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6704
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6705
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
6708
/* Act; i.e. run the block_sigchld() function */
6709
sigset_t actual_old_sigmask;
6710
g_assert_true(block_sigchld(&actual_old_sigmask));
6712
/* Check the actual_old_sigmask; it should be the same as the
6713
previously saved signal mask "expected_sigmask". */
6714
for(int signum = 1; signum < NSIG; signum++){
6715
const int expected_old_block_state
6716
= sigismember(&expected_sigmask, signum);
6717
g_assert_cmpint(expected_old_block_state, >=, 0);
6718
const int actual_old_block_state
6719
= sigismember(&actual_old_sigmask, signum);
6720
g_assert_cmpint(actual_old_block_state, >=, 0);
6721
g_assert_cmpint(actual_old_block_state,
6722
==, expected_old_block_state);
6725
/* Retrieve the newly set signal mask */
6726
sigset_t actual_sigmask;
6727
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
6729
/* SIGCHLD should be blocked */
6730
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
6732
/* Restore signal mask */
6733
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
6737
static void test_restore_sigmask(__attribute__((unused))
6738
test_fixture *fixture,
6739
__attribute__((unused))
6740
gconstpointer user_data){
6741
/* Save original signal mask */
6742
sigset_t orig_sigmask;
6743
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
6745
/* Make sure SIGCHLD is blocked for this test */
6746
sigset_t sigchld_sigmask;
6747
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6748
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6749
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
6752
/* Act; i.e. run the restore_sigmask() function */
6753
g_assert_true(restore_sigmask(&orig_sigmask));
6755
/* Retrieve the newly restored signal mask */
6756
sigset_t restored_sigmask;
6757
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
6760
/* Check the restored_sigmask; it should be the same as the
6761
previously saved signal mask "orig_sigmask". */
6762
for(int signum = 1; signum < NSIG; signum++){
6763
const int orig_block_state = sigismember(&orig_sigmask, signum);
6764
g_assert_cmpint(orig_block_state, >=, 0);
6765
const int restored_block_state = sigismember(&restored_sigmask,
6767
g_assert_cmpint(restored_block_state, >=, 0);
6768
g_assert_cmpint(restored_block_state, ==, orig_block_state);
6771
/* Restore signal mask */
6772
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
6776
static void test_parse_arguments_noargs(__attribute__((unused))
6777
test_fixture *fixture,
6778
__attribute__((unused))
6779
gconstpointer user_data){
6783
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6785
char *agent_directory = NULL;
6786
char *helper_directory = NULL;
6789
char *mandos_argz = NULL;
6790
size_t mandos_argz_length = 0;
6792
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6793
&helper_directory, &user, &group,
6794
&mandos_argz, &mandos_argz_length));
6795
g_assert_null(agent_directory);
6796
g_assert_null(helper_directory);
6797
g_assert_true(user == 0);
6798
g_assert_true(group == 0);
6799
g_assert_null(mandos_argz);
6800
g_assert_true(mandos_argz_length == 0);
6802
for(char **arg = argv; *arg != NULL; arg++){
6807
__attribute__((nonnull))
6808
static bool parse_arguments_devnull(int argc, char *argv[],
6809
const bool exit_failure,
6810
char **agent_directory,
6811
char **helper_directory,
6815
size_t *mandos_argz_length){
6817
FILE *real_stderr = stderr;
6818
FILE *devnull = fopen("/dev/null", "we");
6819
g_assert_nonnull(devnull);
6822
const bool ret = parse_arguments(argc, argv, exit_failure,
6824
helper_directory, user, group,
6825
mandos_argz, mandos_argz_length);
6826
const error_t saved_errno = errno;
6828
stderr = real_stderr;
6829
g_assert_cmpint(fclose(devnull), ==, 0);
6831
errno = saved_errno;
6836
static void test_parse_arguments_invalid(__attribute__((unused))
6837
test_fixture *fixture,
6838
__attribute__((unused))
6839
gconstpointer user_data){
6842
strdup("--invalid"),
6844
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6846
char *agent_directory = NULL;
6847
char *helper_directory = NULL;
6850
char *mandos_argz = NULL;
6851
size_t mandos_argz_length = 0;
6853
g_assert_false(parse_arguments_devnull(argc, argv, false,
6855
&helper_directory, &user,
6856
&group, &mandos_argz,
6857
&mandos_argz_length));
6859
g_assert_true(errno == EINVAL);
6860
g_assert_null(agent_directory);
6861
g_assert_null(helper_directory);
6862
g_assert_null(mandos_argz);
6863
g_assert_true(mandos_argz_length == 0);
6865
for(char **arg = argv; *arg != NULL; arg++){
6870
static void test_parse_arguments_long_dir(__attribute__((unused))
6871
test_fixture *fixture,
6872
__attribute__((unused))
6873
gconstpointer user_data){
6876
strdup("--agent-directory"),
6879
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6881
__attribute__((cleanup(cleanup_string)))
6882
char *agent_directory = NULL;
6883
char *helper_directory = NULL;
6886
__attribute__((cleanup(cleanup_string)))
6887
char *mandos_argz = NULL;
6888
size_t mandos_argz_length = 0;
6890
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6891
&helper_directory, &user, &group,
6892
&mandos_argz, &mandos_argz_length));
6894
g_assert_cmpstr(agent_directory, ==, "/tmp");
6895
g_assert_null(helper_directory);
6896
g_assert_true(user == 0);
6897
g_assert_true(group == 0);
6898
g_assert_null(mandos_argz);
6899
g_assert_true(mandos_argz_length == 0);
6901
for(char **arg = argv; *arg != NULL; arg++){
6906
static void test_parse_arguments_short_dir(__attribute__((unused))
6907
test_fixture *fixture,
6908
__attribute__((unused))
6909
gconstpointer user_data){
6915
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6917
__attribute__((cleanup(cleanup_string)))
6918
char *agent_directory = NULL;
6919
char *helper_directory = NULL;
6922
__attribute__((cleanup(cleanup_string)))
6923
char *mandos_argz = NULL;
6924
size_t mandos_argz_length = 0;
6926
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6927
&helper_directory, &user, &group,
6928
&mandos_argz, &mandos_argz_length));
6930
g_assert_cmpstr(agent_directory, ==, "/tmp");
6931
g_assert_null(helper_directory);
6932
g_assert_true(user == 0);
6933
g_assert_true(group == 0);
6934
g_assert_null(mandos_argz);
6935
g_assert_true(mandos_argz_length == 0);
6937
for(char **arg = argv; *arg != NULL; arg++){
6943
void test_parse_arguments_helper_directory(__attribute__((unused))
6944
test_fixture *fixture,
6945
__attribute__((unused))
6946
gconstpointer user_data){
6949
strdup("--helper-directory"),
6952
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6954
char *agent_directory = NULL;
6955
__attribute__((cleanup(cleanup_string)))
6956
char *helper_directory = NULL;
6959
__attribute__((cleanup(cleanup_string)))
6960
char *mandos_argz = NULL;
6961
size_t mandos_argz_length = 0;
6963
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
6964
&helper_directory, &user, &group,
6965
&mandos_argz, &mandos_argz_length));
6967
g_assert_cmpstr(helper_directory, ==, "/tmp");
6968
g_assert_null(agent_directory);
6969
g_assert_true(user == 0);
6970
g_assert_true(group == 0);
6971
g_assert_null(mandos_argz);
6972
g_assert_true(mandos_argz_length == 0);
6974
for(char **arg = argv; *arg != NULL; arg++){
6980
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
6981
test_fixture *fixture,
6982
__attribute__((unused))
6983
gconstpointer user_data){
6986
strdup("--plugin-helper-dir"),
6989
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
6991
char *agent_directory = NULL;
6992
__attribute__((cleanup(cleanup_string)))
6993
char *helper_directory = NULL;
6996
__attribute__((cleanup(cleanup_string)))
6997
char *mandos_argz = NULL;
6998
size_t mandos_argz_length = 0;
7000
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7001
&helper_directory, &user, &group,
7002
&mandos_argz, &mandos_argz_length));
7004
g_assert_cmpstr(helper_directory, ==, "/tmp");
7005
g_assert_null(agent_directory);
7006
g_assert_true(user == 0);
7007
g_assert_true(group == 0);
7008
g_assert_null(mandos_argz);
7009
g_assert_true(mandos_argz_length == 0);
7011
for(char **arg = argv; *arg != NULL; arg++){
7016
static void test_parse_arguments_user(__attribute__((unused))
7017
test_fixture *fixture,
7018
__attribute__((unused))
7019
gconstpointer user_data){
7025
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7027
char *agent_directory = NULL;
7028
__attribute__((cleanup(cleanup_string)))
7029
char *helper_directory = NULL;
7032
__attribute__((cleanup(cleanup_string)))
7033
char *mandos_argz = NULL;
7034
size_t mandos_argz_length = 0;
7036
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7037
&helper_directory, &user, &group,
7038
&mandos_argz, &mandos_argz_length));
7040
g_assert_null(helper_directory);
7041
g_assert_null(agent_directory);
7042
g_assert_cmpuint((unsigned int)user, ==, 1000);
7043
g_assert_true(group == 0);
7044
g_assert_null(mandos_argz);
7045
g_assert_true(mandos_argz_length == 0);
7047
for(char **arg = argv; *arg != NULL; arg++){
7052
static void test_parse_arguments_user_invalid(__attribute__((unused))
7053
test_fixture *fixture,
7054
__attribute__((unused))
7062
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7064
char *agent_directory = NULL;
7065
__attribute__((cleanup(cleanup_string)))
7066
char *helper_directory = NULL;
7069
__attribute__((cleanup(cleanup_string)))
7070
char *mandos_argz = NULL;
7071
size_t mandos_argz_length = 0;
7073
g_assert_false(parse_arguments_devnull(argc, argv, false,
7075
&helper_directory, &user,
7076
&group, &mandos_argz,
7077
&mandos_argz_length));
7079
g_assert_null(helper_directory);
7080
g_assert_null(agent_directory);
7081
g_assert_cmpuint((unsigned int)user, ==, 0);
7082
g_assert_true(group == 0);
7083
g_assert_null(mandos_argz);
7084
g_assert_true(mandos_argz_length == 0);
7086
for(char **arg = argv; *arg != NULL; arg++){
7092
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7093
test_fixture *fixture,
7094
__attribute__((unused))
7095
gconstpointer user_data){
7101
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7103
char *agent_directory = NULL;
7104
__attribute__((cleanup(cleanup_string)))
7105
char *helper_directory = NULL;
7108
__attribute__((cleanup(cleanup_string)))
7109
char *mandos_argz = NULL;
7110
size_t mandos_argz_length = 0;
7112
g_assert_false(parse_arguments_devnull(argc, argv, false,
7114
&helper_directory, &user,
7115
&group, &mandos_argz,
7116
&mandos_argz_length));
7118
g_assert_null(helper_directory);
7119
g_assert_null(agent_directory);
7120
g_assert_cmpuint((unsigned int)user, ==, 0);
7121
g_assert_true(group == 0);
7122
g_assert_null(mandos_argz);
7123
g_assert_true(mandos_argz_length == 0);
7125
for(char **arg = argv; *arg != NULL; arg++){
7130
static void test_parse_arguments_group(__attribute__((unused))
7131
test_fixture *fixture,
7132
__attribute__((unused))
7133
gconstpointer user_data){
7139
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7141
char *agent_directory = NULL;
7142
__attribute__((cleanup(cleanup_string)))
7143
char *helper_directory = NULL;
7146
__attribute__((cleanup(cleanup_string)))
7147
char *mandos_argz = NULL;
7148
size_t mandos_argz_length = 0;
7150
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7151
&helper_directory, &user, &group,
7152
&mandos_argz, &mandos_argz_length));
7154
g_assert_null(helper_directory);
7155
g_assert_null(agent_directory);
7156
g_assert_true(user == 0);
7157
g_assert_cmpuint((unsigned int)group, ==, 1000);
7158
g_assert_null(mandos_argz);
7159
g_assert_true(mandos_argz_length == 0);
7161
for(char **arg = argv; *arg != NULL; arg++){
7166
static void test_parse_arguments_group_invalid(__attribute__((unused))
7167
test_fixture *fixture,
7168
__attribute__((unused))
7176
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7178
char *agent_directory = NULL;
7179
__attribute__((cleanup(cleanup_string)))
7180
char *helper_directory = NULL;
7183
__attribute__((cleanup(cleanup_string)))
7184
char *mandos_argz = NULL;
7185
size_t mandos_argz_length = 0;
7187
g_assert_false(parse_arguments_devnull(argc, argv, false,
7189
&helper_directory, &user,
7190
&group, &mandos_argz,
7191
&mandos_argz_length));
7193
g_assert_null(helper_directory);
7194
g_assert_null(agent_directory);
7195
g_assert_true(user == 0);
7196
g_assert_true(group == 0);
7197
g_assert_null(mandos_argz);
7198
g_assert_true(mandos_argz_length == 0);
7200
for(char **arg = argv; *arg != NULL; arg++){
7206
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7207
test_fixture *fixture,
7208
__attribute__((unused))
7209
gconstpointer user_data){
7215
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7217
char *agent_directory = NULL;
7218
__attribute__((cleanup(cleanup_string)))
7219
char *helper_directory = NULL;
7222
__attribute__((cleanup(cleanup_string)))
7223
char *mandos_argz = NULL;
7224
size_t mandos_argz_length = 0;
7226
g_assert_false(parse_arguments_devnull(argc, argv, false,
7228
&helper_directory, &user,
7229
&group, &mandos_argz,
7230
&mandos_argz_length));
7232
g_assert_null(helper_directory);
7233
g_assert_null(agent_directory);
7234
g_assert_cmpuint((unsigned int)group, ==, 0);
7235
g_assert_true(group == 0);
7236
g_assert_null(mandos_argz);
7237
g_assert_true(mandos_argz_length == 0);
7239
for(char **arg = argv; *arg != NULL; arg++){
7244
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7245
test_fixture *fixture,
7246
__attribute__((unused))
7251
strdup("mandos-client"),
7253
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7255
__attribute__((cleanup(cleanup_string)))
7256
char *agent_directory = NULL;
7257
__attribute__((cleanup(cleanup_string)))
7258
char *helper_directory = NULL;
7261
__attribute__((cleanup(cleanup_string)))
7262
char *mandos_argz = NULL;
7263
size_t mandos_argz_length = 0;
7265
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7266
&helper_directory, &user, &group,
7267
&mandos_argz, &mandos_argz_length));
7269
g_assert_null(agent_directory);
7270
g_assert_null(helper_directory);
7271
g_assert_true(user == 0);
7272
g_assert_true(group == 0);
7273
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7274
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7275
mandos_argz_length),
7278
for(char **arg = argv; *arg != NULL; arg++){
7283
static void test_parse_arguments_mandos_args(__attribute__((unused))
7284
test_fixture *fixture,
7285
__attribute__((unused))
7286
gconstpointer user_data){
7289
strdup("mandos-client"),
7294
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7296
__attribute__((cleanup(cleanup_string)))
7297
char *agent_directory = NULL;
7298
__attribute__((cleanup(cleanup_string)))
7299
char *helper_directory = NULL;
7302
__attribute__((cleanup(cleanup_string)))
7303
char *mandos_argz = NULL;
7304
size_t mandos_argz_length = 0;
7306
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7307
&helper_directory, &user, &group,
7308
&mandos_argz, &mandos_argz_length));
7310
g_assert_null(agent_directory);
7311
g_assert_null(helper_directory);
7312
g_assert_true(user == 0);
7313
g_assert_true(group == 0);
7314
char *marg = mandos_argz;
7315
g_assert_cmpstr(marg, ==, "mandos-client");
7316
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7317
g_assert_cmpstr(marg, ==, "one");
7318
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7319
g_assert_cmpstr(marg, ==, "two");
7320
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7321
g_assert_cmpstr(marg, ==, "three");
7322
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7323
mandos_argz_length),
7326
for(char **arg = argv; *arg != NULL; arg++){
7331
static void test_parse_arguments_all_args(__attribute__((unused))
7332
test_fixture *fixture,
7333
__attribute__((unused))
7334
gconstpointer user_data){
7337
strdup("--agent-directory"),
7339
strdup("--helper-directory"),
7345
strdup("mandos-client"),
7350
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7352
__attribute__((cleanup(cleanup_string)))
7353
char *agent_directory = NULL;
7354
__attribute__((cleanup(cleanup_string)))
7355
char *helper_directory = NULL;
7358
__attribute__((cleanup(cleanup_string)))
7359
char *mandos_argz = NULL;
7360
size_t mandos_argz_length = 0;
7362
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7363
&helper_directory, &user, &group,
7364
&mandos_argz, &mandos_argz_length));
7366
g_assert_cmpstr(agent_directory, ==, "/tmp");
7367
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7368
g_assert_true(user == 1);
7369
g_assert_true(group == 2);
7370
char *marg = mandos_argz;
7371
g_assert_cmpstr(marg, ==, "mandos-client");
7372
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7373
g_assert_cmpstr(marg, ==, "one");
7374
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7375
g_assert_cmpstr(marg, ==, "two");
7376
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7377
g_assert_cmpstr(marg, ==, "three");
7378
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7379
mandos_argz_length),
7382
for(char **arg = argv; *arg != NULL; arg++){
7387
static void test_parse_arguments_mixed(__attribute__((unused))
7388
test_fixture *fixture,
7389
__attribute__((unused))
7390
gconstpointer user_data){
7393
strdup("mandos-client"),
7397
strdup("--agent-directory"),
7401
strdup("--helper-directory=/var/tmp"),
7403
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7405
__attribute__((cleanup(cleanup_string)))
7406
char *agent_directory = NULL;
7407
__attribute__((cleanup(cleanup_string)))
7408
char *helper_directory = NULL;
7411
__attribute__((cleanup(cleanup_string)))
7412
char *mandos_argz = NULL;
7413
size_t mandos_argz_length = 0;
7415
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7416
&helper_directory, &user, &group,
7417
&mandos_argz, &mandos_argz_length));
7419
g_assert_cmpstr(agent_directory, ==, "/tmp");
7420
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7421
g_assert_true(user == 1);
7422
g_assert_true(group == 0);
7423
char *marg = mandos_argz;
7424
g_assert_cmpstr(marg, ==, "mandos-client");
7425
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7426
g_assert_cmpstr(marg, ==, "one");
7427
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7428
g_assert_cmpstr(marg, ==, "two");
7429
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7430
g_assert_cmpstr(marg, ==, "three");
7431
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7432
mandos_argz_length),
7435
for(char **arg = argv; *arg != NULL; arg++){
7440
/* End of tests section */
7442
/* Test boilerplate section; New tests should be added to the test
7443
suite definition here, in the "run_tests" function.
7445
Finally, this section also contains the should_only_run_tests()
7446
function used by main() for deciding if tests should be run or to
7449
__attribute__((cold))
7450
static bool run_tests(int argc, char *argv[]){
7451
g_test_init(&argc, &argv, NULL);
7453
/* A macro to add a test with no setup or teardown functions */
7454
#define test_add(testpath, testfunc) \
7456
g_test_add((testpath), test_fixture, NULL, NULL, \
7457
(testfunc), NULL); \
7460
/* Test the signal-related functions first, since some other tests
7461
depend on these functions in their setups and teardowns */
7462
test_add("/signal-handling/setup", test_setup_signal_handler);
7463
test_add("/signal-handling/restore", test_restore_signal_handler);
7464
test_add("/signal-handling/block", test_block_sigchld);
7465
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7467
/* Regular non-signal-related tests; these use no setups or
7469
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7470
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7471
test_add("/parse_arguments/long-dir",
7472
test_parse_arguments_long_dir);
7473
test_add("/parse_arguments/short-dir",
7474
test_parse_arguments_short_dir);
7475
test_add("/parse_arguments/helper-directory",
7476
test_parse_arguments_helper_directory);
7477
test_add("/parse_arguments/plugin-helper-dir",
7478
test_parse_arguments_plugin_helper_dir);
7479
test_add("/parse_arguments/user", test_parse_arguments_user);
7480
test_add("/parse_arguments/user-invalid",
7481
test_parse_arguments_user_invalid);
7482
test_add("/parse_arguments/user-zero-invalid",
7483
test_parse_arguments_user_zero_invalid);
7484
test_add("/parse_arguments/group", test_parse_arguments_group);
7485
test_add("/parse_arguments/group-invalid",
7486
test_parse_arguments_group_invalid);
7487
test_add("/parse_arguments/group-zero-invalid",
7488
test_parse_arguments_group_zero_invalid);
7489
test_add("/parse_arguments/mandos-noargs",
7490
test_parse_arguments_mandos_noargs);
7491
test_add("/parse_arguments/mandos-args",
7492
test_parse_arguments_mandos_args);
7493
test_add("/parse_arguments/all-args",
7494
test_parse_arguments_all_args);
7495
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7496
test_add("/queue/create", test_create_queue);
7497
test_add("/queue/add", test_add_to_queue);
7498
test_add("/queue/has_question/empty",
7499
test_queue_has_question_empty);
7500
test_add("/queue/has_question/false",
7501
test_queue_has_question_false);
7502
test_add("/queue/has_question/true", test_queue_has_question_true);
7503
test_add("/queue/has_question/false2",
7504
test_queue_has_question_false2);
7505
test_add("/queue/has_question/true2",
7506
test_queue_has_question_true2);
7507
test_add("/buffer/cleanup", test_cleanup_buffer);
7508
test_add("/string_set/net-set-contains-nothing",
7509
test_string_set_new_set_contains_nothing);
7510
test_add("/string_set/with-added-string-contains-it",
7511
test_string_set_with_added_string_contains_it);
7512
test_add("/string_set/cleared-does-not-contain-string",
7513
test_string_set_cleared_does_not_contain_str);
7514
test_add("/string_set/swap/one-with-empty",
7515
test_string_set_swap_one_with_empty);
7516
test_add("/string_set/swap/empty-with-one",
7517
test_string_set_swap_empty_with_one);
7518
test_add("/string_set/swap/one-with-one",
7519
test_string_set_swap_one_with_one);
7521
/* A macro to add a test using the setup and teardown functions */
7522
#define test_add_st(path, func) \
7524
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7528
/* Signal-related tests; these use setups and teardowns which
7529
establish, during each test run, a signal handler for, and a
7530
signal mask blocking, the SIGCHLD signal, just like main() */
7531
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7532
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7533
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7534
test_add_st("/run_queue/zeroes-next-run",
7535
test_run_queue_zeroes_next_run);
7536
test_add_st("/run_queue/clears-cancelled_filenames",
7537
test_run_queue_clears_cancelled_filenames);
7538
test_add_st("/run_queue/skips-cancelled-filenames",
7539
test_run_queue_skips_cancelled_filenames);
7540
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7541
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7542
test_add_st("/run_queue/two-tasks/quit",
7543
test_run_queue_two_tasks_quit);
7544
test_add_st("/run_queue/two-tasks-cleanup",
7545
test_run_queue_two_tasks_cleanup);
7546
test_add_st("/task-creators/start_mandos_client",
7547
test_start_mandos_client);
7548
test_add_st("/task-creators/start_mandos_client/execv",
7549
test_start_mandos_client_execv);
7550
test_add_st("/task-creators/start_mandos_client/suid/euid",
7551
test_start_mandos_client_suid_euid);
7552
test_add_st("/task-creators/start_mandos_client/suid/egid",
7553
test_start_mandos_client_suid_egid);
7554
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7555
test_start_mandos_client_suid_ruid);
7556
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7557
test_start_mandos_client_suid_rgid);
7558
test_add_st("/task-creators/start_mandos_client/read",
7559
test_start_mandos_client_read);
7560
test_add_st("/task-creators/start_mandos_client/helper-directory",
7561
test_start_mandos_client_helper_directory);
7562
test_add_st("/task-creators/start_mandos_client/sigmask",
7563
test_start_mandos_client_sigmask);
7564
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7565
test_wait_for_mandos_client_exit_badpid);
7566
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7567
test_wait_for_mandos_client_exit_noexit);
7568
test_add_st("/task/wait_for_mandos_client_exit/success",
7569
test_wait_for_mandos_client_exit_success);
7570
test_add_st("/task/wait_for_mandos_client_exit/failure",
7571
test_wait_for_mandos_client_exit_failure);
7572
test_add_st("/task/wait_for_mandos_client_exit/killed",
7573
test_wait_for_mandos_client_exit_killed);
7574
test_add_st("/task/read_mandos_client_output/readerror",
7575
test_read_mandos_client_output_readerror);
7576
test_add_st("/task/read_mandos_client_output/nodata",
7577
test_read_mandos_client_output_nodata);
7578
test_add_st("/task/read_mandos_client_output/eof",
7579
test_read_mandos_client_output_eof);
7580
test_add_st("/task/read_mandos_client_output/once",
7581
test_read_mandos_client_output_once);
7582
test_add_st("/task/read_mandos_client_output/malloc",
7583
test_read_mandos_client_output_malloc);
7584
test_add_st("/task/read_mandos_client_output/append",
7585
test_read_mandos_client_output_append);
7586
test_add_st("/task-creators/add_inotify_dir_watch",
7587
test_add_inotify_dir_watch);
7588
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7589
test_add_inotify_dir_watch_fail);
7590
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7591
test_add_inotify_dir_watch_EAGAIN);
7592
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7593
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7594
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7595
test_add_inotify_dir_watch_IN_MOVED_TO);
7596
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7597
test_add_inotify_dir_watch_IN_DELETE);
7598
test_add_st("/task/read_inotify_event/readerror",
7599
test_read_inotify_event_readerror);
7600
test_add_st("/task/read_inotify_event/bad-epoll",
7601
test_read_inotify_event_bad_epoll);
7602
test_add_st("/task/read_inotify_event/nodata",
7603
test_read_inotify_event_nodata);
7604
test_add_st("/task/read_inotify_event/eof",
7605
test_read_inotify_event_eof);
7606
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7607
test_read_inotify_event_IN_CLOSE_WRITE);
7608
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7609
test_read_inotify_event_IN_MOVED_TO);
7610
test_add_st("/task/read_inotify_event/IN_DELETE",
7611
test_read_inotify_event_IN_DELETE);
7612
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7613
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7614
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7615
test_read_inotify_event_IN_MOVED_TO_badname);
7616
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7617
test_read_inotify_event_IN_DELETE_badname);
7618
test_add_st("/task/open_and_parse_question/ENOENT",
7619
test_open_and_parse_question_ENOENT);
7620
test_add_st("/task/open_and_parse_question/EIO",
7621
test_open_and_parse_question_EIO);
7622
test_add_st("/task/open_and_parse_question/parse-error",
7623
test_open_and_parse_question_parse_error);
7624
test_add_st("/task/open_and_parse_question/nosocket",
7625
test_open_and_parse_question_nosocket);
7626
test_add_st("/task/open_and_parse_question/badsocket",
7627
test_open_and_parse_question_badsocket);
7628
test_add_st("/task/open_and_parse_question/nopid",
7629
test_open_and_parse_question_nopid);
7630
test_add_st("/task/open_and_parse_question/badpid",
7631
test_open_and_parse_question_badpid);
7632
test_add_st("/task/open_and_parse_question/noexist_pid",
7633
test_open_and_parse_question_noexist_pid);
7634
test_add_st("/task/open_and_parse_question/no-notafter",
7635
test_open_and_parse_question_no_notafter);
7636
test_add_st("/task/open_and_parse_question/bad-notafter",
7637
test_open_and_parse_question_bad_notafter);
7638
test_add_st("/task/open_and_parse_question/notafter-0",
7639
test_open_and_parse_question_notafter_0);
7640
test_add_st("/task/open_and_parse_question/notafter-1",
7641
test_open_and_parse_question_notafter_1);
7642
test_add_st("/task/open_and_parse_question/notafter-1-1",
7643
test_open_and_parse_question_notafter_1_1);
7644
test_add_st("/task/open_and_parse_question/notafter-1-2",
7645
test_open_and_parse_question_notafter_1_2);
7646
test_add_st("/task/open_and_parse_question/equal-notafter",
7647
test_open_and_parse_question_equal_notafter);
7648
test_add_st("/task/open_and_parse_question/late-notafter",
7649
test_open_and_parse_question_late_notafter);
7650
test_add_st("/task/cancel_old_question/0-1-2",
7651
test_cancel_old_question_0_1_2);
7652
test_add_st("/task/cancel_old_question/0-2-1",
7653
test_cancel_old_question_0_2_1);
7654
test_add_st("/task/cancel_old_question/1-2-3",
7655
test_cancel_old_question_1_2_3);
7656
test_add_st("/task/cancel_old_question/1-3-2",
7657
test_cancel_old_question_1_3_2);
7658
test_add_st("/task/cancel_old_question/2-1-3",
7659
test_cancel_old_question_2_1_3);
7660
test_add_st("/task/cancel_old_question/2-3-1",
7661
test_cancel_old_question_2_3_1);
7662
test_add_st("/task/cancel_old_question/3-1-2",
7663
test_cancel_old_question_3_1_2);
7664
test_add_st("/task/cancel_old_question/3-2-1",
7665
test_cancel_old_question_3_2_1);
7666
test_add_st("/task/connect_question_socket/name-too-long",
7667
test_connect_question_socket_name_too_long);
7668
test_add_st("/task/connect_question_socket/connect-fail",
7669
test_connect_question_socket_connect_fail);
7670
test_add_st("/task/connect_question_socket/bad-epoll",
7671
test_connect_question_socket_bad_epoll);
7672
test_add_st("/task/connect_question_socket/usable",
7673
test_connect_question_socket_usable);
7674
test_add_st("/task/send_password_to_socket/client-not-exited",
7675
test_send_password_to_socket_client_not_exited);
7676
test_add_st("/task/send_password_to_socket/password-not-read",
7677
test_send_password_to_socket_password_not_read);
7678
test_add_st("/task/send_password_to_socket/EMSGSIZE",
7679
test_send_password_to_socket_EMSGSIZE);
7680
test_add_st("/task/send_password_to_socket/retry",
7681
test_send_password_to_socket_retry);
7682
test_add_st("/task/send_password_to_socket/bad-epoll",
7683
test_send_password_to_socket_bad_epoll);
7684
test_add_st("/task/send_password_to_socket/null-password",
7685
test_send_password_to_socket_null_password);
7686
test_add_st("/task/send_password_to_socket/empty-password",
7687
test_send_password_to_socket_empty_password);
7688
test_add_st("/task/send_password_to_socket/empty-str-password",
7689
test_send_password_to_socket_empty_str_pass);
7690
test_add_st("/task/send_password_to_socket/text-password",
7691
test_send_password_to_socket_text_password);
7692
test_add_st("/task/send_password_to_socket/binary-password",
7693
test_send_password_to_socket_binary_password);
7694
test_add_st("/task/send_password_to_socket/nuls-in-password",
7695
test_send_password_to_socket_nuls_in_password);
7696
test_add_st("/task-creators/add_existing_questions/ENOENT",
7697
test_add_existing_questions_ENOENT);
7698
test_add_st("/task-creators/add_existing_questions/no-questions",
7699
test_add_existing_questions_no_questions);
7700
test_add_st("/task-creators/add_existing_questions/one-question",
7701
test_add_existing_questions_one_question);
7702
test_add_st("/task-creators/add_existing_questions/two-questions",
7703
test_add_existing_questions_two_questions);
7704
test_add_st("/task-creators/add_existing_questions/non-questions",
7705
test_add_existing_questions_non_questions);
7706
test_add_st("/task-creators/add_existing_questions/both-types",
7707
test_add_existing_questions_both_types);
7709
return g_test_run() == 0;
7712
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
7713
GOptionContext *context = g_option_context_new("");
7715
g_option_context_set_help_enabled(context, FALSE);
7716
g_option_context_set_ignore_unknown_options(context, TRUE);
7718
gboolean run_tests = FALSE;
7719
GOptionEntry entries[] = {
7720
{ "test", 0, 0, G_OPTION_ARG_NONE,
7721
&run_tests, "Run tests", NULL },
7724
g_option_context_add_main_entries(context, entries, NULL);
7726
GError *error = NULL;
7728
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
7729
g_option_context_free(context);
7730
g_error("Failed to parse options: %s", error->message);
7733
g_option_context_free(context);
7734
return run_tests != FALSE;