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 | IN_MOVED_TO
1021
| IN_MOVED_FROM| 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_MOVED_FROM | 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_MOVED_FROM(__attribute__((unused))
3676
test_fixture *fixture,
3677
__attribute__((unused))
3680
__attribute__((cleanup(cleanup_close)))
3681
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3682
g_assert_cmpint(epoll_fd, >=, 0);
3683
__attribute__((cleanup(cleanup_queue)))
3684
task_queue *queue = create_queue();
3685
g_assert_nonnull(queue);
3686
__attribute__((cleanup(string_set_clear)))
3687
string_set cancelled_filenames = {};
3688
const mono_microsecs current_time = 0;
3690
bool quit_now = false;
3691
buffer password = {};
3692
bool mandos_client_exited = false;
3693
bool password_is_read = false;
3695
__attribute__((cleanup(cleanup_string)))
3696
char *tempdir = make_temporary_directory();
3697
g_assert_nonnull(tempdir);
3699
__attribute__((cleanup(cleanup_string)))
3700
char *tempfilename = make_temporary_file_in_directory(tempdir);
3701
g_assert_nonnull(tempfilename);
3703
__attribute__((cleanup(cleanup_string)))
3704
char *targetdir = make_temporary_directory();
3705
g_assert_nonnull(targetdir);
3707
__attribute__((cleanup(cleanup_string)))
3708
char *targetfilename = NULL;
3709
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3710
basename(tempfilename)), >, 0);
3711
g_assert_nonnull(targetfilename);
3713
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3715
&cancelled_filenames,
3717
&mandos_client_exited,
3718
&password_is_read));
3720
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3722
const task_context *const added_read_task
3723
= find_matching_task(queue,
3724
(task_context){ .func=read_inotify_event });
3725
g_assert_nonnull(added_read_task);
3727
/* "sufficient to read at least one event." - inotify(7) */
3728
const size_t ievent_size = (sizeof(struct inotify_event)
3730
struct inotify_event *ievent = malloc(ievent_size);
3731
g_assert_nonnull(ievent);
3733
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3735
g_assert_cmpint((int)read_size, >, 0);
3736
g_assert_true(ievent->mask & IN_MOVED_FROM);
3737
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3741
g_assert_cmpint(unlink(targetfilename), ==, 0);
3742
g_assert_cmpint(rmdir(targetdir), ==, 0);
3743
g_assert_cmpint(rmdir(tempdir), ==, 0);
3747
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3748
test_fixture *fixture,
3749
__attribute__((unused))
3750
gconstpointer user_data){
3751
__attribute__((cleanup(cleanup_close)))
3752
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3753
g_assert_cmpint(epoll_fd, >=, 0);
3754
__attribute__((cleanup(cleanup_queue)))
3755
task_queue *queue = create_queue();
3756
g_assert_nonnull(queue);
3757
__attribute__((cleanup(string_set_clear)))
3758
string_set cancelled_filenames = {};
3759
const mono_microsecs current_time = 0;
3761
bool quit_now = false;
3762
buffer password = {};
3763
bool mandos_client_exited = false;
3764
bool password_is_read = false;
3766
__attribute__((cleanup(cleanup_string)))
3767
char *tempdir = make_temporary_directory();
3768
g_assert_nonnull(tempdir);
3770
__attribute__((cleanup(cleanup_string)))
3771
char *tempfile = make_temporary_file_in_directory(tempdir);
3772
g_assert_nonnull(tempfile);
3774
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3776
&cancelled_filenames,
3778
&mandos_client_exited,
3779
&password_is_read));
3780
g_assert_cmpint(unlink(tempfile), ==, 0);
3782
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3784
const task_context *const added_read_task
3785
= find_matching_task(queue,
3786
(task_context){ .func=read_inotify_event });
3787
g_assert_nonnull(added_read_task);
3789
g_assert_cmpint(added_read_task->fd, >, 2);
3790
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3792
/* "sufficient to read at least one event." - inotify(7) */
3793
const size_t ievent_size = (sizeof(struct inotify_event)
3795
struct inotify_event *ievent = malloc(ievent_size);
3796
g_assert_nonnull(ievent);
3798
ssize_t read_size = 0;
3799
read_size = read(added_read_task->fd, ievent, ievent_size);
3801
g_assert_cmpint((int)read_size, >, 0);
3802
g_assert_true(ievent->mask & IN_DELETE);
3803
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3807
g_assert_cmpint(rmdir(tempdir), ==, 0);
3810
static void test_read_inotify_event_readerror(__attribute__((unused))
3811
test_fixture *fixture,
3812
__attribute__((unused))
3815
__attribute__((cleanup(cleanup_close)))
3816
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3817
g_assert_cmpint(epoll_fd, >=, 0);
3818
const mono_microsecs current_time = 0;
3820
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3821
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3823
bool quit_now = false;
3824
__attribute__((cleanup(cleanup_queue)))
3825
task_queue *queue = create_queue();
3826
g_assert_nonnull(queue);
3828
task_context task = {
3829
.func=read_inotify_event,
3832
.quit_now=&quit_now,
3833
.filename=strdup("/nonexistent"),
3834
.cancelled_filenames = &(string_set){},
3836
.current_time=¤t_time,
3838
g_assert_nonnull(task.filename);
3839
run_task_with_stderr_to_dev_null(task, queue);
3840
g_assert_true(quit_now);
3841
g_assert_true(queue->next_run == 0);
3842
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3844
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3846
g_assert_cmpint(close(fd), ==, -1);
3849
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3850
test_fixture *fixture,
3851
__attribute__((unused))
3854
const mono_microsecs current_time = 17;
3857
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3858
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3860
bool quit_now = false;
3861
buffer password = {};
3862
bool mandos_client_exited = false;
3863
bool password_is_read = false;
3864
__attribute__((cleanup(cleanup_queue)))
3865
task_queue *queue = create_queue();
3866
g_assert_nonnull(queue);
3868
task_context task = {
3869
.func=read_inotify_event,
3872
.quit_now=&quit_now,
3873
.password=&password,
3874
.filename=strdup("/nonexistent"),
3875
.cancelled_filenames = &(string_set){},
3877
.current_time=¤t_time,
3878
.mandos_client_exited=&mandos_client_exited,
3879
.password_is_read=&password_is_read,
3881
g_assert_nonnull(task.filename);
3882
run_task_with_stderr_to_dev_null(task, queue);
3884
g_assert_nonnull(find_matching_task(queue, task));
3885
g_assert_true(queue->next_run == 1000000 + current_time);
3887
g_assert_cmpint(close(pipefds[0]), ==, 0);
3888
g_assert_cmpint(close(pipefds[1]), ==, 0);
3891
static void test_read_inotify_event_nodata(__attribute__((unused))
3892
test_fixture *fixture,
3893
__attribute__((unused))
3894
gconstpointer user_data){
3895
__attribute__((cleanup(cleanup_close)))
3896
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3897
g_assert_cmpint(epoll_fd, >=, 0);
3898
const mono_microsecs current_time = 0;
3901
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3903
bool quit_now = false;
3904
buffer password = {};
3905
bool mandos_client_exited = false;
3906
bool password_is_read = false;
3907
__attribute__((cleanup(cleanup_queue)))
3908
task_queue *queue = create_queue();
3909
g_assert_nonnull(queue);
3911
task_context task = {
3912
.func=read_inotify_event,
3915
.quit_now=&quit_now,
3916
.password=&password,
3917
.filename=strdup("/nonexistent"),
3918
.cancelled_filenames = &(string_set){},
3920
.current_time=¤t_time,
3921
.mandos_client_exited=&mandos_client_exited,
3922
.password_is_read=&password_is_read,
3924
g_assert_nonnull(task.filename);
3925
task.func(task, queue);
3926
g_assert_false(quit_now);
3927
g_assert_true(queue->next_run == 0);
3928
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3930
g_assert_nonnull(find_matching_task(queue, (task_context){
3931
.func=read_inotify_event,
3934
.quit_now=&quit_now,
3935
.password=&password,
3936
.filename=task.filename,
3937
.cancelled_filenames=task.cancelled_filenames,
3938
.current_time=¤t_time,
3939
.mandos_client_exited=&mandos_client_exited,
3940
.password_is_read=&password_is_read,
3943
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3944
EPOLLIN | EPOLLRDHUP));
3946
g_assert_cmpint(close(pipefds[1]), ==, 0);
3949
static void test_read_inotify_event_eof(__attribute__((unused))
3950
test_fixture *fixture,
3951
__attribute__((unused))
3952
gconstpointer user_data){
3953
__attribute__((cleanup(cleanup_close)))
3954
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3955
g_assert_cmpint(epoll_fd, >=, 0);
3956
const mono_microsecs current_time = 0;
3959
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3960
g_assert_cmpint(close(pipefds[1]), ==, 0);
3962
bool quit_now = false;
3963
buffer password = {};
3964
__attribute__((cleanup(cleanup_queue)))
3965
task_queue *queue = create_queue();
3966
g_assert_nonnull(queue);
3968
task_context task = {
3969
.func=read_inotify_event,
3972
.quit_now=&quit_now,
3973
.password=&password,
3974
.filename=strdup("/nonexistent"),
3975
.cancelled_filenames = &(string_set){},
3977
.current_time=¤t_time,
3979
run_task_with_stderr_to_dev_null(task, queue);
3980
g_assert_true(quit_now);
3981
g_assert_true(queue->next_run == 0);
3982
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3984
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3986
g_assert_cmpint(close(pipefds[0]), ==, -1);
3990
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
3991
test_fixture *fixture,
3992
__attribute__((unused))
3993
gconstpointer user_data){
3994
__attribute__((cleanup(cleanup_close)))
3995
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3996
g_assert_cmpint(epoll_fd, >=, 0);
3997
const mono_microsecs current_time = 0;
4000
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4002
/* "sufficient to read at least one event." - inotify(7) */
4003
const size_t ievent_max_size = (sizeof(struct inotify_event)
4005
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4007
struct inotify_event event;
4008
char name_buffer[NAME_MAX + 1];
4010
struct inotify_event *const ievent = &ievent_buffer.event;
4012
const char dummy_file_name[] = "ask.dummy_file_name";
4013
ievent->mask = IN_CLOSE_WRITE;
4014
ievent->len = sizeof(dummy_file_name);
4015
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4016
const size_t ievent_size = (sizeof(struct inotify_event)
4017
+ sizeof(dummy_file_name));
4018
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4020
g_assert_cmpint(close(pipefds[1]), ==, 0);
4022
bool quit_now = false;
4023
buffer password = {};
4024
bool mandos_client_exited = false;
4025
bool password_is_read = false;
4026
__attribute__((cleanup(cleanup_queue)))
4027
task_queue *queue = create_queue();
4028
g_assert_nonnull(queue);
4030
task_context task = {
4031
.func=read_inotify_event,
4034
.quit_now=&quit_now,
4035
.password=&password,
4036
.filename=strdup("/nonexistent"),
4037
.cancelled_filenames = &(string_set){},
4039
.current_time=¤t_time,
4040
.mandos_client_exited=&mandos_client_exited,
4041
.password_is_read=&password_is_read,
4043
task.func(task, queue);
4044
g_assert_false(quit_now);
4045
g_assert_true(queue->next_run != 0);
4046
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4048
g_assert_nonnull(find_matching_task(queue, (task_context){
4049
.func=read_inotify_event,
4052
.quit_now=&quit_now,
4053
.password=&password,
4054
.filename=task.filename,
4055
.cancelled_filenames=task.cancelled_filenames,
4056
.current_time=¤t_time,
4057
.mandos_client_exited=&mandos_client_exited,
4058
.password_is_read=&password_is_read,
4061
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4062
EPOLLIN | EPOLLRDHUP));
4064
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4066
__attribute__((cleanup(cleanup_string)))
4067
char *filename = NULL;
4068
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4069
dummy_file_name), >, 0);
4070
g_assert_nonnull(filename);
4071
g_assert_nonnull(find_matching_task(queue, (task_context){
4072
.func=open_and_parse_question,
4075
.question_filename=filename,
4076
.password=&password,
4077
.cancelled_filenames=task.cancelled_filenames,
4078
.current_time=¤t_time,
4079
.mandos_client_exited=&mandos_client_exited,
4080
.password_is_read=&password_is_read,
4085
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4086
test_fixture *fixture,
4087
__attribute__((unused))
4088
gconstpointer user_data){
4089
__attribute__((cleanup(cleanup_close)))
4090
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4091
g_assert_cmpint(epoll_fd, >=, 0);
4092
const mono_microsecs current_time = 0;
4095
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4097
/* "sufficient to read at least one event." - inotify(7) */
4098
const size_t ievent_max_size = (sizeof(struct inotify_event)
4100
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4102
struct inotify_event event;
4103
char name_buffer[NAME_MAX + 1];
4105
struct inotify_event *const ievent = &ievent_buffer.event;
4107
const char dummy_file_name[] = "ask.dummy_file_name";
4108
ievent->mask = IN_MOVED_TO;
4109
ievent->len = sizeof(dummy_file_name);
4110
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4111
const size_t ievent_size = (sizeof(struct inotify_event)
4112
+ sizeof(dummy_file_name));
4113
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4115
g_assert_cmpint(close(pipefds[1]), ==, 0);
4117
bool quit_now = false;
4118
buffer password = {};
4119
bool mandos_client_exited = false;
4120
bool password_is_read = false;
4121
__attribute__((cleanup(cleanup_queue)))
4122
task_queue *queue = create_queue();
4123
g_assert_nonnull(queue);
4125
task_context task = {
4126
.func=read_inotify_event,
4129
.quit_now=&quit_now,
4130
.password=&password,
4131
.filename=strdup("/nonexistent"),
4132
.cancelled_filenames = &(string_set){},
4134
.current_time=¤t_time,
4135
.mandos_client_exited=&mandos_client_exited,
4136
.password_is_read=&password_is_read,
4138
task.func(task, queue);
4139
g_assert_false(quit_now);
4140
g_assert_true(queue->next_run != 0);
4141
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4143
g_assert_nonnull(find_matching_task(queue, (task_context){
4144
.func=read_inotify_event,
4147
.quit_now=&quit_now,
4148
.password=&password,
4149
.filename=task.filename,
4150
.cancelled_filenames=task.cancelled_filenames,
4151
.current_time=¤t_time,
4152
.mandos_client_exited=&mandos_client_exited,
4153
.password_is_read=&password_is_read,
4156
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4157
EPOLLIN | EPOLLRDHUP));
4159
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4161
__attribute__((cleanup(cleanup_string)))
4162
char *filename = NULL;
4163
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4164
dummy_file_name), >, 0);
4165
g_assert_nonnull(filename);
4166
g_assert_nonnull(find_matching_task(queue, (task_context){
4167
.func=open_and_parse_question,
4170
.question_filename=filename,
4171
.password=&password,
4172
.cancelled_filenames=task.cancelled_filenames,
4173
.current_time=¤t_time,
4174
.mandos_client_exited=&mandos_client_exited,
4175
.password_is_read=&password_is_read,
4180
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4181
test_fixture *fixture,
4182
__attribute__((unused))
4183
gconstpointer user_data){
4184
__attribute__((cleanup(cleanup_close)))
4185
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4186
g_assert_cmpint(epoll_fd, >=, 0);
4187
__attribute__((cleanup(string_set_clear)))
4188
string_set cancelled_filenames = {};
4189
const mono_microsecs current_time = 0;
4192
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4194
/* "sufficient to read at least one event." - inotify(7) */
4195
const size_t ievent_max_size = (sizeof(struct inotify_event)
4197
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4199
struct inotify_event event;
4200
char name_buffer[NAME_MAX + 1];
4202
struct inotify_event *const ievent = &ievent_buffer.event;
4204
const char dummy_file_name[] = "ask.dummy_file_name";
4205
ievent->mask = IN_MOVED_FROM;
4206
ievent->len = sizeof(dummy_file_name);
4207
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4208
const size_t ievent_size = (sizeof(struct inotify_event)
4209
+ sizeof(dummy_file_name));
4210
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4212
g_assert_cmpint(close(pipefds[1]), ==, 0);
4214
bool quit_now = false;
4215
buffer password = {};
4216
bool mandos_client_exited = false;
4217
bool password_is_read = false;
4218
__attribute__((cleanup(cleanup_queue)))
4219
task_queue *queue = create_queue();
4220
g_assert_nonnull(queue);
4222
task_context task = {
4223
.func=read_inotify_event,
4226
.quit_now=&quit_now,
4227
.password=&password,
4228
.filename=strdup("/nonexistent"),
4229
.cancelled_filenames=&cancelled_filenames,
4230
.current_time=¤t_time,
4231
.mandos_client_exited=&mandos_client_exited,
4232
.password_is_read=&password_is_read,
4234
task.func(task, queue);
4235
g_assert_false(quit_now);
4236
g_assert_true(queue->next_run == 0);
4237
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4239
g_assert_nonnull(find_matching_task(queue, (task_context){
4240
.func=read_inotify_event,
4243
.quit_now=&quit_now,
4244
.password=&password,
4245
.filename=task.filename,
4246
.cancelled_filenames=&cancelled_filenames,
4247
.current_time=¤t_time,
4248
.mandos_client_exited=&mandos_client_exited,
4249
.password_is_read=&password_is_read,
4252
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4253
EPOLLIN | EPOLLRDHUP));
4255
__attribute__((cleanup(cleanup_string)))
4256
char *filename = NULL;
4257
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4258
dummy_file_name), >, 0);
4259
g_assert_nonnull(filename);
4260
g_assert_true(string_set_contains(*task.cancelled_filenames,
4264
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4265
test_fixture *fixture,
4266
__attribute__((unused))
4269
__attribute__((cleanup(cleanup_close)))
4270
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4271
g_assert_cmpint(epoll_fd, >=, 0);
4272
__attribute__((cleanup(string_set_clear)))
4273
string_set cancelled_filenames = {};
4274
const mono_microsecs current_time = 0;
4277
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4279
/* "sufficient to read at least one event." - inotify(7) */
4280
const size_t ievent_max_size = (sizeof(struct inotify_event)
4282
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4284
struct inotify_event event;
4285
char name_buffer[NAME_MAX + 1];
4287
struct inotify_event *const ievent = &ievent_buffer.event;
4289
const char dummy_file_name[] = "ask.dummy_file_name";
4290
ievent->mask = IN_DELETE;
4291
ievent->len = sizeof(dummy_file_name);
4292
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4293
const size_t ievent_size = (sizeof(struct inotify_event)
4294
+ sizeof(dummy_file_name));
4295
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4297
g_assert_cmpint(close(pipefds[1]), ==, 0);
4299
bool quit_now = false;
4300
buffer password = {};
4301
bool mandos_client_exited = false;
4302
bool password_is_read = false;
4303
__attribute__((cleanup(cleanup_queue)))
4304
task_queue *queue = create_queue();
4305
g_assert_nonnull(queue);
4307
task_context task = {
4308
.func=read_inotify_event,
4311
.quit_now=&quit_now,
4312
.password=&password,
4313
.filename=strdup("/nonexistent"),
4314
.cancelled_filenames=&cancelled_filenames,
4315
.current_time=¤t_time,
4316
.mandos_client_exited=&mandos_client_exited,
4317
.password_is_read=&password_is_read,
4319
task.func(task, queue);
4320
g_assert_false(quit_now);
4321
g_assert_true(queue->next_run == 0);
4322
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4324
g_assert_nonnull(find_matching_task(queue, (task_context){
4325
.func=read_inotify_event,
4328
.quit_now=&quit_now,
4329
.password=&password,
4330
.filename=task.filename,
4331
.cancelled_filenames=&cancelled_filenames,
4332
.current_time=¤t_time,
4333
.mandos_client_exited=&mandos_client_exited,
4334
.password_is_read=&password_is_read,
4337
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4338
EPOLLIN | EPOLLRDHUP));
4340
__attribute__((cleanup(cleanup_string)))
4341
char *filename = NULL;
4342
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4343
dummy_file_name), >, 0);
4344
g_assert_nonnull(filename);
4345
g_assert_true(string_set_contains(*task.cancelled_filenames,
4350
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4351
test_fixture *fixture,
4352
__attribute__((unused))
4355
__attribute__((cleanup(cleanup_close)))
4356
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4357
g_assert_cmpint(epoll_fd, >=, 0);
4358
const mono_microsecs current_time = 0;
4361
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4363
/* "sufficient to read at least one event." - inotify(7) */
4364
const size_t ievent_max_size = (sizeof(struct inotify_event)
4366
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4368
struct inotify_event event;
4369
char name_buffer[NAME_MAX + 1];
4371
struct inotify_event *const ievent = &ievent_buffer.event;
4373
const char dummy_file_name[] = "ignored.dummy_file_name";
4374
ievent->mask = IN_CLOSE_WRITE;
4375
ievent->len = sizeof(dummy_file_name);
4376
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4377
const size_t ievent_size = (sizeof(struct inotify_event)
4378
+ sizeof(dummy_file_name));
4379
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4381
g_assert_cmpint(close(pipefds[1]), ==, 0);
4383
bool quit_now = false;
4384
buffer password = {};
4385
bool mandos_client_exited = false;
4386
bool password_is_read = false;
4387
__attribute__((cleanup(cleanup_queue)))
4388
task_queue *queue = create_queue();
4389
g_assert_nonnull(queue);
4391
task_context task = {
4392
.func=read_inotify_event,
4395
.quit_now=&quit_now,
4396
.password=&password,
4397
.filename=strdup("/nonexistent"),
4398
.cancelled_filenames = &(string_set){},
4400
.current_time=¤t_time,
4401
.mandos_client_exited=&mandos_client_exited,
4402
.password_is_read=&password_is_read,
4404
task.func(task, queue);
4405
g_assert_false(quit_now);
4406
g_assert_true(queue->next_run == 0);
4407
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4409
g_assert_nonnull(find_matching_task(queue, (task_context){
4410
.func=read_inotify_event,
4413
.quit_now=&quit_now,
4414
.password=&password,
4415
.filename=task.filename,
4416
.cancelled_filenames=task.cancelled_filenames,
4417
.current_time=¤t_time,
4418
.mandos_client_exited=&mandos_client_exited,
4419
.password_is_read=&password_is_read,
4422
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4423
EPOLLIN | EPOLLRDHUP));
4427
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4428
test_fixture *fixture,
4429
__attribute__((unused))
4430
gconstpointer user_data){
4431
__attribute__((cleanup(cleanup_close)))
4432
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4433
g_assert_cmpint(epoll_fd, >=, 0);
4434
const mono_microsecs current_time = 0;
4437
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4439
/* "sufficient to read at least one event." - inotify(7) */
4440
const size_t ievent_max_size = (sizeof(struct inotify_event)
4442
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4444
struct inotify_event event;
4445
char name_buffer[NAME_MAX + 1];
4447
struct inotify_event *const ievent = &ievent_buffer.event;
4449
const char dummy_file_name[] = "ignored.dummy_file_name";
4450
ievent->mask = IN_MOVED_TO;
4451
ievent->len = sizeof(dummy_file_name);
4452
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4453
const size_t ievent_size = (sizeof(struct inotify_event)
4454
+ sizeof(dummy_file_name));
4455
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4457
g_assert_cmpint(close(pipefds[1]), ==, 0);
4459
bool quit_now = false;
4460
buffer password = {};
4461
bool mandos_client_exited = false;
4462
bool password_is_read = false;
4463
__attribute__((cleanup(cleanup_queue)))
4464
task_queue *queue = create_queue();
4465
g_assert_nonnull(queue);
4467
task_context task = {
4468
.func=read_inotify_event,
4471
.quit_now=&quit_now,
4472
.password=&password,
4473
.filename=strdup("/nonexistent"),
4474
.cancelled_filenames = &(string_set){},
4476
.current_time=¤t_time,
4477
.mandos_client_exited=&mandos_client_exited,
4478
.password_is_read=&password_is_read,
4480
task.func(task, queue);
4481
g_assert_false(quit_now);
4482
g_assert_true(queue->next_run == 0);
4483
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4485
g_assert_nonnull(find_matching_task(queue, (task_context){
4486
.func=read_inotify_event,
4489
.quit_now=&quit_now,
4490
.password=&password,
4491
.filename=task.filename,
4492
.cancelled_filenames=task.cancelled_filenames,
4493
.current_time=¤t_time,
4494
.mandos_client_exited=&mandos_client_exited,
4495
.password_is_read=&password_is_read,
4498
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4499
EPOLLIN | EPOLLRDHUP));
4503
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4504
test_fixture *fixture,
4505
__attribute__((unused))
4508
__attribute__((cleanup(cleanup_close)))
4509
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4510
g_assert_cmpint(epoll_fd, >=, 0);
4511
__attribute__((cleanup(string_set_clear)))
4512
string_set cancelled_filenames = {};
4513
const mono_microsecs current_time = 0;
4516
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4518
/* "sufficient to read at least one event." - inotify(7) */
4519
const size_t ievent_max_size = (sizeof(struct inotify_event)
4521
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4523
struct inotify_event event;
4524
char name_buffer[NAME_MAX + 1];
4526
struct inotify_event *const ievent = &ievent_buffer.event;
4528
const char dummy_file_name[] = "ignored.dummy_file_name";
4529
ievent->mask = IN_MOVED_FROM;
4530
ievent->len = sizeof(dummy_file_name);
4531
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4532
const size_t ievent_size = (sizeof(struct inotify_event)
4533
+ sizeof(dummy_file_name));
4534
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4536
g_assert_cmpint(close(pipefds[1]), ==, 0);
4538
bool quit_now = false;
4539
buffer password = {};
4540
bool mandos_client_exited = false;
4541
bool password_is_read = false;
4542
__attribute__((cleanup(cleanup_queue)))
4543
task_queue *queue = create_queue();
4544
g_assert_nonnull(queue);
4546
task_context task = {
4547
.func=read_inotify_event,
4550
.quit_now=&quit_now,
4551
.password=&password,
4552
.filename=strdup("/nonexistent"),
4553
.cancelled_filenames=&cancelled_filenames,
4554
.current_time=¤t_time,
4555
.mandos_client_exited=&mandos_client_exited,
4556
.password_is_read=&password_is_read,
4558
task.func(task, queue);
4559
g_assert_false(quit_now);
4560
g_assert_true(queue->next_run == 0);
4561
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4563
g_assert_nonnull(find_matching_task(queue, (task_context){
4564
.func=read_inotify_event,
4567
.quit_now=&quit_now,
4568
.password=&password,
4569
.filename=task.filename,
4570
.cancelled_filenames=&cancelled_filenames,
4571
.current_time=¤t_time,
4572
.mandos_client_exited=&mandos_client_exited,
4573
.password_is_read=&password_is_read,
4576
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4577
EPOLLIN | EPOLLRDHUP));
4579
__attribute__((cleanup(cleanup_string)))
4580
char *filename = NULL;
4581
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4582
dummy_file_name), >, 0);
4583
g_assert_nonnull(filename);
4584
g_assert_false(string_set_contains(cancelled_filenames, filename));
4588
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4589
test_fixture *fixture,
4590
__attribute__((unused))
4593
__attribute__((cleanup(cleanup_close)))
4594
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4595
g_assert_cmpint(epoll_fd, >=, 0);
4596
__attribute__((cleanup(string_set_clear)))
4597
string_set cancelled_filenames = {};
4598
const mono_microsecs current_time = 0;
4601
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4603
/* "sufficient to read at least one event." - inotify(7) */
4604
const size_t ievent_max_size = (sizeof(struct inotify_event)
4606
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4608
struct inotify_event event;
4609
char name_buffer[NAME_MAX + 1];
4611
struct inotify_event *const ievent = &ievent_buffer.event;
4613
const char dummy_file_name[] = "ignored.dummy_file_name";
4614
ievent->mask = IN_DELETE;
4615
ievent->len = sizeof(dummy_file_name);
4616
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4617
const size_t ievent_size = (sizeof(struct inotify_event)
4618
+ sizeof(dummy_file_name));
4619
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4621
g_assert_cmpint(close(pipefds[1]), ==, 0);
4623
bool quit_now = false;
4624
buffer password = {};
4625
bool mandos_client_exited = false;
4626
bool password_is_read = false;
4627
__attribute__((cleanup(cleanup_queue)))
4628
task_queue *queue = create_queue();
4629
g_assert_nonnull(queue);
4631
task_context task = {
4632
.func=read_inotify_event,
4635
.quit_now=&quit_now,
4636
.password=&password,
4637
.filename=strdup("/nonexistent"),
4638
.cancelled_filenames=&cancelled_filenames,
4639
.current_time=¤t_time,
4640
.mandos_client_exited=&mandos_client_exited,
4641
.password_is_read=&password_is_read,
4643
task.func(task, queue);
4644
g_assert_false(quit_now);
4645
g_assert_true(queue->next_run == 0);
4646
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4648
g_assert_nonnull(find_matching_task(queue, (task_context){
4649
.func=read_inotify_event,
4652
.quit_now=&quit_now,
4653
.password=&password,
4654
.filename=task.filename,
4655
.cancelled_filenames=&cancelled_filenames,
4656
.current_time=¤t_time,
4657
.mandos_client_exited=&mandos_client_exited,
4658
.password_is_read=&password_is_read,
4661
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4662
EPOLLIN | EPOLLRDHUP));
4664
__attribute__((cleanup(cleanup_string)))
4665
char *filename = NULL;
4666
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4667
dummy_file_name), >, 0);
4668
g_assert_nonnull(filename);
4669
g_assert_false(string_set_contains(cancelled_filenames, filename));
4673
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4674
test_fixture *fixture,
4675
__attribute__((unused))
4676
gconstpointer user_data){
4677
__attribute__((cleanup(cleanup_close)))
4678
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4679
g_assert_cmpint(epoll_fd, >=, 0);
4680
__attribute__((cleanup(string_set_clear)))
4681
string_set cancelled_filenames = {};
4682
bool mandos_client_exited = false;
4683
bool password_is_read = false;
4684
__attribute__((cleanup(cleanup_queue)))
4685
task_queue *queue = create_queue();
4686
g_assert_nonnull(queue);
4688
char *const filename = strdup("/nonexistent");
4689
g_assert_nonnull(filename);
4690
task_context task = {
4691
.func=open_and_parse_question,
4692
.question_filename=filename,
4694
.password=(buffer[]){{}},
4696
.cancelled_filenames=&cancelled_filenames,
4697
.current_time=(mono_microsecs[]){0},
4698
.mandos_client_exited=&mandos_client_exited,
4699
.password_is_read=&password_is_read,
4701
task.func(task, queue);
4702
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4705
static void test_open_and_parse_question_EIO(__attribute__((unused))
4706
test_fixture *fixture,
4707
__attribute__((unused))
4708
gconstpointer user_data){
4709
__attribute__((cleanup(cleanup_close)))
4710
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4711
g_assert_cmpint(epoll_fd, >=, 0);
4712
__attribute__((cleanup(string_set_clear)))
4713
string_set cancelled_filenames = {};
4714
buffer password = {};
4715
bool mandos_client_exited = false;
4716
bool password_is_read = false;
4717
__attribute__((cleanup(cleanup_queue)))
4718
task_queue *queue = create_queue();
4719
g_assert_nonnull(queue);
4720
const mono_microsecs current_time = 0;
4722
char *filename = strdup("/proc/self/mem");
4723
task_context task = {
4724
.func=open_and_parse_question,
4725
.question_filename=filename,
4727
.password=&password,
4729
.cancelled_filenames=&cancelled_filenames,
4730
.current_time=¤t_time,
4731
.mandos_client_exited=&mandos_client_exited,
4732
.password_is_read=&password_is_read,
4734
run_task_with_stderr_to_dev_null(task, queue);
4735
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4739
test_open_and_parse_question_parse_error(__attribute__((unused))
4740
test_fixture *fixture,
4741
__attribute__((unused))
4742
gconstpointer user_data){
4743
__attribute__((cleanup(cleanup_close)))
4744
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4745
g_assert_cmpint(epoll_fd, >=, 0);
4746
__attribute__((cleanup(string_set_clear)))
4747
string_set cancelled_filenames = {};
4748
__attribute__((cleanup(cleanup_queue)))
4749
task_queue *queue = create_queue();
4750
g_assert_nonnull(queue);
4752
__attribute__((cleanup(cleanup_string)))
4753
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4754
g_assert_nonnull(tempfilename);
4755
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4756
g_assert_cmpint(tempfile, >, 0);
4757
const char bad_data[] = "this is bad syntax\n";
4758
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4759
==, sizeof(bad_data));
4760
g_assert_cmpint(close(tempfile), ==, 0);
4762
char *const filename = strdup(tempfilename);
4763
g_assert_nonnull(filename);
4764
task_context task = {
4765
.func=open_and_parse_question,
4766
.question_filename=filename,
4768
.password=(buffer[]){{}},
4770
.cancelled_filenames=&cancelled_filenames,
4771
.current_time=(mono_microsecs[]){0},
4772
.mandos_client_exited=(bool[]){false},
4773
.password_is_read=(bool[]){false},
4775
run_task_with_stderr_to_dev_null(task, queue);
4777
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4779
g_assert_cmpint(unlink(tempfilename), ==, 0);
4783
void test_open_and_parse_question_nosocket(__attribute__((unused))
4784
test_fixture *fixture,
4785
__attribute__((unused))
4786
gconstpointer user_data){
4787
__attribute__((cleanup(cleanup_close)))
4788
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4789
g_assert_cmpint(epoll_fd, >=, 0);
4790
__attribute__((cleanup(string_set_clear)))
4791
string_set cancelled_filenames = {};
4792
__attribute__((cleanup(cleanup_queue)))
4793
task_queue *queue = create_queue();
4794
g_assert_nonnull(queue);
4796
__attribute__((cleanup(cleanup_string)))
4797
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4798
g_assert_nonnull(tempfilename);
4799
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4800
g_assert_cmpint(questionfile, >, 0);
4801
FILE *qf = fdopen(questionfile, "w");
4802
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4803
g_assert_cmpint(fclose(qf), ==, 0);
4805
char *const filename = strdup(tempfilename);
4806
g_assert_nonnull(filename);
4807
task_context task = {
4808
.func=open_and_parse_question,
4809
.question_filename=filename,
4811
.password=(buffer[]){{}},
4813
.cancelled_filenames=&cancelled_filenames,
4814
.current_time=(mono_microsecs[]){0},
4815
.mandos_client_exited=(bool[]){false},
4816
.password_is_read=(bool[]){false},
4818
run_task_with_stderr_to_dev_null(task, queue);
4819
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4821
g_assert_cmpint(unlink(tempfilename), ==, 0);
4825
void test_open_and_parse_question_badsocket(__attribute__((unused))
4826
test_fixture *fixture,
4827
__attribute__((unused))
4828
gconstpointer user_data){
4829
__attribute__((cleanup(cleanup_close)))
4830
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4831
g_assert_cmpint(epoll_fd, >=, 0);
4832
__attribute__((cleanup(string_set_clear)))
4833
string_set cancelled_filenames = {};
4834
__attribute__((cleanup(cleanup_queue)))
4835
task_queue *queue = create_queue();
4836
g_assert_nonnull(queue);
4838
__attribute__((cleanup(cleanup_string)))
4839
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4840
g_assert_nonnull(tempfilename);
4841
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4842
g_assert_cmpint(questionfile, >, 0);
4843
FILE *qf = fdopen(questionfile, "w");
4844
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4845
g_assert_cmpint(fclose(qf), ==, 0);
4847
char *const filename = strdup(tempfilename);
4848
g_assert_nonnull(filename);
4849
task_context task = {
4850
.func=open_and_parse_question,
4851
.question_filename=filename,
4853
.password=(buffer[]){{}},
4855
.cancelled_filenames=&cancelled_filenames,
4856
.current_time=(mono_microsecs[]){0},
4857
.mandos_client_exited=(bool[]){false},
4858
.password_is_read=(bool[]){false},
4860
run_task_with_stderr_to_dev_null(task, queue);
4861
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4863
g_assert_cmpint(unlink(tempfilename), ==, 0);
4867
void test_open_and_parse_question_nopid(__attribute__((unused))
4868
test_fixture *fixture,
4869
__attribute__((unused))
4870
gconstpointer user_data){
4871
__attribute__((cleanup(cleanup_close)))
4872
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4873
g_assert_cmpint(epoll_fd, >=, 0);
4874
__attribute__((cleanup(string_set_clear)))
4875
string_set cancelled_filenames = {};
4876
__attribute__((cleanup(cleanup_queue)))
4877
task_queue *queue = create_queue();
4878
g_assert_nonnull(queue);
4880
__attribute__((cleanup(cleanup_string)))
4881
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4882
g_assert_nonnull(tempfilename);
4883
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4884
g_assert_cmpint(questionfile, >, 0);
4885
FILE *qf = fdopen(questionfile, "w");
4886
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
4887
g_assert_cmpint(fclose(qf), ==, 0);
4889
char *const filename = strdup(tempfilename);
4890
g_assert_nonnull(filename);
4891
task_context task = {
4892
.func=open_and_parse_question,
4893
.question_filename=filename,
4895
.password=(buffer[]){{}},
4897
.cancelled_filenames=&cancelled_filenames,
4898
.current_time=(mono_microsecs[]){0},
4899
.mandos_client_exited=(bool[]){false},
4900
.password_is_read=(bool[]){false},
4902
run_task_with_stderr_to_dev_null(task, queue);
4903
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4905
g_assert_cmpint(unlink(tempfilename), ==, 0);
4909
void test_open_and_parse_question_badpid(__attribute__((unused))
4910
test_fixture *fixture,
4911
__attribute__((unused))
4912
gconstpointer user_data){
4913
__attribute__((cleanup(cleanup_close)))
4914
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4915
g_assert_cmpint(epoll_fd, >=, 0);
4916
__attribute__((cleanup(string_set_clear)))
4917
string_set cancelled_filenames = {};
4918
__attribute__((cleanup(cleanup_queue)))
4919
task_queue *queue = create_queue();
4920
g_assert_nonnull(queue);
4922
__attribute__((cleanup(cleanup_string)))
4923
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4924
g_assert_nonnull(tempfilename);
4925
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4926
g_assert_cmpint(questionfile, >, 0);
4927
FILE *qf = fdopen(questionfile, "w");
4928
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
4930
g_assert_cmpint(fclose(qf), ==, 0);
4932
char *const filename = strdup(tempfilename);
4933
g_assert_nonnull(filename);
4934
task_context task = {
4935
.func=open_and_parse_question,
4936
.question_filename=filename,
4938
.password=(buffer[]){{}},
4940
.cancelled_filenames=&cancelled_filenames,
4941
.current_time=(mono_microsecs[]){0},
4942
.mandos_client_exited=(bool[]){false},
4943
.password_is_read=(bool[]){false},
4945
run_task_with_stderr_to_dev_null(task, queue);
4946
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4948
g_assert_cmpint(unlink(tempfilename), ==, 0);
4952
test_open_and_parse_question_noexist_pid(__attribute__((unused))
4953
test_fixture *fixture,
4954
__attribute__((unused))
4955
gconstpointer user_data){
4956
__attribute__((cleanup(cleanup_close)))
4957
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4958
g_assert_cmpint(epoll_fd, >=, 0);
4959
__attribute__((cleanup(string_set_clear)))
4960
string_set cancelled_filenames = {};
4961
buffer password = {};
4962
bool mandos_client_exited = false;
4963
bool password_is_read = false;
4964
__attribute__((cleanup(cleanup_queue)))
4965
task_queue *queue = create_queue();
4966
g_assert_nonnull(queue);
4967
const mono_microsecs current_time = 0;
4969
/* Find value of sysctl kernel.pid_max */
4970
uintmax_t pid_max = 0;
4971
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
4972
g_assert_nonnull(sysctl_pid_max);
4973
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
4975
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
4977
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
4978
g_assert_true(nonexisting_pid > 0); /* Overflow check */
4980
__attribute__((cleanup(cleanup_string)))
4981
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4982
g_assert_nonnull(tempfilename);
4983
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4984
g_assert_cmpint(questionfile, >, 0);
4985
FILE *qf = fdopen(questionfile, "w");
4986
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
4987
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
4989
g_assert_cmpint(fclose(qf), ==, 0);
4991
char *const question_filename = strdup(tempfilename);
4992
g_assert_nonnull(question_filename);
4993
task_context task = {
4994
.func=open_and_parse_question,
4995
.question_filename=question_filename,
4997
.password=&password,
4998
.filename=question_filename,
4999
.cancelled_filenames=&cancelled_filenames,
5000
.current_time=¤t_time,
5001
.mandos_client_exited=&mandos_client_exited,
5002
.password_is_read=&password_is_read,
5004
run_task_with_stderr_to_dev_null(task, queue);
5005
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5007
g_assert_cmpint(unlink(tempfilename), ==, 0);
5011
test_open_and_parse_question_no_notafter(__attribute__((unused))
5012
test_fixture *fixture,
5013
__attribute__((unused))
5014
gconstpointer user_data){
5015
__attribute__((cleanup(cleanup_close)))
5016
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5017
g_assert_cmpint(epoll_fd, >=, 0);
5018
__attribute__((cleanup(string_set_clear)))
5019
string_set cancelled_filenames = {};
5020
buffer password = {};
5021
bool mandos_client_exited = false;
5022
bool password_is_read = false;
5023
__attribute__((cleanup(cleanup_queue)))
5024
task_queue *queue = create_queue();
5025
g_assert_nonnull(queue);
5026
const mono_microsecs current_time = 0;
5028
__attribute__((cleanup(cleanup_string)))
5029
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5030
g_assert_nonnull(tempfilename);
5031
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5032
g_assert_cmpint(questionfile, >, 0);
5033
FILE *qf = fdopen(questionfile, "w");
5034
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5035
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5036
g_assert_cmpint(fclose(qf), ==, 0);
5038
char *const filename = strdup(tempfilename);
5039
g_assert_nonnull(filename);
5040
task_context task = {
5041
.func=open_and_parse_question,
5042
.question_filename=filename,
5044
.password=&password,
5046
.cancelled_filenames=&cancelled_filenames,
5047
.current_time=¤t_time,
5048
.mandos_client_exited=&mandos_client_exited,
5049
.password_is_read=&password_is_read,
5051
task.func(task, queue);
5052
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5054
__attribute__((cleanup(cleanup_string)))
5055
char *socket_filename = strdup("/nonexistent");
5056
g_assert_nonnull(socket_filename);
5057
g_assert_nonnull(find_matching_task(queue, (task_context){
5058
.func=connect_question_socket,
5059
.question_filename=tempfilename,
5060
.filename=socket_filename,
5062
.password=&password,
5063
.current_time=¤t_time,
5064
.mandos_client_exited=&mandos_client_exited,
5065
.password_is_read=&password_is_read,
5068
g_assert_true(queue->next_run != 0);
5070
g_assert_cmpint(unlink(tempfilename), ==, 0);
5074
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5075
test_fixture *fixture,
5076
__attribute__((unused))
5077
gconstpointer user_data){
5078
__attribute__((cleanup(cleanup_close)))
5079
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5080
g_assert_cmpint(epoll_fd, >=, 0);
5081
__attribute__((cleanup(string_set_clear)))
5082
string_set cancelled_filenames = {};
5083
buffer password = {};
5084
bool mandos_client_exited = false;
5085
bool password_is_read = false;
5086
__attribute__((cleanup(cleanup_queue)))
5087
task_queue *queue = create_queue();
5088
g_assert_nonnull(queue);
5089
const mono_microsecs current_time = 0;
5091
__attribute__((cleanup(cleanup_string)))
5092
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5093
g_assert_nonnull(tempfilename);
5094
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5095
g_assert_cmpint(questionfile, >, 0);
5096
FILE *qf = fdopen(questionfile, "w");
5097
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5098
PRIuMAX "\nNotAfter=\n",
5099
(uintmax_t)getpid()), >, 0);
5100
g_assert_cmpint(fclose(qf), ==, 0);
5102
char *const filename = strdup(tempfilename);
5103
g_assert_nonnull(filename);
5104
task_context task = {
5105
.func=open_and_parse_question,
5106
.question_filename=filename,
5108
.password=&password,
5110
.cancelled_filenames=&cancelled_filenames,
5111
.current_time=¤t_time,
5112
.mandos_client_exited=&mandos_client_exited,
5113
.password_is_read=&password_is_read,
5115
run_task_with_stderr_to_dev_null(task, queue);
5116
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5118
__attribute__((cleanup(cleanup_string)))
5119
char *socket_filename = strdup("/nonexistent");
5120
g_assert_nonnull(find_matching_task(queue, (task_context){
5121
.func=connect_question_socket,
5122
.question_filename=tempfilename,
5123
.filename=socket_filename,
5125
.password=&password,
5126
.current_time=¤t_time,
5127
.mandos_client_exited=&mandos_client_exited,
5128
.password_is_read=&password_is_read,
5130
g_assert_true(queue->next_run != 0);
5132
g_assert_cmpint(unlink(tempfilename), ==, 0);
5136
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5138
const mono_microsecs
5140
const mono_microsecs
5142
__attribute__((cleanup(cleanup_close)))
5143
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5144
g_assert_cmpint(epoll_fd, >=, 0);
5145
__attribute__((cleanup(string_set_clear)))
5146
string_set cancelled_filenames = {};
5147
buffer password = {};
5148
bool mandos_client_exited = false;
5149
bool password_is_read = false;
5150
__attribute__((cleanup(cleanup_queue)))
5151
task_queue *queue = create_queue();
5152
g_assert_nonnull(queue);
5153
queue->next_run = next_queue_run;
5155
__attribute__((cleanup(cleanup_string)))
5156
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5157
g_assert_nonnull(tempfilename);
5158
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5159
g_assert_cmpint(questionfile, >, 0);
5160
FILE *qf = fdopen(questionfile, "w");
5161
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5162
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5163
(uintmax_t)getpid(), notafter), >, 0);
5164
g_assert_cmpint(fclose(qf), ==, 0);
5166
char *const filename = strdup(tempfilename);
5167
g_assert_nonnull(filename);
5168
task_context task = {
5169
.func=open_and_parse_question,
5170
.question_filename=filename,
5172
.password=&password,
5174
.cancelled_filenames=&cancelled_filenames,
5175
.current_time=¤t_time,
5176
.mandos_client_exited=&mandos_client_exited,
5177
.password_is_read=&password_is_read,
5179
task.func(task, queue);
5181
if(queue->length >= 1){
5182
__attribute__((cleanup(cleanup_string)))
5183
char *socket_filename = strdup("/nonexistent");
5184
g_assert_nonnull(find_matching_task(queue, (task_context){
5185
.func=connect_question_socket,
5186
.filename=socket_filename,
5188
.password=&password,
5189
.current_time=¤t_time,
5190
.cancelled_filenames=&cancelled_filenames,
5191
.mandos_client_exited=&mandos_client_exited,
5192
.password_is_read=&password_is_read,
5194
g_assert_true(queue->next_run != 0);
5198
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5199
} else if(current_time >= notafter) {
5200
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5202
g_assert_nonnull(find_matching_task(queue, (task_context){
5203
.func=cancel_old_question,
5204
.question_filename=tempfilename,
5205
.filename=tempfilename,
5207
.cancelled_filenames=&cancelled_filenames,
5208
.current_time=¤t_time,
5211
g_assert_true(queue->next_run == 1);
5213
g_assert_cmpint(unlink(tempfilename), ==, 0);
5217
test_open_and_parse_question_notafter_0(__attribute__((unused))
5218
test_fixture *fixture,
5219
__attribute__((unused))
5220
gconstpointer user_data){
5221
/* current_time, notafter, next_queue_run */
5222
assert_open_and_parse_question_with_notafter(0, 0, 0);
5226
test_open_and_parse_question_notafter_1(__attribute__((unused))
5227
test_fixture *fixture,
5228
__attribute__((unused))
5229
gconstpointer user_data){
5230
/* current_time, notafter, next_queue_run */
5231
assert_open_and_parse_question_with_notafter(0, 1, 0);
5235
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5236
test_fixture *fixture,
5237
__attribute__((unused))
5238
gconstpointer user_data){
5239
/* current_time, notafter, next_queue_run */
5240
assert_open_and_parse_question_with_notafter(0, 1, 1);
5244
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5245
test_fixture *fixture,
5246
__attribute__((unused))
5247
gconstpointer user_data){
5248
/* current_time, notafter, next_queue_run */
5249
assert_open_and_parse_question_with_notafter(0, 1, 2);
5253
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5254
test_fixture *fixture,
5255
__attribute__((unused))
5256
gconstpointer user_data){
5257
/* current_time, notafter, next_queue_run */
5258
assert_open_and_parse_question_with_notafter(1, 1, 0);
5262
test_open_and_parse_question_late_notafter(__attribute__((unused))
5263
test_fixture *fixture,
5264
__attribute__((unused))
5265
gconstpointer user_data){
5266
/* current_time, notafter, next_queue_run */
5267
assert_open_and_parse_question_with_notafter(2, 1, 0);
5270
static void assert_cancel_old_question_param(const mono_microsecs
5272
const mono_microsecs
5274
const mono_microsecs
5276
const mono_microsecs
5278
__attribute__((cleanup(string_set_clear)))
5279
string_set cancelled_filenames = {};
5280
__attribute__((cleanup(cleanup_queue)))
5281
task_queue *queue = create_queue();
5282
g_assert_nonnull(queue);
5283
queue->next_run = next_queue_run;
5285
char *const question_filename = strdup("/nonexistent");
5286
g_assert_nonnull(question_filename);
5287
task_context task = {
5288
.func=cancel_old_question,
5289
.question_filename=question_filename,
5290
.filename=question_filename,
5292
.cancelled_filenames=&cancelled_filenames,
5293
.current_time=¤t_time,
5295
task.func(task, queue);
5297
if(current_time >= notafter){
5298
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5299
g_assert_true(string_set_contains(cancelled_filenames,
5302
g_assert_nonnull(find_matching_task(queue, (task_context){
5303
.func=cancel_old_question,
5304
.question_filename=question_filename,
5305
.filename=question_filename,
5307
.cancelled_filenames=&cancelled_filenames,
5308
.current_time=¤t_time,
5311
g_assert_false(string_set_contains(cancelled_filenames,
5312
question_filename));
5314
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5315
(unsigned int)next_set_to);
5318
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5319
test_fixture *fixture,
5320
__attribute__((unused))
5321
gconstpointer user_data){
5322
/* next_queue_run unset,
5323
cancellation should happen because time has come,
5324
next_queue_run should be unchanged */
5325
/* next_queue_run, notafter, current_time, next_set_to */
5326
assert_cancel_old_question_param(0, 1, 2, 0);
5329
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5330
test_fixture *fixture,
5331
__attribute__((unused))
5332
gconstpointer user_data){
5333
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5334
and current_time is not yet notafter or greater,
5335
update value of next_queue_run to value of notafter */
5336
/* next_queue_run, notafter, current_time, next_set_to */
5337
assert_cancel_old_question_param(0, 2, 1, 2);
5340
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5341
test_fixture *fixture,
5342
__attribute__((unused))
5343
gconstpointer user_data){
5344
/* next_queue_run 1,
5345
cancellation should happen because time has come,
5346
next_queue_run should be unchanged */
5347
/* next_queue_run, notafter, current_time, next_set_to */
5348
assert_cancel_old_question_param(1, 2, 3, 1);
5351
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5352
test_fixture *fixture,
5353
__attribute__((unused))
5354
gconstpointer user_data){
5355
/* If next_queue_run is set,
5356
and current_time is not yet notafter or greater,
5357
and notafter is larger than next_queue_run
5358
next_queue_run should be unchanged */
5359
/* next_queue_run, notafter, current_time, next_set_to */
5360
assert_cancel_old_question_param(1, 3, 2, 1);
5363
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5364
test_fixture *fixture,
5365
__attribute__((unused))
5366
gconstpointer user_data){
5367
/* next_queue_run 2,
5368
cancellation should happen because time has come,
5369
next_queue_run should be unchanged */
5370
/* next_queue_run, notafter, current_time, next_set_to */
5371
assert_cancel_old_question_param(2, 1, 3, 2);
5374
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5375
test_fixture *fixture,
5376
__attribute__((unused))
5377
gconstpointer user_data){
5378
/* If next_queue_run is set,
5379
and current_time is not yet notafter or greater,
5380
and notafter is larger than next_queue_run
5381
next_queue_run should be unchanged */
5382
/* next_queue_run, notafter, current_time, next_set_to */
5383
assert_cancel_old_question_param(2, 3, 1, 2);
5386
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5387
test_fixture *fixture,
5388
__attribute__((unused))
5389
gconstpointer user_data){
5390
/* next_queue_run 3,
5391
cancellation should happen because time has come,
5392
next_queue_run should be unchanged */
5393
/* next_queue_run, notafter, current_time, next_set_to */
5394
assert_cancel_old_question_param(3, 1, 2, 3);
5397
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5398
test_fixture *fixture,
5399
__attribute__((unused))
5400
gconstpointer user_data){
5401
/* If next_queue_run is set,
5402
and current_time is not yet notafter or greater,
5403
and notafter is smaller than next_queue_run
5404
update value of next_queue_run to value of notafter */
5405
/* next_queue_run, notafter, current_time, next_set_to */
5406
assert_cancel_old_question_param(3, 2, 1, 2);
5410
test_connect_question_socket_name_too_long(__attribute__((unused))
5411
test_fixture *fixture,
5412
__attribute__((unused))
5413
gconstpointer user_data){
5414
__attribute__((cleanup(cleanup_close)))
5415
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5416
g_assert_cmpint(epoll_fd, >=, 0);
5417
const char question_filename[] = "/nonexistent/question";
5418
__attribute__((cleanup(string_set_clear)))
5419
string_set cancelled_filenames = {};
5420
__attribute__((cleanup(cleanup_queue)))
5421
task_queue *queue = create_queue();
5422
g_assert_nonnull(queue);
5423
__attribute__((cleanup(cleanup_string)))
5424
char *tempdir = make_temporary_directory();
5425
g_assert_nonnull(tempdir);
5426
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5427
char socket_name[sizeof(unix_socket.sun_path)];
5428
memset(socket_name, 'x', sizeof(socket_name));
5429
socket_name[sizeof(socket_name)-1] = '\0';
5430
char *filename = NULL;
5431
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5433
g_assert_nonnull(filename);
5435
task_context task = {
5436
.func=connect_question_socket,
5437
.question_filename=strdup(question_filename),
5439
.password=(buffer[]){{}},
5441
.cancelled_filenames=&cancelled_filenames,
5442
.mandos_client_exited=(bool[]){false},
5443
.password_is_read=(bool[]){false},
5444
.current_time=(mono_microsecs[]){0},
5446
g_assert_nonnull(task.question_filename);
5447
run_task_with_stderr_to_dev_null(task, queue);
5449
g_assert_true(string_set_contains(cancelled_filenames,
5450
question_filename));
5451
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5452
g_assert_true(queue->next_run == 0);
5454
g_assert_cmpint(rmdir(tempdir), ==, 0);
5458
void test_connect_question_socket_connect_fail(__attribute__((unused))
5459
test_fixture *fixture,
5460
__attribute__((unused))
5463
__attribute__((cleanup(cleanup_close)))
5464
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5465
g_assert_cmpint(epoll_fd, >=, 0);
5466
const char question_filename[] = "/nonexistent/question";
5467
__attribute__((cleanup(string_set_clear)))
5468
string_set cancelled_filenames = {};
5469
const mono_microsecs current_time = 3;
5470
__attribute__((cleanup(cleanup_queue)))
5471
task_queue *queue = create_queue();
5472
g_assert_nonnull(queue);
5473
__attribute__((cleanup(cleanup_string)))
5474
char *tempdir = make_temporary_directory();
5475
g_assert_nonnull(tempdir);
5476
char socket_name[] = "nonexistent";
5477
char *filename = NULL;
5478
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5480
g_assert_nonnull(filename);
5482
task_context task = {
5483
.func=connect_question_socket,
5484
.question_filename=strdup(question_filename),
5486
.password=(buffer[]){{}},
5488
.cancelled_filenames=&cancelled_filenames,
5489
.mandos_client_exited=(bool[]){false},
5490
.password_is_read=(bool[]){false},
5491
.current_time=¤t_time,
5493
g_assert_nonnull(task.question_filename);
5494
run_task_with_stderr_to_dev_null(task, queue);
5496
g_assert_nonnull(find_matching_task(queue, task));
5498
g_assert_false(string_set_contains(cancelled_filenames,
5499
question_filename));
5500
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5501
g_assert_true(queue->next_run == 1000000 + current_time);
5503
g_assert_cmpint(rmdir(tempdir), ==, 0);
5507
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5508
test_fixture *fixture,
5509
__attribute__((unused))
5510
gconstpointer user_data){
5511
__attribute__((cleanup(cleanup_close)))
5512
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5513
__attribute__((cleanup(cleanup_string)))
5514
char *const question_filename = strdup("/nonexistent/question");
5515
g_assert_nonnull(question_filename);
5516
__attribute__((cleanup(string_set_clear)))
5517
string_set cancelled_filenames = {};
5518
const mono_microsecs current_time = 5;
5519
__attribute__((cleanup(cleanup_queue)))
5520
task_queue *queue = create_queue();
5521
g_assert_nonnull(queue);
5522
__attribute__((cleanup(cleanup_string)))
5523
char *tempdir = make_temporary_directory();
5524
g_assert_nonnull(tempdir);
5525
__attribute__((cleanup(cleanup_close)))
5526
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5527
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5528
g_assert_cmpint(sock_fd, >=, 0);
5529
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5530
const char socket_name[] = "socket_name";
5531
__attribute__((cleanup(cleanup_string)))
5532
char *filename = NULL;
5533
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5535
g_assert_nonnull(filename);
5536
g_assert_cmpint((int)strlen(filename), <,
5537
(int)sizeof(sock_name.sun_path));
5538
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5539
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5540
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5541
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5542
task_context task = {
5543
.func=connect_question_socket,
5544
.question_filename=strdup(question_filename),
5546
.password=(buffer[]){{}},
5547
.filename=strdup(filename),
5548
.cancelled_filenames=&cancelled_filenames,
5549
.mandos_client_exited=(bool[]){false},
5550
.password_is_read=(bool[]){false},
5551
.current_time=¤t_time,
5553
g_assert_nonnull(task.question_filename);
5554
run_task_with_stderr_to_dev_null(task, queue);
5556
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5557
const task_context *const added_task
5558
= find_matching_task(queue, task);
5559
g_assert_nonnull(added_task);
5560
g_assert_true(queue->next_run == 1000000 + current_time);
5562
g_assert_cmpint(unlink(filename), ==, 0);
5563
g_assert_cmpint(rmdir(tempdir), ==, 0);
5567
void test_connect_question_socket_usable(__attribute__((unused))
5568
test_fixture *fixture,
5569
__attribute__((unused))
5570
gconstpointer user_data){
5571
__attribute__((cleanup(cleanup_close)))
5572
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5573
g_assert_cmpint(epoll_fd, >=, 0);
5574
__attribute__((cleanup(cleanup_string)))
5575
char *const question_filename = strdup("/nonexistent/question");
5576
g_assert_nonnull(question_filename);
5577
__attribute__((cleanup(string_set_clear)))
5578
string_set cancelled_filenames = {};
5579
buffer password = {};
5580
bool mandos_client_exited = false;
5581
bool password_is_read = false;
5582
const mono_microsecs current_time = 0;
5583
__attribute__((cleanup(cleanup_queue)))
5584
task_queue *queue = create_queue();
5585
g_assert_nonnull(queue);
5586
__attribute__((cleanup(cleanup_string)))
5587
char *tempdir = make_temporary_directory();
5588
g_assert_nonnull(tempdir);
5589
__attribute__((cleanup(cleanup_close)))
5590
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5591
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5592
g_assert_cmpint(sock_fd, >=, 0);
5593
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5594
const char socket_name[] = "socket_name";
5595
__attribute__((cleanup(cleanup_string)))
5596
char *filename = NULL;
5597
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5599
g_assert_nonnull(filename);
5600
g_assert_cmpint((int)strlen(filename), <,
5601
(int)sizeof(sock_name.sun_path));
5602
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5603
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5604
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5605
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5606
task_context task = {
5607
.func=connect_question_socket,
5608
.question_filename=strdup(question_filename),
5610
.password=&password,
5611
.filename=strdup(filename),
5612
.cancelled_filenames=&cancelled_filenames,
5613
.mandos_client_exited=&mandos_client_exited,
5614
.password_is_read=&password_is_read,
5615
.current_time=¤t_time,
5617
g_assert_nonnull(task.question_filename);
5618
task.func(task, queue);
5620
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5621
const task_context *const added_task
5622
= find_matching_task(queue, (task_context){
5623
.func=send_password_to_socket,
5624
.question_filename=question_filename,
5627
.password=&password,
5628
.cancelled_filenames=&cancelled_filenames,
5629
.mandos_client_exited=&mandos_client_exited,
5630
.password_is_read=&password_is_read,
5631
.current_time=¤t_time,
5633
g_assert_nonnull(added_task);
5634
g_assert_cmpint(added_task->fd, >, 0);
5636
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5639
const int fd = added_task->fd;
5640
g_assert_cmpint(fd, >, 0);
5641
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5644
char write_data[PIPE_BUF];
5646
/* Construct test password buffer */
5647
/* Start with + since that is what the real procotol uses */
5648
write_data[0] = '+';
5649
/* Set a special character at string end just to mark the end */
5650
write_data[sizeof(write_data)-2] = 'y';
5651
/* Set NUL at buffer end, as suggested by the protocol */
5652
write_data[sizeof(write_data)-1] = '\0';
5653
/* Fill rest of password with 'x' */
5654
memset(write_data+1, 'x', sizeof(write_data)-3);
5655
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5656
MSG_NOSIGNAL), ==, sizeof(write_data));
5659
/* read from sock_fd */
5660
char read_data[sizeof(write_data)];
5661
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5662
==, sizeof(read_data));
5664
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5667
/* writing to sock_fd should fail */
5668
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5669
MSG_NOSIGNAL), <, 0);
5671
/* reading from fd should fail */
5672
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5673
MSG_NOSIGNAL), <, 0);
5675
g_assert_cmpint(unlink(filename), ==, 0);
5676
g_assert_cmpint(rmdir(tempdir), ==, 0);
5680
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5681
test_fixture *fixture,
5682
__attribute__((unused))
5685
__attribute__((cleanup(cleanup_close)))
5686
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5687
g_assert_cmpint(epoll_fd, >=, 0);
5688
__attribute__((cleanup(cleanup_string)))
5689
char *const question_filename = strdup("/nonexistent/question");
5690
g_assert_nonnull(question_filename);
5691
__attribute__((cleanup(cleanup_string)))
5692
char *const filename = strdup("/nonexistent/socket");
5693
g_assert_nonnull(filename);
5694
__attribute__((cleanup(string_set_clear)))
5695
string_set cancelled_filenames = {};
5696
buffer password = {};
5697
bool password_is_read = true;
5698
__attribute__((cleanup(cleanup_queue)))
5699
task_queue *queue = create_queue();
5700
g_assert_nonnull(queue);
5702
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5703
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5705
__attribute__((cleanup(cleanup_close)))
5706
const int read_socket = socketfds[0];
5707
const int write_socket = socketfds[1];
5708
task_context task = {
5709
.func=send_password_to_socket,
5710
.question_filename=strdup(question_filename),
5711
.filename=strdup(filename),
5714
.password=&password,
5715
.cancelled_filenames=&cancelled_filenames,
5716
.mandos_client_exited=(bool[]){false},
5717
.password_is_read=&password_is_read,
5718
.current_time=(mono_microsecs[]){0},
5720
g_assert_nonnull(task.question_filename);
5722
task.func(task, queue);
5724
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5726
const task_context *const added_task
5727
= find_matching_task(queue, task);
5728
g_assert_nonnull(added_task);
5729
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5730
g_assert_true(password_is_read);
5732
g_assert_cmpint(added_task->fd, >, 0);
5733
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5738
test_send_password_to_socket_password_not_read(__attribute__((unused))
5739
test_fixture *fixture,
5740
__attribute__((unused))
5743
__attribute__((cleanup(cleanup_close)))
5744
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5745
g_assert_cmpint(epoll_fd, >=, 0);
5746
__attribute__((cleanup(cleanup_string)))
5747
char *const question_filename = strdup("/nonexistent/question");
5748
g_assert_nonnull(question_filename);
5749
__attribute__((cleanup(cleanup_string)))
5750
char *const filename = strdup("/nonexistent/socket");
5751
__attribute__((cleanup(string_set_clear)))
5752
string_set cancelled_filenames = {};
5753
buffer password = {};
5754
__attribute__((cleanup(cleanup_queue)))
5755
task_queue *queue = create_queue();
5756
g_assert_nonnull(queue);
5758
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5759
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5761
__attribute__((cleanup(cleanup_close)))
5762
const int read_socket = socketfds[0];
5763
const int write_socket = socketfds[1];
5764
task_context task = {
5765
.func=send_password_to_socket,
5766
.question_filename=strdup(question_filename),
5767
.filename=strdup(filename),
5770
.password=&password,
5771
.cancelled_filenames=&cancelled_filenames,
5772
.mandos_client_exited=(bool[]){false},
5773
.password_is_read=(bool[]){false},
5774
.current_time=(mono_microsecs[]){0},
5776
g_assert_nonnull(task.question_filename);
5778
task.func(task, queue);
5780
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5782
const task_context *const added_task = find_matching_task(queue,
5784
g_assert_nonnull(added_task);
5785
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5786
g_assert_true(queue->next_run == 0);
5788
g_assert_cmpint(added_task->fd, >, 0);
5789
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5794
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5795
test_fixture *fixture,
5796
__attribute__((unused))
5797
gconstpointer user_data){
5798
__attribute__((cleanup(cleanup_close)))
5799
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5800
g_assert_cmpint(epoll_fd, >=, 0);
5801
const char question_filename[] = "/nonexistent/question";
5802
char *const filename = strdup("/nonexistent/socket");
5803
__attribute__((cleanup(string_set_clear)))
5804
string_set cancelled_filenames = {};
5805
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5806
__attribute__((cleanup(cleanup_buffer)))
5808
.data=malloc(oversized),
5810
.allocated=oversized,
5812
g_assert_nonnull(password.data);
5813
if(mlock(password.data, password.allocated) != 0){
5814
g_assert_true(errno == EPERM or errno == ENOMEM);
5816
/* Construct test password buffer */
5817
/* Start with + since that is what the real procotol uses */
5818
password.data[0] = '+';
5819
/* Set a special character at string end just to mark the end */
5820
password.data[oversized-3] = 'y';
5821
/* Set NUL at buffer end, as suggested by the protocol */
5822
password.data[oversized-2] = '\0';
5823
/* Fill rest of password with 'x' */
5824
memset(password.data+1, 'x', oversized-3);
5826
__attribute__((cleanup(cleanup_queue)))
5827
task_queue *queue = create_queue();
5828
g_assert_nonnull(queue);
5830
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5831
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5833
__attribute__((cleanup(cleanup_close)))
5834
const int read_socket = socketfds[0];
5835
__attribute__((cleanup(cleanup_close)))
5836
const int write_socket = socketfds[1];
5837
task_context task = {
5838
.func=send_password_to_socket,
5839
.question_filename=strdup(question_filename),
5843
.password=&password,
5844
.cancelled_filenames=&cancelled_filenames,
5845
.mandos_client_exited=(bool[]){true},
5846
.password_is_read=(bool[]){true},
5847
.current_time=(mono_microsecs[]){0},
5849
g_assert_nonnull(task.question_filename);
5851
run_task_with_stderr_to_dev_null(task, queue);
5853
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5854
g_assert_true(string_set_contains(cancelled_filenames,
5855
question_filename));
5858
static void test_send_password_to_socket_retry(__attribute__((unused))
5859
test_fixture *fixture,
5860
__attribute__((unused))
5863
__attribute__((cleanup(cleanup_close)))
5864
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5865
g_assert_cmpint(epoll_fd, >=, 0);
5866
__attribute__((cleanup(cleanup_string)))
5867
char *const question_filename = strdup("/nonexistent/question");
5868
g_assert_nonnull(question_filename);
5869
__attribute__((cleanup(cleanup_string)))
5870
char *const filename = strdup("/nonexistent/socket");
5871
g_assert_nonnull(filename);
5872
__attribute__((cleanup(string_set_clear)))
5873
string_set cancelled_filenames = {};
5874
__attribute__((cleanup(cleanup_buffer)))
5875
buffer password = {};
5877
__attribute__((cleanup(cleanup_queue)))
5878
task_queue *queue = create_queue();
5879
g_assert_nonnull(queue);
5881
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5882
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5884
__attribute__((cleanup(cleanup_close)))
5885
const int read_socket = socketfds[0];
5886
const int write_socket = socketfds[1];
5887
/* Close the server side socket to force ECONNRESET on client */
5888
g_assert_cmpint(close(read_socket), ==, 0);
5889
task_context task = {
5890
.func=send_password_to_socket,
5891
.question_filename=strdup(question_filename),
5892
.filename=strdup(filename),
5895
.password=&password,
5896
.cancelled_filenames=&cancelled_filenames,
5897
.mandos_client_exited=(bool[]){true},
5898
.password_is_read=(bool[]){true},
5899
.current_time=(mono_microsecs[]){0},
5901
g_assert_nonnull(task.question_filename);
5903
task.func(task, queue);
5905
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5907
const task_context *const added_task = find_matching_task(queue,
5909
g_assert_nonnull(added_task);
5910
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5912
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5917
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
5918
test_fixture *fixture,
5919
__attribute__((unused))
5920
gconstpointer user_data){
5921
__attribute__((cleanup(cleanup_close)))
5922
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5923
__attribute__((cleanup(cleanup_string)))
5924
char *const question_filename = strdup("/nonexistent/question");
5925
g_assert_nonnull(question_filename);
5926
__attribute__((cleanup(cleanup_string)))
5927
char *const filename = strdup("/nonexistent/socket");
5928
g_assert_nonnull(filename);
5929
__attribute__((cleanup(string_set_clear)))
5930
string_set cancelled_filenames = {};
5931
__attribute__((cleanup(cleanup_buffer)))
5932
buffer password = {};
5934
const mono_microsecs current_time = 11;
5935
__attribute__((cleanup(cleanup_queue)))
5936
task_queue *queue = create_queue();
5937
g_assert_nonnull(queue);
5939
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5940
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5942
__attribute__((cleanup(cleanup_close)))
5943
const int read_socket = socketfds[0];
5944
const int write_socket = socketfds[1];
5945
/* Close the server side socket to force ECONNRESET on client */
5946
g_assert_cmpint(close(read_socket), ==, 0);
5947
task_context task = {
5948
.func=send_password_to_socket,
5949
.question_filename=strdup(question_filename),
5950
.filename=strdup(filename),
5953
.password=&password,
5954
.cancelled_filenames=&cancelled_filenames,
5955
.mandos_client_exited=(bool[]){true},
5956
.password_is_read=(bool[]){true},
5957
.current_time=¤t_time,
5959
g_assert_nonnull(task.question_filename);
5961
run_task_with_stderr_to_dev_null(task, queue);
5963
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5965
const task_context *const added_task = find_matching_task(queue,
5967
g_assert_nonnull(added_task);
5968
g_assert_true(queue->next_run == current_time + 1000000);
5969
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5972
static void assert_send_password_to_socket_password(buffer password){
5973
__attribute__((cleanup(cleanup_close)))
5974
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5975
g_assert_cmpint(epoll_fd, >=, 0);
5976
char *const question_filename = strdup("/nonexistent/question");
5977
g_assert_nonnull(question_filename);
5978
char *const filename = strdup("/nonexistent/socket");
5979
g_assert_nonnull(filename);
5980
__attribute__((cleanup(string_set_clear)))
5981
string_set cancelled_filenames = {};
5983
__attribute__((cleanup(cleanup_queue)))
5984
task_queue *queue = create_queue();
5985
g_assert_nonnull(queue);
5987
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5988
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5990
__attribute__((cleanup(cleanup_close)))
5991
const int read_socket = socketfds[0];
5992
const int write_socket = socketfds[1];
5993
task_context task = {
5994
.func=send_password_to_socket,
5995
.question_filename=question_filename,
5999
.password=&password,
6000
.cancelled_filenames=&cancelled_filenames,
6001
.mandos_client_exited=(bool[]){true},
6002
.password_is_read=(bool[]){true},
6003
.current_time=(mono_microsecs[]){0},
6006
char *expected_written_data = malloc(password.length + 2);
6007
g_assert_nonnull(expected_written_data);
6008
expected_written_data[0] = '+';
6009
expected_written_data[password.length + 1] = '\0';
6010
if(password.length > 0){
6011
g_assert_nonnull(password.data);
6012
memcpy(expected_written_data + 1, password.data, password.length);
6015
task.func(task, queue);
6018
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6019
(int)(password.length + 2));
6020
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6022
g_assert_true(memcmp(expected_written_data, buf,
6023
password.length + 2) == 0);
6025
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6027
free(expected_written_data);
6031
test_send_password_to_socket_null_password(__attribute__((unused))
6032
test_fixture *fixture,
6033
__attribute__((unused))
6034
gconstpointer user_data){
6035
__attribute__((cleanup(cleanup_buffer)))
6036
buffer password = {};
6037
assert_send_password_to_socket_password(password);
6041
test_send_password_to_socket_empty_password(__attribute__((unused))
6042
test_fixture *fixture,
6043
__attribute__((unused))
6044
gconstpointer user_data){
6045
__attribute__((cleanup(cleanup_buffer)))
6047
.data=malloc(1), /* because malloc(0) may return NULL */
6049
.allocated=0, /* deliberate lie */
6051
g_assert_nonnull(password.data);
6052
assert_send_password_to_socket_password(password);
6056
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6057
test_fixture *fixture,
6058
__attribute__((unused))
6059
gconstpointer user_data){
6060
__attribute__((cleanup(cleanup_buffer)))
6066
if(mlock(password.data, password.allocated) != 0){
6067
g_assert_true(errno == EPERM or errno == ENOMEM);
6069
assert_send_password_to_socket_password(password);
6073
test_send_password_to_socket_text_password(__attribute__((unused))
6074
test_fixture *fixture,
6075
__attribute__((unused))
6076
gconstpointer user_data){
6077
const char dummy_test_password[] = "dummy test password";
6078
__attribute__((cleanup(cleanup_buffer)))
6080
.data = strdup(dummy_test_password),
6081
.length = strlen(dummy_test_password),
6082
.allocated = sizeof(dummy_test_password),
6084
if(mlock(password.data, password.allocated) != 0){
6085
g_assert_true(errno == EPERM or errno == ENOMEM);
6087
assert_send_password_to_socket_password(password);
6091
test_send_password_to_socket_binary_password(__attribute__((unused))
6092
test_fixture *fixture,
6093
__attribute__((unused))
6094
gconstpointer user_data){
6095
__attribute__((cleanup(cleanup_buffer)))
6101
g_assert_nonnull(password.data);
6102
if(mlock(password.data, password.allocated) != 0){
6103
g_assert_true(errno == EPERM or errno == ENOMEM);
6105
char c = 1; /* Start at 1, avoiding NUL */
6106
for(int i=0; i < 255; i++){
6107
password.data[i] = c++;
6109
assert_send_password_to_socket_password(password);
6113
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6114
test_fixture *fixture,
6115
__attribute__((unused))
6118
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6119
__attribute__((cleanup(cleanup_buffer)))
6121
.data=malloc(sizeof(test_password)),
6122
.length=sizeof(test_password),
6123
.allocated=sizeof(test_password),
6125
g_assert_nonnull(password.data);
6126
if(mlock(password.data, password.allocated) !=0){
6127
g_assert_true(errno == EPERM or errno == ENOMEM);
6129
memcpy(password.data, test_password, password.allocated);
6130
assert_send_password_to_socket_password(password);
6133
static bool assert_add_existing_questions_to_devnull(task_queue
6146
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6147
test_fixture *fixture,
6148
__attribute__((unused))
6151
__attribute__((cleanup(cleanup_queue)))
6152
task_queue *queue = create_queue();
6153
g_assert_nonnull(queue);
6154
__attribute__((cleanup(cleanup_close)))
6155
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6156
g_assert_cmpint(epoll_fd, >=, 0);
6157
__attribute__((cleanup(string_set_clear)))
6158
string_set cancelled_filenames = {};
6160
g_assert_false(assert_add_existing_questions_to_devnull
6163
(buffer[]){{}}, /* password */
6164
&cancelled_filenames,
6165
(mono_microsecs[]){0}, /* current_time */
6166
(bool[]){false}, /* mandos_client_exited */
6167
(bool[]){false}, /* password_is_read */
6168
"/nonexistent")); /* dirname */
6170
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6174
bool assert_add_existing_questions_to_devnull(task_queue
6181
*cancelled_filenames,
6182
const mono_microsecs
6183
*const current_time,
6185
mandos_client_exited,
6190
__attribute__((cleanup(cleanup_close)))
6191
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6192
g_assert_cmpint(devnull_fd, >=, 0);
6193
__attribute__((cleanup(cleanup_close)))
6194
const int real_stderr_fd = dup(STDERR_FILENO);
6195
g_assert_cmpint(real_stderr_fd, >=, 0);
6196
dup2(devnull_fd, STDERR_FILENO);
6197
const bool ret = add_existing_questions(queue, epoll_fd, password,
6198
cancelled_filenames,
6200
mandos_client_exited,
6201
password_is_read, dirname);
6202
dup2(real_stderr_fd, STDERR_FILENO);
6207
void test_add_existing_questions_no_questions(__attribute__((unused))
6208
test_fixture *fixture,
6209
__attribute__((unused))
6212
__attribute__((cleanup(cleanup_queue)))
6213
task_queue *queue = create_queue();
6214
g_assert_nonnull(queue);
6215
__attribute__((cleanup(cleanup_close)))
6216
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6217
g_assert_cmpint(epoll_fd, >=, 0);
6218
__attribute__((cleanup(string_set_clear)))
6219
string_set cancelled_filenames = {};
6220
__attribute__((cleanup(cleanup_string)))
6221
char *tempdir = make_temporary_directory();
6222
g_assert_nonnull(tempdir);
6224
g_assert_false(assert_add_existing_questions_to_devnull
6227
(buffer[]){{}}, /* password */
6228
&cancelled_filenames,
6229
(mono_microsecs[]){0}, /* current_time */
6230
(bool[]){false}, /* mandos_client_exited */
6231
(bool[]){false}, /* password_is_read */
6234
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6236
g_assert_cmpint(rmdir(tempdir), ==, 0);
6239
static char *make_question_file_in_directory(const char *const);
6242
void test_add_existing_questions_one_question(__attribute__((unused))
6243
test_fixture *fixture,
6244
__attribute__((unused))
6247
__attribute__((cleanup(cleanup_queue)))
6248
task_queue *queue = create_queue();
6249
g_assert_nonnull(queue);
6250
__attribute__((cleanup(cleanup_close)))
6251
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6252
g_assert_cmpint(epoll_fd, >=, 0);
6253
__attribute__((cleanup(cleanup_buffer)))
6254
buffer password = {};
6255
__attribute__((cleanup(string_set_clear)))
6256
string_set cancelled_filenames = {};
6257
const mono_microsecs current_time = 0;
6258
bool mandos_client_exited = false;
6259
bool password_is_read = false;
6260
__attribute__((cleanup(cleanup_string)))
6261
char *tempdir = make_temporary_directory();
6262
g_assert_nonnull(tempdir);
6263
__attribute__((cleanup(cleanup_string)))
6264
char *question_filename
6265
= make_question_file_in_directory(tempdir);
6266
g_assert_nonnull(question_filename);
6268
g_assert_true(assert_add_existing_questions_to_devnull
6272
&cancelled_filenames,
6274
&mandos_client_exited,
6278
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6280
g_assert_nonnull(find_matching_task(queue, (task_context){
6281
.func=open_and_parse_question,
6283
.filename=question_filename,
6284
.question_filename=question_filename,
6285
.password=&password,
6286
.cancelled_filenames=&cancelled_filenames,
6287
.current_time=¤t_time,
6288
.mandos_client_exited=&mandos_client_exited,
6289
.password_is_read=&password_is_read,
6292
g_assert_true(queue->next_run == 1);
6294
g_assert_cmpint(unlink(question_filename), ==, 0);
6295
g_assert_cmpint(rmdir(tempdir), ==, 0);
6298
static char *make_question_file_in_directory(const char
6300
return make_temporary_prefixed_file_in_directory("ask.", dir);
6304
void test_add_existing_questions_two_questions(__attribute__((unused))
6305
test_fixture *fixture,
6306
__attribute__((unused))
6309
__attribute__((cleanup(cleanup_queue)))
6310
task_queue *queue = create_queue();
6311
g_assert_nonnull(queue);
6312
__attribute__((cleanup(cleanup_close)))
6313
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6314
g_assert_cmpint(epoll_fd, >=, 0);
6315
__attribute__((cleanup(cleanup_buffer)))
6316
buffer password = {};
6317
__attribute__((cleanup(string_set_clear)))
6318
string_set cancelled_filenames = {};
6319
const mono_microsecs current_time = 0;
6320
bool mandos_client_exited = false;
6321
bool password_is_read = false;
6322
__attribute__((cleanup(cleanup_string)))
6323
char *tempdir = make_temporary_directory();
6324
g_assert_nonnull(tempdir);
6325
__attribute__((cleanup(cleanup_string)))
6326
char *question_filename1
6327
= make_question_file_in_directory(tempdir);
6328
g_assert_nonnull(question_filename1);
6329
__attribute__((cleanup(cleanup_string)))
6330
char *question_filename2
6331
= make_question_file_in_directory(tempdir);
6332
g_assert_nonnull(question_filename2);
6334
g_assert_true(assert_add_existing_questions_to_devnull
6338
&cancelled_filenames,
6340
&mandos_client_exited,
6344
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6346
g_assert_true(queue->next_run == 1);
6348
__attribute__((cleanup(string_set_clear)))
6349
string_set seen_questions = {};
6351
bool queue_contains_question_opener(char *const question_filename){
6352
return(find_matching_task(queue, (task_context){
6353
.func=open_and_parse_question,
6355
.question_filename=question_filename,
6356
.password=&password,
6357
.cancelled_filenames=&cancelled_filenames,
6358
.current_time=¤t_time,
6359
.mandos_client_exited=&mandos_client_exited,
6360
.password_is_read=&password_is_read,
6364
g_assert_true(queue_contains_question_opener(question_filename1));
6365
g_assert_true(queue_contains_question_opener(question_filename2));
6367
g_assert_true(queue->next_run == 1);
6369
g_assert_cmpint(unlink(question_filename1), ==, 0);
6370
g_assert_cmpint(unlink(question_filename2), ==, 0);
6371
g_assert_cmpint(rmdir(tempdir), ==, 0);
6375
test_add_existing_questions_non_questions(__attribute__((unused))
6376
test_fixture *fixture,
6377
__attribute__((unused))
6378
gconstpointer user_data){
6379
__attribute__((cleanup(cleanup_queue)))
6380
task_queue *queue = create_queue();
6381
g_assert_nonnull(queue);
6382
__attribute__((cleanup(cleanup_close)))
6383
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6384
g_assert_cmpint(epoll_fd, >=, 0);
6385
__attribute__((cleanup(string_set_clear)))
6386
string_set cancelled_filenames = {};
6387
__attribute__((cleanup(cleanup_string)))
6388
char *tempdir = make_temporary_directory();
6389
g_assert_nonnull(tempdir);
6390
__attribute__((cleanup(cleanup_string)))
6391
char *question_filename1
6392
= make_temporary_file_in_directory(tempdir);
6393
g_assert_nonnull(question_filename1);
6394
__attribute__((cleanup(cleanup_string)))
6395
char *question_filename2
6396
= make_temporary_file_in_directory(tempdir);
6397
g_assert_nonnull(question_filename2);
6399
g_assert_false(assert_add_existing_questions_to_devnull
6402
(buffer[]){{}}, /* password */
6403
&cancelled_filenames,
6404
(mono_microsecs[]){0}, /* current_time */
6405
(bool[]){false}, /* mandos_client_exited */
6406
(bool[]){false}, /* password_is_read */
6409
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6411
g_assert_cmpint(unlink(question_filename1), ==, 0);
6412
g_assert_cmpint(unlink(question_filename2), ==, 0);
6413
g_assert_cmpint(rmdir(tempdir), ==, 0);
6417
test_add_existing_questions_both_types(__attribute__((unused))
6418
test_fixture *fixture,
6419
__attribute__((unused))
6420
gconstpointer user_data){
6421
__attribute__((cleanup(cleanup_queue)))
6422
task_queue *queue = create_queue();
6423
g_assert_nonnull(queue);
6424
__attribute__((cleanup(cleanup_close)))
6425
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6426
g_assert_cmpint(epoll_fd, >=, 0);
6427
__attribute__((cleanup(cleanup_buffer)))
6428
buffer password = {};
6429
__attribute__((cleanup(string_set_clear)))
6430
string_set cancelled_filenames = {};
6431
const mono_microsecs current_time = 0;
6432
bool mandos_client_exited = false;
6433
bool password_is_read = false;
6434
__attribute__((cleanup(cleanup_string)))
6435
char *tempdir = make_temporary_directory();
6436
g_assert_nonnull(tempdir);
6437
__attribute__((cleanup(cleanup_string)))
6438
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6439
g_assert_nonnull(tempfilename1);
6440
__attribute__((cleanup(cleanup_string)))
6441
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6442
g_assert_nonnull(tempfilename2);
6443
__attribute__((cleanup(cleanup_string)))
6444
char *question_filename
6445
= make_question_file_in_directory(tempdir);
6446
g_assert_nonnull(question_filename);
6448
g_assert_true(assert_add_existing_questions_to_devnull
6452
&cancelled_filenames,
6454
&mandos_client_exited,
6458
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6460
g_assert_nonnull(find_matching_task(queue, (task_context){
6461
.func=open_and_parse_question,
6463
.filename=question_filename,
6464
.question_filename=question_filename,
6465
.password=&password,
6466
.cancelled_filenames=&cancelled_filenames,
6467
.current_time=¤t_time,
6468
.mandos_client_exited=&mandos_client_exited,
6469
.password_is_read=&password_is_read,
6472
g_assert_true(queue->next_run == 1);
6474
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6475
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6476
g_assert_cmpint(unlink(question_filename), ==, 0);
6477
g_assert_cmpint(rmdir(tempdir), ==, 0);
6480
static void test_wait_for_event_timeout(__attribute__((unused))
6481
test_fixture *fixture,
6482
__attribute__((unused))
6483
gconstpointer user_data){
6484
__attribute__((cleanup(cleanup_close)))
6485
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6486
g_assert_cmpint(epoll_fd, >=, 0);
6488
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6491
static void test_wait_for_event_event(__attribute__((unused))
6492
test_fixture *fixture,
6493
__attribute__((unused))
6494
gconstpointer user_data){
6495
__attribute__((cleanup(cleanup_close)))
6496
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6497
g_assert_cmpint(epoll_fd, >=, 0);
6499
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6500
__attribute__((cleanup(cleanup_close)))
6501
const int read_pipe = pipefds[0];
6502
__attribute__((cleanup(cleanup_close)))
6503
const int write_pipe = pipefds[1];
6504
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6505
&(struct epoll_event)
6506
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6507
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6509
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6512
static void test_wait_for_event_sigchld(test_fixture *fixture,
6513
__attribute__((unused))
6514
gconstpointer user_data){
6515
const pid_t pid = fork();
6516
if(pid == 0){ /* Child */
6517
if(not restore_signal_handler(&fixture->orig_sigaction)){
6518
_exit(EXIT_FAILURE);
6520
if(not restore_sigmask(&fixture->orig_sigmask)){
6521
_exit(EXIT_FAILURE);
6525
g_assert_true(pid != -1);
6526
__attribute__((cleanup(cleanup_close)))
6527
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6528
g_assert_cmpint(epoll_fd, >=, 0);
6530
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6533
g_assert_true(waitpid(pid, &status, 0) == pid);
6534
g_assert_true(WIFEXITED(status));
6535
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6538
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6539
test_fixture *fixture,
6540
__attribute__((unused))
6541
gconstpointer user_data){
6542
__attribute__((cleanup(cleanup_queue)))
6543
task_queue *queue = create_queue();
6544
g_assert_nonnull(queue);
6545
queue->next_run = 1;
6546
__attribute__((cleanup(cleanup_close)))
6547
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6548
__attribute__((cleanup(string_set_clear)))
6549
string_set cancelled_filenames = {};
6550
bool quit_now = false;
6552
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6553
g_assert_false(quit_now);
6554
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6558
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6559
test_fixture *fixture,
6560
__attribute__((unused))
6563
__attribute__((cleanup(cleanup_queue)))
6564
task_queue *queue = create_queue();
6565
g_assert_nonnull(queue);
6566
__attribute__((cleanup(string_set_clear)))
6567
string_set cancelled_filenames = {};
6568
bool quit_now = false;
6569
const char question_filename[] = "/nonexistent/question_filename";
6570
g_assert_true(string_set_add(&cancelled_filenames,
6571
question_filename));
6573
g_assert_true(add_to_queue(queue,
6574
(task_context){ .func=dummy_func }));
6576
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6577
g_assert_false(quit_now);
6578
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6579
g_assert_false(string_set_contains(cancelled_filenames,
6580
question_filename));
6584
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6585
test_fixture *fixture,
6586
__attribute__((unused))
6589
__attribute__((cleanup(cleanup_queue)))
6590
task_queue *queue = create_queue();
6591
g_assert_nonnull(queue);
6592
__attribute__((cleanup(string_set_clear)))
6593
string_set cancelled_filenames = {};
6594
bool quit_now = false;
6596
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6597
__attribute__((cleanup(cleanup_close)))
6598
const int read_pipe = pipefds[0];
6599
g_assert_cmpint(close(pipefds[1]), ==, 0);
6600
const char question_filename[] = "/nonexistent/question_filename";
6601
g_assert_true(string_set_add(&cancelled_filenames,
6602
question_filename));
6603
__attribute__((nonnull))
6604
void quit_func(const task_context task,
6605
__attribute__((unused)) task_queue *const q){
6606
g_assert_nonnull(task.quit_now);
6607
*task.quit_now = true;
6609
task_context task = {
6611
.question_filename=strdup(question_filename),
6612
.quit_now=&quit_now,
6615
g_assert_nonnull(task.question_filename);
6617
g_assert_true(add_to_queue(queue, task));
6619
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6620
g_assert_false(quit_now);
6622
/* read_pipe should be closed already */
6624
bool read_pipe_closed = (close(read_pipe) == -1);
6625
read_pipe_closed &= (errno == EBADF);
6626
g_assert_true(read_pipe_closed);
6629
static void test_run_queue_one_task(__attribute__((unused))
6630
test_fixture *fixture,
6631
__attribute__((unused))
6632
gconstpointer user_data){
6633
__attribute__((cleanup(cleanup_queue)))
6634
task_queue *queue = create_queue();
6635
g_assert_nonnull(queue);
6636
__attribute__((cleanup(string_set_clear)))
6637
string_set cancelled_filenames = {};
6638
bool quit_now = false;
6640
__attribute__((nonnull))
6641
void next_run_func(__attribute__((unused))
6642
const task_context task,
6643
task_queue *const q){
6647
task_context task = {
6648
.func=next_run_func,
6650
g_assert_true(add_to_queue(queue, task));
6652
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6653
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6654
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6657
static void test_run_queue_two_tasks(__attribute__((unused))
6658
test_fixture *fixture,
6659
__attribute__((unused))
6660
gconstpointer user_data){
6661
__attribute__((cleanup(cleanup_queue)))
6662
task_queue *queue = create_queue();
6663
g_assert_nonnull(queue);
6664
queue->next_run = 1;
6665
__attribute__((cleanup(string_set_clear)))
6666
string_set cancelled_filenames = {};
6667
bool quit_now = false;
6668
bool mandos_client_exited = false;
6670
__attribute__((nonnull))
6671
void next_run_func(__attribute__((unused))
6672
const task_context task,
6673
task_queue *const q){
6677
__attribute__((nonnull))
6678
void exited_func(const task_context task,
6679
__attribute__((unused)) task_queue *const q){
6680
*task.mandos_client_exited = true;
6683
task_context task1 = {
6684
.func=next_run_func,
6686
g_assert_true(add_to_queue(queue, task1));
6688
task_context task2 = {
6690
.mandos_client_exited=&mandos_client_exited,
6692
g_assert_true(add_to_queue(queue, task2));
6694
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6695
g_assert_false(quit_now);
6696
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6697
g_assert_true(mandos_client_exited);
6698
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6701
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6702
test_fixture *fixture,
6703
__attribute__((unused))
6704
gconstpointer user_data){
6705
__attribute__((cleanup(cleanup_queue)))
6706
task_queue *queue = create_queue();
6707
g_assert_nonnull(queue);
6708
__attribute__((cleanup(string_set_clear)))
6709
string_set cancelled_filenames = {};
6710
bool quit_now = false;
6711
bool mandos_client_exited = false;
6712
bool password_is_read = false;
6714
__attribute__((nonnull))
6715
void set_exited_func(const task_context task,
6716
__attribute__((unused)) task_queue *const q){
6717
*task.mandos_client_exited = true;
6718
*task.quit_now = true;
6720
task_context task1 = {
6721
.func=set_exited_func,
6722
.quit_now=&quit_now,
6723
.mandos_client_exited=&mandos_client_exited,
6725
g_assert_true(add_to_queue(queue, task1));
6727
__attribute__((nonnull))
6728
void set_read_func(const task_context task,
6729
__attribute__((unused)) task_queue *const q){
6730
*task.quit_now = true;
6731
*task.password_is_read = true;
6733
task_context task2 = {
6734
.func=set_read_func,
6735
.quit_now=&quit_now,
6736
.password_is_read=&password_is_read,
6738
g_assert_true(add_to_queue(queue, task2));
6740
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6741
g_assert_true(quit_now);
6742
g_assert_true(mandos_client_exited xor password_is_read);
6743
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6746
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6747
test_fixture *fixture,
6748
__attribute__((unused))
6749
gconstpointer user_data){
6750
__attribute__((cleanup(cleanup_queue)))
6751
task_queue *queue = create_queue();
6752
g_assert_nonnull(queue);
6753
__attribute__((cleanup(string_set_clear)))
6754
string_set cancelled_filenames = {};
6756
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6757
__attribute__((cleanup(cleanup_close)))
6758
const int read_pipe = pipefds[0];
6759
__attribute__((cleanup(cleanup_close)))
6760
const int write_pipe = pipefds[1];
6761
bool quit_now = false;
6763
__attribute__((nonnull))
6764
void read_func(const task_context task,
6765
__attribute__((unused)) task_queue *const q){
6766
*task.quit_now = true;
6768
task_context task1 = {
6770
.quit_now=&quit_now,
6773
g_assert_true(add_to_queue(queue, task1));
6775
__attribute__((nonnull))
6776
void write_func(const task_context task,
6777
__attribute__((unused)) task_queue *const q){
6778
*task.quit_now = true;
6780
task_context task2 = {
6782
.quit_now=&quit_now,
6785
g_assert_true(add_to_queue(queue, task2));
6787
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6788
g_assert_true(quit_now);
6790
/* Either read_pipe or write_pipe should be closed already */
6792
bool close_read_pipe = (close(read_pipe) == -1);
6793
close_read_pipe &= (errno == EBADF);
6795
bool close_write_pipe = (close(write_pipe) == -1);
6796
close_write_pipe &= (errno == EBADF);
6797
g_assert_true(close_read_pipe xor close_write_pipe);
6798
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6801
static void test_setup_signal_handler(__attribute__((unused))
6802
test_fixture *fixture,
6803
__attribute__((unused))
6804
gconstpointer user_data){
6805
/* Save current SIGCHLD action, whatever it is */
6806
struct sigaction expected_sigchld_action;
6807
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6810
/* Act; i.e. run the setup_signal_handler() function */
6811
struct sigaction actual_old_sigchld_action;
6812
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6814
/* Check that the function correctly set "actual_old_sigchld_action"
6815
to the same values as the previously saved
6816
"expected_sigchld_action" */
6817
/* Check member sa_handler */
6818
g_assert_true(actual_old_sigchld_action.sa_handler
6819
== expected_sigchld_action.sa_handler);
6820
/* Check member sa_mask */
6821
for(int signum = 1; signum < NSIG; signum++){
6822
const int expected_old_block_state
6823
= sigismember(&expected_sigchld_action.sa_mask, signum);
6824
g_assert_cmpint(expected_old_block_state, >=, 0);
6825
const int actual_old_block_state
6826
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6827
g_assert_cmpint(actual_old_block_state, >=, 0);
6828
g_assert_cmpint(actual_old_block_state,
6829
==, expected_old_block_state);
6831
/* Check member sa_flags */
6832
g_assert_true((actual_old_sigchld_action.sa_flags
6833
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6834
== (expected_sigchld_action.sa_flags
6835
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6837
/* Retrieve the current signal handler for SIGCHLD as set by
6838
setup_signal_handler() */
6839
struct sigaction actual_new_sigchld_action;
6840
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6841
&actual_new_sigchld_action), ==, 0);
6842
/* Check that the signal handler (member sa_handler) is correctly
6843
set to the "handle_sigchld" function */
6844
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6845
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6846
g_assert_true(actual_new_sigchld_action.sa_handler
6848
/* Check (in member sa_mask) that at least a handful of signals are
6849
actually blocked during the signal handler */
6850
for(int signum = 1; signum < NSIG; signum++){
6851
int actual_new_block_state;
6857
actual_new_block_state
6858
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6859
g_assert_cmpint(actual_new_block_state, ==, 1);
6861
case SIGKILL: /* non-blockable */
6862
case SIGSTOP: /* non-blockable */
6863
case SIGCHLD: /* always blocked */
6868
/* Check member sa_flags */
6869
g_assert_true((actual_new_sigchld_action.sa_flags
6870
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6871
== (SA_NOCLDSTOP | SA_RESTART));
6873
/* Restore signal handler */
6874
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6878
static void test_restore_signal_handler(__attribute__((unused))
6879
test_fixture *fixture,
6880
__attribute__((unused))
6881
gconstpointer user_data){
6882
/* Save current SIGCHLD action, whatever it is */
6883
struct sigaction expected_sigchld_action;
6884
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6886
/* Since we haven't established a signal handler yet, there should
6887
not be one established. But another test may have relied on
6888
restore_signal_handler() to restore the signal handler, and if
6889
restore_signal_handler() is buggy (which we should be prepared
6890
for in this test) the signal handler may not have been restored
6891
properly; check for this: */
6892
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
6894
/* Establish a signal handler */
6895
struct sigaction sigchld_action = {
6896
.sa_handler=handle_sigchld,
6897
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
6899
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
6900
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
6902
/* Act; i.e. run the restore_signal_handler() function */
6903
g_assert_true(restore_signal_handler(&expected_sigchld_action));
6905
/* Retrieve the restored signal handler data */
6906
struct sigaction actual_restored_sigchld_action;
6907
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6908
&actual_restored_sigchld_action), ==, 0);
6910
/* Check that the function correctly restored the signal action, as
6911
saved in "actual_restored_sigchld_action", to the same values as
6912
the previously saved "expected_sigchld_action" */
6913
/* Check member sa_handler */
6914
g_assert_true(actual_restored_sigchld_action.sa_handler
6915
== expected_sigchld_action.sa_handler);
6916
/* Check member sa_mask */
6917
for(int signum = 1; signum < NSIG; signum++){
6918
const int expected_old_block_state
6919
= sigismember(&expected_sigchld_action.sa_mask, signum);
6920
g_assert_cmpint(expected_old_block_state, >=, 0);
6921
const int actual_restored_block_state
6922
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
6923
g_assert_cmpint(actual_restored_block_state, >=, 0);
6924
g_assert_cmpint(actual_restored_block_state,
6925
==, expected_old_block_state);
6927
/* Check member sa_flags */
6928
g_assert_true((actual_restored_sigchld_action.sa_flags
6929
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6930
== (expected_sigchld_action.sa_flags
6931
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6934
static void test_block_sigchld(__attribute__((unused))
6935
test_fixture *fixture,
6936
__attribute__((unused))
6937
gconstpointer user_data){
6938
/* Save original signal mask */
6939
sigset_t expected_sigmask;
6940
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
6943
/* Make sure SIGCHLD is unblocked for this test */
6944
sigset_t sigchld_sigmask;
6945
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6946
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6947
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
6950
/* Act; i.e. run the block_sigchld() function */
6951
sigset_t actual_old_sigmask;
6952
g_assert_true(block_sigchld(&actual_old_sigmask));
6954
/* Check the actual_old_sigmask; it should be the same as the
6955
previously saved signal mask "expected_sigmask". */
6956
for(int signum = 1; signum < NSIG; signum++){
6957
const int expected_old_block_state
6958
= sigismember(&expected_sigmask, signum);
6959
g_assert_cmpint(expected_old_block_state, >=, 0);
6960
const int actual_old_block_state
6961
= sigismember(&actual_old_sigmask, signum);
6962
g_assert_cmpint(actual_old_block_state, >=, 0);
6963
g_assert_cmpint(actual_old_block_state,
6964
==, expected_old_block_state);
6967
/* Retrieve the newly set signal mask */
6968
sigset_t actual_sigmask;
6969
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
6971
/* SIGCHLD should be blocked */
6972
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
6974
/* Restore signal mask */
6975
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
6979
static void test_restore_sigmask(__attribute__((unused))
6980
test_fixture *fixture,
6981
__attribute__((unused))
6982
gconstpointer user_data){
6983
/* Save original signal mask */
6984
sigset_t orig_sigmask;
6985
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
6987
/* Make sure SIGCHLD is blocked for this test */
6988
sigset_t sigchld_sigmask;
6989
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
6990
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
6991
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
6994
/* Act; i.e. run the restore_sigmask() function */
6995
g_assert_true(restore_sigmask(&orig_sigmask));
6997
/* Retrieve the newly restored signal mask */
6998
sigset_t restored_sigmask;
6999
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7002
/* Check the restored_sigmask; it should be the same as the
7003
previously saved signal mask "orig_sigmask". */
7004
for(int signum = 1; signum < NSIG; signum++){
7005
const int orig_block_state = sigismember(&orig_sigmask, signum);
7006
g_assert_cmpint(orig_block_state, >=, 0);
7007
const int restored_block_state = sigismember(&restored_sigmask,
7009
g_assert_cmpint(restored_block_state, >=, 0);
7010
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7013
/* Restore signal mask */
7014
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7018
static void test_parse_arguments_noargs(__attribute__((unused))
7019
test_fixture *fixture,
7020
__attribute__((unused))
7021
gconstpointer user_data){
7025
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7027
char *agent_directory = NULL;
7028
char *helper_directory = NULL;
7031
char *mandos_argz = NULL;
7032
size_t mandos_argz_length = 0;
7034
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7035
&helper_directory, &user, &group,
7036
&mandos_argz, &mandos_argz_length));
7037
g_assert_null(agent_directory);
7038
g_assert_null(helper_directory);
7039
g_assert_true(user == 0);
7040
g_assert_true(group == 0);
7041
g_assert_null(mandos_argz);
7042
g_assert_true(mandos_argz_length == 0);
7044
for(char **arg = argv; *arg != NULL; arg++){
7049
__attribute__((nonnull))
7050
static bool parse_arguments_devnull(int argc, char *argv[],
7051
const bool exit_failure,
7052
char **agent_directory,
7053
char **helper_directory,
7057
size_t *mandos_argz_length){
7059
FILE *real_stderr = stderr;
7060
FILE *devnull = fopen("/dev/null", "we");
7061
g_assert_nonnull(devnull);
7064
const bool ret = parse_arguments(argc, argv, exit_failure,
7066
helper_directory, user, group,
7067
mandos_argz, mandos_argz_length);
7068
const error_t saved_errno = errno;
7070
stderr = real_stderr;
7071
g_assert_cmpint(fclose(devnull), ==, 0);
7073
errno = saved_errno;
7078
static void test_parse_arguments_invalid(__attribute__((unused))
7079
test_fixture *fixture,
7080
__attribute__((unused))
7081
gconstpointer user_data){
7084
strdup("--invalid"),
7086
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7088
char *agent_directory = NULL;
7089
char *helper_directory = NULL;
7092
char *mandos_argz = NULL;
7093
size_t mandos_argz_length = 0;
7095
g_assert_false(parse_arguments_devnull(argc, argv, false,
7097
&helper_directory, &user,
7098
&group, &mandos_argz,
7099
&mandos_argz_length));
7101
g_assert_true(errno == EINVAL);
7102
g_assert_null(agent_directory);
7103
g_assert_null(helper_directory);
7104
g_assert_null(mandos_argz);
7105
g_assert_true(mandos_argz_length == 0);
7107
for(char **arg = argv; *arg != NULL; arg++){
7112
static void test_parse_arguments_long_dir(__attribute__((unused))
7113
test_fixture *fixture,
7114
__attribute__((unused))
7115
gconstpointer user_data){
7118
strdup("--agent-directory"),
7121
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7123
__attribute__((cleanup(cleanup_string)))
7124
char *agent_directory = NULL;
7125
char *helper_directory = NULL;
7128
__attribute__((cleanup(cleanup_string)))
7129
char *mandos_argz = NULL;
7130
size_t mandos_argz_length = 0;
7132
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7133
&helper_directory, &user, &group,
7134
&mandos_argz, &mandos_argz_length));
7136
g_assert_cmpstr(agent_directory, ==, "/tmp");
7137
g_assert_null(helper_directory);
7138
g_assert_true(user == 0);
7139
g_assert_true(group == 0);
7140
g_assert_null(mandos_argz);
7141
g_assert_true(mandos_argz_length == 0);
7143
for(char **arg = argv; *arg != NULL; arg++){
7148
static void test_parse_arguments_short_dir(__attribute__((unused))
7149
test_fixture *fixture,
7150
__attribute__((unused))
7151
gconstpointer user_data){
7157
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7159
__attribute__((cleanup(cleanup_string)))
7160
char *agent_directory = NULL;
7161
char *helper_directory = NULL;
7164
__attribute__((cleanup(cleanup_string)))
7165
char *mandos_argz = NULL;
7166
size_t mandos_argz_length = 0;
7168
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7169
&helper_directory, &user, &group,
7170
&mandos_argz, &mandos_argz_length));
7172
g_assert_cmpstr(agent_directory, ==, "/tmp");
7173
g_assert_null(helper_directory);
7174
g_assert_true(user == 0);
7175
g_assert_true(group == 0);
7176
g_assert_null(mandos_argz);
7177
g_assert_true(mandos_argz_length == 0);
7179
for(char **arg = argv; *arg != NULL; arg++){
7185
void test_parse_arguments_helper_directory(__attribute__((unused))
7186
test_fixture *fixture,
7187
__attribute__((unused))
7188
gconstpointer user_data){
7191
strdup("--helper-directory"),
7194
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7196
char *agent_directory = NULL;
7197
__attribute__((cleanup(cleanup_string)))
7198
char *helper_directory = NULL;
7201
__attribute__((cleanup(cleanup_string)))
7202
char *mandos_argz = NULL;
7203
size_t mandos_argz_length = 0;
7205
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7206
&helper_directory, &user, &group,
7207
&mandos_argz, &mandos_argz_length));
7209
g_assert_cmpstr(helper_directory, ==, "/tmp");
7210
g_assert_null(agent_directory);
7211
g_assert_true(user == 0);
7212
g_assert_true(group == 0);
7213
g_assert_null(mandos_argz);
7214
g_assert_true(mandos_argz_length == 0);
7216
for(char **arg = argv; *arg != NULL; arg++){
7222
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7223
test_fixture *fixture,
7224
__attribute__((unused))
7225
gconstpointer user_data){
7228
strdup("--plugin-helper-dir"),
7231
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7233
char *agent_directory = NULL;
7234
__attribute__((cleanup(cleanup_string)))
7235
char *helper_directory = NULL;
7238
__attribute__((cleanup(cleanup_string)))
7239
char *mandos_argz = NULL;
7240
size_t mandos_argz_length = 0;
7242
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7243
&helper_directory, &user, &group,
7244
&mandos_argz, &mandos_argz_length));
7246
g_assert_cmpstr(helper_directory, ==, "/tmp");
7247
g_assert_null(agent_directory);
7248
g_assert_true(user == 0);
7249
g_assert_true(group == 0);
7250
g_assert_null(mandos_argz);
7251
g_assert_true(mandos_argz_length == 0);
7253
for(char **arg = argv; *arg != NULL; arg++){
7258
static void test_parse_arguments_user(__attribute__((unused))
7259
test_fixture *fixture,
7260
__attribute__((unused))
7261
gconstpointer user_data){
7267
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7269
char *agent_directory = NULL;
7270
__attribute__((cleanup(cleanup_string)))
7271
char *helper_directory = NULL;
7274
__attribute__((cleanup(cleanup_string)))
7275
char *mandos_argz = NULL;
7276
size_t mandos_argz_length = 0;
7278
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7279
&helper_directory, &user, &group,
7280
&mandos_argz, &mandos_argz_length));
7282
g_assert_null(helper_directory);
7283
g_assert_null(agent_directory);
7284
g_assert_cmpuint((unsigned int)user, ==, 1000);
7285
g_assert_true(group == 0);
7286
g_assert_null(mandos_argz);
7287
g_assert_true(mandos_argz_length == 0);
7289
for(char **arg = argv; *arg != NULL; arg++){
7294
static void test_parse_arguments_user_invalid(__attribute__((unused))
7295
test_fixture *fixture,
7296
__attribute__((unused))
7304
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7306
char *agent_directory = NULL;
7307
__attribute__((cleanup(cleanup_string)))
7308
char *helper_directory = NULL;
7311
__attribute__((cleanup(cleanup_string)))
7312
char *mandos_argz = NULL;
7313
size_t mandos_argz_length = 0;
7315
g_assert_false(parse_arguments_devnull(argc, argv, false,
7317
&helper_directory, &user,
7318
&group, &mandos_argz,
7319
&mandos_argz_length));
7321
g_assert_null(helper_directory);
7322
g_assert_null(agent_directory);
7323
g_assert_cmpuint((unsigned int)user, ==, 0);
7324
g_assert_true(group == 0);
7325
g_assert_null(mandos_argz);
7326
g_assert_true(mandos_argz_length == 0);
7328
for(char **arg = argv; *arg != NULL; arg++){
7334
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7335
test_fixture *fixture,
7336
__attribute__((unused))
7337
gconstpointer user_data){
7343
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7345
char *agent_directory = NULL;
7346
__attribute__((cleanup(cleanup_string)))
7347
char *helper_directory = NULL;
7350
__attribute__((cleanup(cleanup_string)))
7351
char *mandos_argz = NULL;
7352
size_t mandos_argz_length = 0;
7354
g_assert_false(parse_arguments_devnull(argc, argv, false,
7356
&helper_directory, &user,
7357
&group, &mandos_argz,
7358
&mandos_argz_length));
7360
g_assert_null(helper_directory);
7361
g_assert_null(agent_directory);
7362
g_assert_cmpuint((unsigned int)user, ==, 0);
7363
g_assert_true(group == 0);
7364
g_assert_null(mandos_argz);
7365
g_assert_true(mandos_argz_length == 0);
7367
for(char **arg = argv; *arg != NULL; arg++){
7372
static void test_parse_arguments_group(__attribute__((unused))
7373
test_fixture *fixture,
7374
__attribute__((unused))
7375
gconstpointer user_data){
7381
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7383
char *agent_directory = NULL;
7384
__attribute__((cleanup(cleanup_string)))
7385
char *helper_directory = NULL;
7388
__attribute__((cleanup(cleanup_string)))
7389
char *mandos_argz = NULL;
7390
size_t mandos_argz_length = 0;
7392
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7393
&helper_directory, &user, &group,
7394
&mandos_argz, &mandos_argz_length));
7396
g_assert_null(helper_directory);
7397
g_assert_null(agent_directory);
7398
g_assert_true(user == 0);
7399
g_assert_cmpuint((unsigned int)group, ==, 1000);
7400
g_assert_null(mandos_argz);
7401
g_assert_true(mandos_argz_length == 0);
7403
for(char **arg = argv; *arg != NULL; arg++){
7408
static void test_parse_arguments_group_invalid(__attribute__((unused))
7409
test_fixture *fixture,
7410
__attribute__((unused))
7418
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7420
char *agent_directory = NULL;
7421
__attribute__((cleanup(cleanup_string)))
7422
char *helper_directory = NULL;
7425
__attribute__((cleanup(cleanup_string)))
7426
char *mandos_argz = NULL;
7427
size_t mandos_argz_length = 0;
7429
g_assert_false(parse_arguments_devnull(argc, argv, false,
7431
&helper_directory, &user,
7432
&group, &mandos_argz,
7433
&mandos_argz_length));
7435
g_assert_null(helper_directory);
7436
g_assert_null(agent_directory);
7437
g_assert_true(user == 0);
7438
g_assert_true(group == 0);
7439
g_assert_null(mandos_argz);
7440
g_assert_true(mandos_argz_length == 0);
7442
for(char **arg = argv; *arg != NULL; arg++){
7448
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7449
test_fixture *fixture,
7450
__attribute__((unused))
7451
gconstpointer user_data){
7457
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7459
char *agent_directory = NULL;
7460
__attribute__((cleanup(cleanup_string)))
7461
char *helper_directory = NULL;
7464
__attribute__((cleanup(cleanup_string)))
7465
char *mandos_argz = NULL;
7466
size_t mandos_argz_length = 0;
7468
g_assert_false(parse_arguments_devnull(argc, argv, false,
7470
&helper_directory, &user,
7471
&group, &mandos_argz,
7472
&mandos_argz_length));
7474
g_assert_null(helper_directory);
7475
g_assert_null(agent_directory);
7476
g_assert_cmpuint((unsigned int)group, ==, 0);
7477
g_assert_true(group == 0);
7478
g_assert_null(mandos_argz);
7479
g_assert_true(mandos_argz_length == 0);
7481
for(char **arg = argv; *arg != NULL; arg++){
7486
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7487
test_fixture *fixture,
7488
__attribute__((unused))
7493
strdup("mandos-client"),
7495
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7497
__attribute__((cleanup(cleanup_string)))
7498
char *agent_directory = NULL;
7499
__attribute__((cleanup(cleanup_string)))
7500
char *helper_directory = NULL;
7503
__attribute__((cleanup(cleanup_string)))
7504
char *mandos_argz = NULL;
7505
size_t mandos_argz_length = 0;
7507
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7508
&helper_directory, &user, &group,
7509
&mandos_argz, &mandos_argz_length));
7511
g_assert_null(agent_directory);
7512
g_assert_null(helper_directory);
7513
g_assert_true(user == 0);
7514
g_assert_true(group == 0);
7515
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7516
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7517
mandos_argz_length),
7520
for(char **arg = argv; *arg != NULL; arg++){
7525
static void test_parse_arguments_mandos_args(__attribute__((unused))
7526
test_fixture *fixture,
7527
__attribute__((unused))
7528
gconstpointer user_data){
7531
strdup("mandos-client"),
7536
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7538
__attribute__((cleanup(cleanup_string)))
7539
char *agent_directory = NULL;
7540
__attribute__((cleanup(cleanup_string)))
7541
char *helper_directory = NULL;
7544
__attribute__((cleanup(cleanup_string)))
7545
char *mandos_argz = NULL;
7546
size_t mandos_argz_length = 0;
7548
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7549
&helper_directory, &user, &group,
7550
&mandos_argz, &mandos_argz_length));
7552
g_assert_null(agent_directory);
7553
g_assert_null(helper_directory);
7554
g_assert_true(user == 0);
7555
g_assert_true(group == 0);
7556
char *marg = mandos_argz;
7557
g_assert_cmpstr(marg, ==, "mandos-client");
7558
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7559
g_assert_cmpstr(marg, ==, "one");
7560
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7561
g_assert_cmpstr(marg, ==, "two");
7562
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7563
g_assert_cmpstr(marg, ==, "three");
7564
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7565
mandos_argz_length),
7568
for(char **arg = argv; *arg != NULL; arg++){
7573
static void test_parse_arguments_all_args(__attribute__((unused))
7574
test_fixture *fixture,
7575
__attribute__((unused))
7576
gconstpointer user_data){
7579
strdup("--agent-directory"),
7581
strdup("--helper-directory"),
7587
strdup("mandos-client"),
7592
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7594
__attribute__((cleanup(cleanup_string)))
7595
char *agent_directory = NULL;
7596
__attribute__((cleanup(cleanup_string)))
7597
char *helper_directory = NULL;
7600
__attribute__((cleanup(cleanup_string)))
7601
char *mandos_argz = NULL;
7602
size_t mandos_argz_length = 0;
7604
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7605
&helper_directory, &user, &group,
7606
&mandos_argz, &mandos_argz_length));
7608
g_assert_cmpstr(agent_directory, ==, "/tmp");
7609
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7610
g_assert_true(user == 1);
7611
g_assert_true(group == 2);
7612
char *marg = mandos_argz;
7613
g_assert_cmpstr(marg, ==, "mandos-client");
7614
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7615
g_assert_cmpstr(marg, ==, "one");
7616
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7617
g_assert_cmpstr(marg, ==, "two");
7618
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7619
g_assert_cmpstr(marg, ==, "three");
7620
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7621
mandos_argz_length),
7624
for(char **arg = argv; *arg != NULL; arg++){
7629
static void test_parse_arguments_mixed(__attribute__((unused))
7630
test_fixture *fixture,
7631
__attribute__((unused))
7632
gconstpointer user_data){
7635
strdup("mandos-client"),
7639
strdup("--agent-directory"),
7643
strdup("--helper-directory=/var/tmp"),
7645
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7647
__attribute__((cleanup(cleanup_string)))
7648
char *agent_directory = NULL;
7649
__attribute__((cleanup(cleanup_string)))
7650
char *helper_directory = NULL;
7653
__attribute__((cleanup(cleanup_string)))
7654
char *mandos_argz = NULL;
7655
size_t mandos_argz_length = 0;
7657
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7658
&helper_directory, &user, &group,
7659
&mandos_argz, &mandos_argz_length));
7661
g_assert_cmpstr(agent_directory, ==, "/tmp");
7662
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7663
g_assert_true(user == 1);
7664
g_assert_true(group == 0);
7665
char *marg = mandos_argz;
7666
g_assert_cmpstr(marg, ==, "mandos-client");
7667
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7668
g_assert_cmpstr(marg, ==, "one");
7669
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7670
g_assert_cmpstr(marg, ==, "two");
7671
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7672
g_assert_cmpstr(marg, ==, "three");
7673
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7674
mandos_argz_length),
7677
for(char **arg = argv; *arg != NULL; arg++){
7682
/* End of tests section */
7684
/* Test boilerplate section; New tests should be added to the test
7685
suite definition here, in the "run_tests" function.
7687
Finally, this section also contains the should_only_run_tests()
7688
function used by main() for deciding if tests should be run or to
7691
__attribute__((cold))
7692
static bool run_tests(int argc, char *argv[]){
7693
g_test_init(&argc, &argv, NULL);
7695
/* A macro to add a test with no setup or teardown functions */
7696
#define test_add(testpath, testfunc) \
7698
g_test_add((testpath), test_fixture, NULL, NULL, \
7699
(testfunc), NULL); \
7702
/* Test the signal-related functions first, since some other tests
7703
depend on these functions in their setups and teardowns */
7704
test_add("/signal-handling/setup", test_setup_signal_handler);
7705
test_add("/signal-handling/restore", test_restore_signal_handler);
7706
test_add("/signal-handling/block", test_block_sigchld);
7707
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7709
/* Regular non-signal-related tests; these use no setups or
7711
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7712
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7713
test_add("/parse_arguments/long-dir",
7714
test_parse_arguments_long_dir);
7715
test_add("/parse_arguments/short-dir",
7716
test_parse_arguments_short_dir);
7717
test_add("/parse_arguments/helper-directory",
7718
test_parse_arguments_helper_directory);
7719
test_add("/parse_arguments/plugin-helper-dir",
7720
test_parse_arguments_plugin_helper_dir);
7721
test_add("/parse_arguments/user", test_parse_arguments_user);
7722
test_add("/parse_arguments/user-invalid",
7723
test_parse_arguments_user_invalid);
7724
test_add("/parse_arguments/user-zero-invalid",
7725
test_parse_arguments_user_zero_invalid);
7726
test_add("/parse_arguments/group", test_parse_arguments_group);
7727
test_add("/parse_arguments/group-invalid",
7728
test_parse_arguments_group_invalid);
7729
test_add("/parse_arguments/group-zero-invalid",
7730
test_parse_arguments_group_zero_invalid);
7731
test_add("/parse_arguments/mandos-noargs",
7732
test_parse_arguments_mandos_noargs);
7733
test_add("/parse_arguments/mandos-args",
7734
test_parse_arguments_mandos_args);
7735
test_add("/parse_arguments/all-args",
7736
test_parse_arguments_all_args);
7737
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7738
test_add("/queue/create", test_create_queue);
7739
test_add("/queue/add", test_add_to_queue);
7740
test_add("/queue/has_question/empty",
7741
test_queue_has_question_empty);
7742
test_add("/queue/has_question/false",
7743
test_queue_has_question_false);
7744
test_add("/queue/has_question/true", test_queue_has_question_true);
7745
test_add("/queue/has_question/false2",
7746
test_queue_has_question_false2);
7747
test_add("/queue/has_question/true2",
7748
test_queue_has_question_true2);
7749
test_add("/buffer/cleanup", test_cleanup_buffer);
7750
test_add("/string_set/net-set-contains-nothing",
7751
test_string_set_new_set_contains_nothing);
7752
test_add("/string_set/with-added-string-contains-it",
7753
test_string_set_with_added_string_contains_it);
7754
test_add("/string_set/cleared-does-not-contain-string",
7755
test_string_set_cleared_does_not_contain_str);
7756
test_add("/string_set/swap/one-with-empty",
7757
test_string_set_swap_one_with_empty);
7758
test_add("/string_set/swap/empty-with-one",
7759
test_string_set_swap_empty_with_one);
7760
test_add("/string_set/swap/one-with-one",
7761
test_string_set_swap_one_with_one);
7763
/* A macro to add a test using the setup and teardown functions */
7764
#define test_add_st(path, func) \
7766
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7770
/* Signal-related tests; these use setups and teardowns which
7771
establish, during each test run, a signal handler for, and a
7772
signal mask blocking, the SIGCHLD signal, just like main() */
7773
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7774
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7775
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7776
test_add_st("/run_queue/zeroes-next-run",
7777
test_run_queue_zeroes_next_run);
7778
test_add_st("/run_queue/clears-cancelled_filenames",
7779
test_run_queue_clears_cancelled_filenames);
7780
test_add_st("/run_queue/skips-cancelled-filenames",
7781
test_run_queue_skips_cancelled_filenames);
7782
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7783
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7784
test_add_st("/run_queue/two-tasks/quit",
7785
test_run_queue_two_tasks_quit);
7786
test_add_st("/run_queue/two-tasks-cleanup",
7787
test_run_queue_two_tasks_cleanup);
7788
test_add_st("/task-creators/start_mandos_client",
7789
test_start_mandos_client);
7790
test_add_st("/task-creators/start_mandos_client/execv",
7791
test_start_mandos_client_execv);
7792
test_add_st("/task-creators/start_mandos_client/suid/euid",
7793
test_start_mandos_client_suid_euid);
7794
test_add_st("/task-creators/start_mandos_client/suid/egid",
7795
test_start_mandos_client_suid_egid);
7796
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7797
test_start_mandos_client_suid_ruid);
7798
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7799
test_start_mandos_client_suid_rgid);
7800
test_add_st("/task-creators/start_mandos_client/read",
7801
test_start_mandos_client_read);
7802
test_add_st("/task-creators/start_mandos_client/helper-directory",
7803
test_start_mandos_client_helper_directory);
7804
test_add_st("/task-creators/start_mandos_client/sigmask",
7805
test_start_mandos_client_sigmask);
7806
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7807
test_wait_for_mandos_client_exit_badpid);
7808
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7809
test_wait_for_mandos_client_exit_noexit);
7810
test_add_st("/task/wait_for_mandos_client_exit/success",
7811
test_wait_for_mandos_client_exit_success);
7812
test_add_st("/task/wait_for_mandos_client_exit/failure",
7813
test_wait_for_mandos_client_exit_failure);
7814
test_add_st("/task/wait_for_mandos_client_exit/killed",
7815
test_wait_for_mandos_client_exit_killed);
7816
test_add_st("/task/read_mandos_client_output/readerror",
7817
test_read_mandos_client_output_readerror);
7818
test_add_st("/task/read_mandos_client_output/nodata",
7819
test_read_mandos_client_output_nodata);
7820
test_add_st("/task/read_mandos_client_output/eof",
7821
test_read_mandos_client_output_eof);
7822
test_add_st("/task/read_mandos_client_output/once",
7823
test_read_mandos_client_output_once);
7824
test_add_st("/task/read_mandos_client_output/malloc",
7825
test_read_mandos_client_output_malloc);
7826
test_add_st("/task/read_mandos_client_output/append",
7827
test_read_mandos_client_output_append);
7828
test_add_st("/task-creators/add_inotify_dir_watch",
7829
test_add_inotify_dir_watch);
7830
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7831
test_add_inotify_dir_watch_fail);
7832
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7833
test_add_inotify_dir_watch_EAGAIN);
7834
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7835
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7836
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7837
test_add_inotify_dir_watch_IN_MOVED_TO);
7838
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7839
test_add_inotify_dir_watch_IN_MOVED_FROM);
7840
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7841
test_add_inotify_dir_watch_IN_DELETE);
7842
test_add_st("/task/read_inotify_event/readerror",
7843
test_read_inotify_event_readerror);
7844
test_add_st("/task/read_inotify_event/bad-epoll",
7845
test_read_inotify_event_bad_epoll);
7846
test_add_st("/task/read_inotify_event/nodata",
7847
test_read_inotify_event_nodata);
7848
test_add_st("/task/read_inotify_event/eof",
7849
test_read_inotify_event_eof);
7850
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7851
test_read_inotify_event_IN_CLOSE_WRITE);
7852
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7853
test_read_inotify_event_IN_MOVED_TO);
7854
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7855
test_read_inotify_event_IN_MOVED_FROM);
7856
test_add_st("/task/read_inotify_event/IN_DELETE",
7857
test_read_inotify_event_IN_DELETE);
7858
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7859
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7860
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7861
test_read_inotify_event_IN_MOVED_TO_badname);
7862
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7863
test_read_inotify_event_IN_MOVED_FROM_badname);
7864
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7865
test_read_inotify_event_IN_DELETE_badname);
7866
test_add_st("/task/open_and_parse_question/ENOENT",
7867
test_open_and_parse_question_ENOENT);
7868
test_add_st("/task/open_and_parse_question/EIO",
7869
test_open_and_parse_question_EIO);
7870
test_add_st("/task/open_and_parse_question/parse-error",
7871
test_open_and_parse_question_parse_error);
7872
test_add_st("/task/open_and_parse_question/nosocket",
7873
test_open_and_parse_question_nosocket);
7874
test_add_st("/task/open_and_parse_question/badsocket",
7875
test_open_and_parse_question_badsocket);
7876
test_add_st("/task/open_and_parse_question/nopid",
7877
test_open_and_parse_question_nopid);
7878
test_add_st("/task/open_and_parse_question/badpid",
7879
test_open_and_parse_question_badpid);
7880
test_add_st("/task/open_and_parse_question/noexist_pid",
7881
test_open_and_parse_question_noexist_pid);
7882
test_add_st("/task/open_and_parse_question/no-notafter",
7883
test_open_and_parse_question_no_notafter);
7884
test_add_st("/task/open_and_parse_question/bad-notafter",
7885
test_open_and_parse_question_bad_notafter);
7886
test_add_st("/task/open_and_parse_question/notafter-0",
7887
test_open_and_parse_question_notafter_0);
7888
test_add_st("/task/open_and_parse_question/notafter-1",
7889
test_open_and_parse_question_notafter_1);
7890
test_add_st("/task/open_and_parse_question/notafter-1-1",
7891
test_open_and_parse_question_notafter_1_1);
7892
test_add_st("/task/open_and_parse_question/notafter-1-2",
7893
test_open_and_parse_question_notafter_1_2);
7894
test_add_st("/task/open_and_parse_question/equal-notafter",
7895
test_open_and_parse_question_equal_notafter);
7896
test_add_st("/task/open_and_parse_question/late-notafter",
7897
test_open_and_parse_question_late_notafter);
7898
test_add_st("/task/cancel_old_question/0-1-2",
7899
test_cancel_old_question_0_1_2);
7900
test_add_st("/task/cancel_old_question/0-2-1",
7901
test_cancel_old_question_0_2_1);
7902
test_add_st("/task/cancel_old_question/1-2-3",
7903
test_cancel_old_question_1_2_3);
7904
test_add_st("/task/cancel_old_question/1-3-2",
7905
test_cancel_old_question_1_3_2);
7906
test_add_st("/task/cancel_old_question/2-1-3",
7907
test_cancel_old_question_2_1_3);
7908
test_add_st("/task/cancel_old_question/2-3-1",
7909
test_cancel_old_question_2_3_1);
7910
test_add_st("/task/cancel_old_question/3-1-2",
7911
test_cancel_old_question_3_1_2);
7912
test_add_st("/task/cancel_old_question/3-2-1",
7913
test_cancel_old_question_3_2_1);
7914
test_add_st("/task/connect_question_socket/name-too-long",
7915
test_connect_question_socket_name_too_long);
7916
test_add_st("/task/connect_question_socket/connect-fail",
7917
test_connect_question_socket_connect_fail);
7918
test_add_st("/task/connect_question_socket/bad-epoll",
7919
test_connect_question_socket_bad_epoll);
7920
test_add_st("/task/connect_question_socket/usable",
7921
test_connect_question_socket_usable);
7922
test_add_st("/task/send_password_to_socket/client-not-exited",
7923
test_send_password_to_socket_client_not_exited);
7924
test_add_st("/task/send_password_to_socket/password-not-read",
7925
test_send_password_to_socket_password_not_read);
7926
test_add_st("/task/send_password_to_socket/EMSGSIZE",
7927
test_send_password_to_socket_EMSGSIZE);
7928
test_add_st("/task/send_password_to_socket/retry",
7929
test_send_password_to_socket_retry);
7930
test_add_st("/task/send_password_to_socket/bad-epoll",
7931
test_send_password_to_socket_bad_epoll);
7932
test_add_st("/task/send_password_to_socket/null-password",
7933
test_send_password_to_socket_null_password);
7934
test_add_st("/task/send_password_to_socket/empty-password",
7935
test_send_password_to_socket_empty_password);
7936
test_add_st("/task/send_password_to_socket/empty-str-password",
7937
test_send_password_to_socket_empty_str_pass);
7938
test_add_st("/task/send_password_to_socket/text-password",
7939
test_send_password_to_socket_text_password);
7940
test_add_st("/task/send_password_to_socket/binary-password",
7941
test_send_password_to_socket_binary_password);
7942
test_add_st("/task/send_password_to_socket/nuls-in-password",
7943
test_send_password_to_socket_nuls_in_password);
7944
test_add_st("/task-creators/add_existing_questions/ENOENT",
7945
test_add_existing_questions_ENOENT);
7946
test_add_st("/task-creators/add_existing_questions/no-questions",
7947
test_add_existing_questions_no_questions);
7948
test_add_st("/task-creators/add_existing_questions/one-question",
7949
test_add_existing_questions_one_question);
7950
test_add_st("/task-creators/add_existing_questions/two-questions",
7951
test_add_existing_questions_two_questions);
7952
test_add_st("/task-creators/add_existing_questions/non-questions",
7953
test_add_existing_questions_non_questions);
7954
test_add_st("/task-creators/add_existing_questions/both-types",
7955
test_add_existing_questions_both_types);
7957
return g_test_run() == 0;
7960
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
7961
GOptionContext *context = g_option_context_new("");
7963
g_option_context_set_help_enabled(context, FALSE);
7964
g_option_context_set_ignore_unknown_options(context, TRUE);
7966
gboolean run_tests = FALSE;
7967
GOptionEntry entries[] = {
7968
{ "test", 0, 0, G_OPTION_ARG_NONE,
7969
&run_tests, "Run tests", NULL },
7972
g_option_context_add_main_entries(context, entries, NULL);
7974
GError *error = NULL;
7976
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
7977
g_option_context_free(context);
7978
g_error("Failed to parse options: %s", error->message);
7981
g_option_context_free(context);
7982
return run_tests != FALSE;