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-2020 Teddy Hogeborn
6
* Copyright © 2019-2020 Björn Påhlsson
8
* This file is part of Mandos.
10
* Mandos is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* Mandos is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
* Contact the authors at <mandos@recompile.se>.
27
#include <inttypes.h> /* uintmax_t, PRIuMAX, PRIdMAX,
28
intmax_t, uint32_t, SCNx32,
30
#include <stddef.h> /* size_t */
31
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
33
#include <stdbool.h> /* bool, true, false */
34
#include <signal.h> /* struct sigaction, sigset_t,
35
sigemptyset(), sigaddset(),
36
SIGCHLD, pthread_sigmask(),
37
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
SA_NOCLDSTOP, sigfillset(), kill(),
39
SIGTERM, sigdelset(), SIGKILL,
40
NSIG, sigismember(), SA_ONSTACK,
41
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
44
malloc(), free(), strtoumax(),
45
realloc(), setenv(), calloc(),
46
mkdtemp(), mkostemp() */
47
#include <iso646.h> /* not, or, and, xor */
48
#include <error.h> /* error() */
49
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
#include <errno.h> /* errno, error_t, EACCES,
51
ENAMETOOLONG, ENOENT, ENOTDIR,
52
ENOMEM, EEXIST, ECHILD, EPERM,
53
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <stdint.h> /* SIZE_MAX */
77
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
78
fork(), _exit(), dup2(),
79
STDOUT_FILENO, setresgid(),
80
setresuid(), execv(), ssize_t,
81
read(), dup3(), getuid(), dup(),
82
STDERR_FILENO, pause(), write(),
83
rmdir(), unlink(), getpid() */
84
#include <sys/mman.h> /* munlock(), mlock() */
85
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
86
F_GETFD, F_GETFL, FD_CLOEXEC,
87
open(), O_WRONLY, O_NOCTTY,
88
O_RDONLY, O_NOFOLLOW */
89
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
91
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
92
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
93
IN_CLOEXEC, inotify_add_watch(),
94
IN_CLOSE_WRITE, IN_MOVED_TO,
95
IN_MOVED_FROM, IN_DELETE,
96
IN_EXCL_UNLINK, IN_ONLYDIR,
97
struct inotify_event */
98
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
99
#include <stdio.h> /* asprintf(), FILE, stderr, fopen(),
100
fclose(), getline(), sscanf(),
101
feof(), ferror(), rename(),
102
fdopen(), fprintf(), fscanf() */
103
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
GError, g_key_file_load_from_file(),
105
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
106
g_key_file_get_string(), guint64,
107
g_key_file_get_uint64(),
108
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
109
g_assert_true(), g_assert_nonnull(),
110
g_assert_null(), g_assert_false(),
111
g_assert_cmpint(), g_assert_cmpuint(),
112
g_test_skip(), g_assert_cmpstr(),
113
g_test_init(), g_test_add(), g_test_run(),
114
GOptionContext, g_option_context_new(),
115
g_option_context_set_help_enabled(), FALSE,
116
g_option_context_set_ignore_unknown_options(),
117
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
118
g_option_context_add_main_entries(),
119
g_option_context_parse(),
120
g_option_context_free(), g_error() */
121
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
122
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
123
SOCK_DGRAM, SOCK_NONBLOCK,
124
SOCK_CLOEXEC, connect(),
125
struct sockaddr, socklen_t,
126
shutdown(), SHUT_RD, send(),
127
MSG_NOSIGNAL, bind(), recv(),
129
#include <glob.h> /* globfree(), glob_t, glob(),
130
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
131
GLOB_ABORTED, GLOB_NOMATCH,
134
/* End of includes */
136
/* Start of declarations of private types and functions */
138
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
139
typedef uintmax_t mono_microsecs;
141
/* "task_queue" - A queue of tasks to be run */
143
struct task_struct *tasks; /* Tasks in this queue */
144
size_t length; /* Number of tasks */
145
/* Memory allocated for "tasks", in bytes */
147
/* Time when this queue should be run, at the latest */
148
mono_microsecs next_run;
149
} __attribute__((designated_init)) task_queue;
151
/* "task_func" - A function type for task functions
153
I.e. functions for the code which runs when a task is run, all have
155
typedef void (task_func) (const struct task_struct,
157
__attribute__((nonnull));
159
/* "buffer" - A data buffer for a growing array of bytes
161
Used for the "password" variable */
166
} __attribute__((designated_init)) buffer;
168
/* "string_set" - A set type which can contain strings
170
Used by the "cancelled_filenames" variable */
172
char *argz; /* Do not access these except in */
173
size_t argz_len; /* the string_set_* functions */
174
} __attribute__((designated_init)) string_set;
176
/* "task_context" - local variables for tasks
178
This data structure distinguishes between different tasks which are
179
using the same function. This data structure is passed to every
180
task function when each task is run.
182
Note that not every task uses every struct member. */
183
typedef struct task_struct {
184
task_func *const func; /* The function run by this task */
185
char *const question_filename; /* The question file */
186
const pid_t pid; /* Mandos client process ID */
187
const int epoll_fd; /* The epoll set file descriptor */
188
bool *const quit_now; /* Set to true on fatal errors */
189
const int fd; /* General purpose file descriptor */
190
bool *const mandos_client_exited; /* Set true when client exits */
191
buffer *const password; /* As read from client process */
192
bool *const password_is_read; /* "password" is done growing */
193
char *filename; /* General purpose file name */
194
/* A set of strings of all the file names of questions which have
195
been cancelled for any reason; tasks pertaining to these question
196
files should not be run */
197
string_set *const cancelled_filenames;
198
const mono_microsecs notafter; /* "NotAfter" from question file */
199
/* Updated before each queue run; is compared with queue.next_run */
200
const mono_microsecs *const current_time;
201
} __attribute__((designated_init)) task_context;
203
/* Declare all our functions here so we can define them in any order
204
below. Note: test functions are *not* declared here, they are
205
declared in the test section. */
206
__attribute__((warn_unused_result))
207
static bool should_only_run_tests(int *, char **[]);
208
__attribute__((warn_unused_result, cold))
209
static bool run_tests(int, char *[]);
210
static void handle_sigchld(__attribute__((unused)) int sig){}
211
__attribute__((warn_unused_result, malloc))
212
task_queue *create_queue(void);
213
__attribute__((nonnull, warn_unused_result))
214
bool add_to_queue(task_queue *const, const task_context);
215
__attribute__((nonnull))
216
void cleanup_task(const task_context *const);
217
__attribute__((nonnull))
218
void cleanup_queue(task_queue *const *const);
219
__attribute__((pure, nonnull, warn_unused_result))
220
bool queue_has_question(const task_queue *const);
221
__attribute__((nonnull))
222
void cleanup_close(const int *const);
223
__attribute__((nonnull))
224
void cleanup_string(char *const *const);
225
__attribute__((nonnull))
226
void cleanup_buffer(buffer *const);
227
__attribute__((pure, nonnull, warn_unused_result))
228
bool string_set_contains(const string_set, const char *const);
229
__attribute__((nonnull, warn_unused_result))
230
bool string_set_add(string_set *const, const char *const);
231
__attribute__((nonnull))
232
void string_set_clear(string_set *);
233
void string_set_swap(string_set *const, string_set *const);
234
__attribute__((nonnull, warn_unused_result))
235
bool start_mandos_client(task_queue *const, const int, bool *const,
236
bool *const, buffer *const, bool *const,
237
const struct sigaction *const,
238
const sigset_t, const char *const,
239
const uid_t, const gid_t,
240
const char *const *const);
241
__attribute__((nonnull))
242
task_func wait_for_mandos_client_exit;
243
__attribute__((nonnull))
244
task_func read_mandos_client_output;
245
__attribute__((warn_unused_result))
246
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
247
buffer *const, const char *const,
248
string_set *, const mono_microsecs *const,
249
bool *const, bool *const);
250
__attribute__((nonnull))
251
task_func read_inotify_event;
252
__attribute__((nonnull))
253
task_func open_and_parse_question;
254
__attribute__((nonnull))
255
task_func cancel_old_question;
256
__attribute__((nonnull))
257
task_func connect_question_socket;
258
__attribute__((nonnull))
259
task_func send_password_to_socket;
260
__attribute__((warn_unused_result))
261
bool add_existing_questions(task_queue *const, const int,
262
buffer *const, string_set *,
263
const mono_microsecs *const,
264
bool *const, bool *const,
266
__attribute__((nonnull, warn_unused_result))
267
bool wait_for_event(const int, const mono_microsecs,
268
const mono_microsecs);
269
bool run_queue(task_queue **const, string_set *const, bool *const);
270
bool clear_all_fds_from_epoll_set(const int);
271
mono_microsecs get_current_time(void);
272
__attribute__((nonnull, warn_unused_result))
273
bool setup_signal_handler(struct sigaction *const);
274
__attribute__((nonnull))
275
bool restore_signal_handler(const struct sigaction *const);
276
__attribute__((nonnull, warn_unused_result))
277
bool block_sigchld(sigset_t *const);
278
__attribute__((nonnull))
279
bool restore_sigmask(const sigset_t *const);
280
__attribute__((nonnull))
281
bool parse_arguments(int, char *[], const bool, char **, char **,
282
uid_t *const , gid_t *const, char **, size_t *);
284
/* End of declarations of private types and functions */
286
/* Start of "main" section; this section LACKS TESTS!
288
Code here should be as simple as possible. */
290
/* These are required to be global by Argp */
291
const char *argp_program_version = "password-agent " VERSION;
292
const char *argp_program_bug_address = "<mandos@recompile.se>";
294
int main(int argc, char *argv[]){
296
/* If the --test option is passed, skip all normal operations and
297
instead only run the run_tests() function, which also does all
298
its own option parsing, so we don't have to do anything here. */
299
if(should_only_run_tests(&argc, &argv)){
300
if(run_tests(argc, argv)){
301
return EXIT_SUCCESS; /* All tests successful */
303
return EXIT_FAILURE; /* Some test(s) failed */
306
__attribute__((cleanup(cleanup_string)))
307
char *agent_directory = NULL;
309
__attribute__((cleanup(cleanup_string)))
310
char *helper_directory = NULL;
315
__attribute__((cleanup(cleanup_string)))
316
char *mandos_argz = NULL;
317
size_t mandos_argz_length = 0;
319
if(not parse_arguments(argc, argv, true, &agent_directory,
320
&helper_directory, &user, &group,
321
&mandos_argz, &mandos_argz_length)){
322
/* This should never happen, since "true" is passed as the third
323
argument to parse_arguments() above, which should make
324
argp_parse() call exit() if any parsing error occurs. */
325
error(EX_USAGE, errno, "Failed to parse arguments");
328
const char default_agent_directory[] = "/run/systemd/ask-password";
329
const char default_helper_directory[]
330
= "/lib/mandos/plugin-helpers";
331
const char *const default_argv[]
332
= {"/lib/mandos/plugins.d/mandos-client", NULL };
334
/* Set variables to default values if unset */
335
if(agent_directory == NULL){
336
agent_directory = strdup(default_agent_directory);
337
if(agent_directory == NULL){
338
error(EX_OSERR, errno, "Failed strdup()");
341
if(helper_directory == NULL){
342
helper_directory = strdup(default_helper_directory);
343
if(helper_directory == NULL){
344
error(EX_OSERR, errno, "Failed strdup()");
348
user = 65534; /* nobody */
351
group = 65534; /* nogroup */
353
/* If parse_opt did not create an argz vector, create one with
355
if(mandos_argz == NULL){
357
#pragma GCC diagnostic push
358
/* argz_create() takes a non-const argv for some unknown reason -
359
argz_create() isn't modifying the strings, just copying them.
360
Therefore, this cast to non-const should be safe. */
361
#pragma GCC diagnostic ignored "-Wcast-qual"
363
errno = argz_create((char *const *)default_argv, &mandos_argz,
364
&mandos_argz_length);
366
#pragma GCC diagnostic pop
369
error(EX_OSERR, errno, "Failed argz_create()");
372
/* Use argz vector to create a normal argv, usable by execv() */
374
char **mandos_argv = malloc((argz_count(mandos_argz,
376
+ 1) * sizeof(char *));
377
if(mandos_argv == NULL){
378
error_t saved_errno = errno;
380
error(EX_OSERR, saved_errno, "Failed malloc()");
382
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
384
sigset_t orig_sigmask;
385
if(not block_sigchld(&orig_sigmask)){
389
struct sigaction old_sigchld_action;
390
if(not setup_signal_handler(&old_sigchld_action)){
394
mono_microsecs current_time = 0;
396
bool mandos_client_exited = false;
397
bool quit_now = false;
398
__attribute__((cleanup(cleanup_close)))
399
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
401
error(EX_OSERR, errno, "Failed to create epoll set fd");
403
__attribute__((cleanup(cleanup_queue)))
404
task_queue *queue = create_queue();
406
error(EX_OSERR, errno, "Failed to create task queue");
409
__attribute__((cleanup(cleanup_buffer)))
410
buffer password = {};
411
bool password_is_read = false;
413
__attribute__((cleanup(string_set_clear)))
414
string_set cancelled_filenames = {};
416
/* Add tasks to queue */
417
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
418
&quit_now, &password, &password_is_read,
419
&old_sigchld_action, orig_sigmask,
420
helper_directory, user, group,
421
(const char *const *)mandos_argv)){
422
return EX_OSERR; /* Error has already been printed */
424
/* These variables were only for start_mandos_client() and are not
429
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
430
agent_directory, &cancelled_filenames,
431
¤t_time, &mandos_client_exited,
433
switch(errno){ /* Error has already been printed */
443
if(not add_existing_questions(queue, epoll_fd, &password,
444
&cancelled_filenames, ¤t_time,
445
&mandos_client_exited,
446
&password_is_read, agent_directory)){
447
return EXIT_FAILURE; /* Error has already been printed */
452
current_time = get_current_time();
453
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
454
const error_t saved_errno = errno;
455
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
459
current_time = get_current_time();
460
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
461
const error_t saved_errno = errno;
462
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
465
/* When no tasks about questions are left in the queue, break out
466
of the loop (and implicitly exit the program) */
467
} while(queue_has_question(queue));
469
restore_signal_handler(&old_sigchld_action);
470
restore_sigmask(&orig_sigmask);
475
__attribute__((warn_unused_result))
476
mono_microsecs get_current_time(void){
477
struct timespec currtime;
478
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
479
error(0, errno, "Failed to get current time");
482
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
483
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
486
/* End of "main" section */
488
/* Start of regular code section; ALL this code has tests */
490
__attribute__((nonnull))
491
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
492
char **agent_directory, char **helper_directory,
493
uid_t *const user, gid_t *const group,
494
char **mandos_argz, size_t *mandos_argz_length){
496
const struct argp_option options[] = {
497
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
498
.doc="Systemd password agent directory" },
499
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
500
.doc="Mandos Client password helper directory" },
501
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
502
.flags=OPTION_HIDDEN | OPTION_ALIAS },
503
{ .name="user", .key='u', .arg="USERID",
504
.doc="User ID the Mandos Client will use as its unprivileged"
506
{ .name="userid", .key=130, /* From plugin--runner */
507
.flags=OPTION_HIDDEN | OPTION_ALIAS },
508
{ .name="group", .key='g', .arg="GROUPID",
509
.doc="Group ID the Mandos Client will use as its unprivileged"
511
{ .name="groupid", .key=131, /* From plugin--runner */
512
.flags=OPTION_HIDDEN | OPTION_ALIAS },
513
{ .name="test", .key=255, /* See should_only_run_tests() */
514
.doc="Skip normal operation, and only run self-tests. See"
515
" --test --help.", .group=10, },
519
__attribute__((nonnull(3)))
520
error_t parse_opt(int key, char *arg, struct argp_state *state){
523
case 'd': /* --agent-directory */
524
*agent_directory = strdup(arg);
526
case 128: /* --helper-directory */
527
case 129: /* --plugin-helper-dir */
528
*helper_directory = strdup(arg);
530
case 'u': /* --user */
531
case 130: /* --userid */
534
uintmax_t tmp_id = 0;
536
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
537
if(errno != 0 or tmp == arg or *tmp != '\0'
538
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
539
return ARGP_ERR_UNKNOWN;
541
*user = (uid_t)tmp_id;
545
case 'g': /* --group */
546
case 131: /* --groupid */
549
uintmax_t tmp_id = 0;
551
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
552
if(errno != 0 or tmp == arg or *tmp != '\0'
553
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
554
return ARGP_ERR_UNKNOWN;
556
*group = (gid_t)tmp_id;
561
/* Copy arguments into argz vector */
562
return argz_create(state->argv + state->next, mandos_argz,
565
return ARGP_ERR_UNKNOWN;
570
const struct argp argp = {
573
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
574
.doc = "Mandos password agent -- runs Mandos client as a"
575
" systemd password agent",
578
errno = argp_parse(&argp, argc, argv,
579
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
584
__attribute__((nonnull, warn_unused_result))
585
bool block_sigchld(sigset_t *const orig_sigmask){
586
sigset_t sigchld_sigmask;
587
if(sigemptyset(&sigchld_sigmask) < 0){
588
error(0, errno, "Failed to empty signal set");
591
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
592
error(0, errno, "Failed to add SIGCHLD to signal set");
595
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
596
error(0, errno, "Failed to block SIGCHLD signal");
602
__attribute__((nonnull, warn_unused_result, const))
603
bool restore_sigmask(const sigset_t *const orig_sigmask){
604
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
605
error(0, errno, "Failed to restore blocked signals");
611
__attribute__((nonnull, warn_unused_result))
612
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
613
struct sigaction sigchld_action = {
614
.sa_handler=handle_sigchld,
615
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
617
/* Set all signals in "sa_mask" struct member; this makes all
618
signals automatically blocked during signal handler */
619
if(sigfillset(&sigchld_action.sa_mask) != 0){
620
error(0, errno, "Failed to do sigfillset()");
623
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
624
error(0, errno, "Failed to set SIGCHLD signal handler");
630
__attribute__((nonnull, warn_unused_result))
631
bool restore_signal_handler(const struct sigaction *const
633
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
634
error(0, errno, "Failed to restore signal handler");
640
__attribute__((warn_unused_result, malloc))
641
task_queue *create_queue(void){
642
task_queue *queue = malloc(sizeof(task_queue));
646
queue->allocated = 0;
652
__attribute__((nonnull, warn_unused_result))
653
bool add_to_queue(task_queue *const queue, const task_context task){
654
if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
656
error(0, ENOMEM, "Failed to allocate %" PRIuMAX
657
" tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
662
if(needed_size > (queue->allocated)){
663
task_context *const new_tasks = realloc(queue->tasks,
665
if(new_tasks == NULL){
666
error(0, errno, "Failed to allocate %" PRIuMAX
667
" bytes for queue->tasks", (uintmax_t)needed_size);
670
queue->tasks = new_tasks;
671
queue->allocated = needed_size;
673
/* Using memcpy here is necessary because doing */
674
/* queue->tasks[queue->length++] = task; */
675
/* would violate const-ness of task members */
676
memcpy(&(queue->tasks[queue->length++]), &task,
677
sizeof(task_context));
681
__attribute__((nonnull))
682
void cleanup_task(const task_context *const task){
683
const error_t saved_errno = errno;
684
/* free and close all task data */
685
free(task->question_filename);
686
if(task->filename != task->question_filename){
687
free(task->filename);
690
kill(task->pid, SIGTERM);
698
__attribute__((nonnull))
699
void free_queue(task_queue *const queue){
704
__attribute__((nonnull))
705
void cleanup_queue(task_queue *const *const queue){
709
for(size_t i = 0; i < (*queue)->length; i++){
710
const task_context *const task = ((*queue)->tasks)+i;
716
__attribute__((pure, nonnull, warn_unused_result))
717
bool queue_has_question(const task_queue *const queue){
718
for(size_t i=0; i < queue->length; i++){
719
if(queue->tasks[i].question_filename != NULL){
726
__attribute__((nonnull))
727
void cleanup_close(const int *const fd){
728
const error_t saved_errno = errno;
733
__attribute__((nonnull))
734
void cleanup_string(char *const *const ptr){
738
__attribute__((nonnull))
739
void cleanup_buffer(buffer *buf){
740
if(buf->allocated > 0){
741
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
742
explicit_bzero(buf->data, buf->allocated);
744
memset(buf->data, '\0', buf->allocated);
747
if(buf->data != NULL){
748
if(munlock(buf->data, buf->allocated) != 0){
749
error(0, errno, "Failed to unlock memory of old buffer");
758
__attribute__((pure, nonnull, warn_unused_result))
759
bool string_set_contains(const string_set set, const char *const str){
760
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
761
s = argz_next(set.argz, set.argz_len, s)){
762
if(strcmp(s, str) == 0){
769
__attribute__((nonnull, warn_unused_result))
770
bool string_set_add(string_set *const set, const char *const str){
771
if(string_set_contains(*set, str)){
774
error_t error = argz_add(&set->argz, &set->argz_len, str);
782
__attribute__((nonnull))
783
void string_set_clear(string_set *set){
789
__attribute__((nonnull))
790
void string_set_swap(string_set *const set1, string_set *const set2){
791
/* Swap contents of two string sets */
793
char *const tmp_argz = set1->argz;
794
set1->argz = set2->argz;
795
set2->argz = tmp_argz;
798
const size_t tmp_argz_len = set1->argz_len;
799
set1->argz_len = set2->argz_len;
800
set2->argz_len = tmp_argz_len;
804
__attribute__((nonnull, warn_unused_result))
805
bool start_mandos_client(task_queue *const queue,
807
bool *const mandos_client_exited,
808
bool *const quit_now, buffer *const password,
809
bool *const password_is_read,
810
const struct sigaction *const
811
old_sigchld_action, const sigset_t sigmask,
812
const char *const helper_directory,
813
const uid_t user, const gid_t group,
814
const char *const *const argv){
816
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
817
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
821
const pid_t pid = fork();
823
if(not restore_signal_handler(old_sigchld_action)){
826
if(not restore_sigmask(&sigmask)){
829
if(close(pipefds[0]) != 0){
830
error(0, errno, "Failed to close() parent pipe fd");
833
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
834
error(0, errno, "Failed to dup2() pipe fd to stdout");
837
if(close(pipefds[1]) != 0){
838
error(0, errno, "Failed to close() old child pipe fd");
841
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
842
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
843
" \"%s\", 1)", helper_directory);
846
if(group != 0 and setresgid(group, 0, 0) == -1){
847
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
848
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
851
if(user != 0 and setresuid(user, 0, 0) == -1){
852
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
853
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
857
#pragma GCC diagnostic push
858
/* For historical reasons, the "argv" argument to execv() is not
859
const, but it is safe to override this. */
860
#pragma GCC diagnostic ignored "-Wcast-qual"
862
execv(argv[0], (char **)argv);
864
#pragma GCC diagnostic pop
866
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
872
error(0, errno, "Failed to fork()");
877
if(not add_to_queue(queue, (task_context){
878
.func=wait_for_mandos_client_exit,
880
.mandos_client_exited=mandos_client_exited,
883
error(0, errno, "Failed to add wait_for_mandos_client to queue");
888
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
889
&(struct epoll_event)
890
{ .events=EPOLLIN | EPOLLRDHUP });
891
if(ret != 0 and errno != EEXIST){
892
error(0, errno, "Failed to add file descriptor to epoll set");
897
return add_to_queue(queue, (task_context){
898
.func=read_mandos_client_output,
903
.password_is_read=password_is_read,
907
__attribute__((nonnull))
908
void wait_for_mandos_client_exit(const task_context task,
909
task_queue *const queue){
910
const pid_t pid = task.pid;
911
bool *const mandos_client_exited = task.mandos_client_exited;
912
bool *const quit_now = task.quit_now;
915
switch(waitpid(pid, &status, WNOHANG)){
916
case 0: /* Not exited yet */
917
if(not add_to_queue(queue, task)){
918
error(0, errno, "Failed to add myself to queue");
923
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
929
default: /* Has exited */
930
*mandos_client_exited = true;
931
if((not WIFEXITED(status))
932
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
933
error(0, 0, "Mandos client failed or was killed");
939
__attribute__((nonnull))
940
void read_mandos_client_output(const task_context task,
941
task_queue *const queue){
942
buffer *const password = task.password;
943
bool *const quit_now = task.quit_now;
944
bool *const password_is_read = task.password_is_read;
945
const int fd = task.fd;
946
const int epoll_fd = task.epoll_fd;
948
const size_t new_potential_size = (password->length + PIPE_BUF);
949
if(password->allocated < new_potential_size){
950
char *const new_buffer = calloc(new_potential_size, 1);
951
if(new_buffer == NULL){
952
error(0, errno, "Failed to allocate %" PRIuMAX
953
" bytes for password", (uintmax_t)new_potential_size);
958
if(mlock(new_buffer, new_potential_size) != 0){
959
/* Warn but do not treat as fatal error */
960
if(errno != EPERM and errno != ENOMEM){
961
error(0, errno, "Failed to lock memory for password");
964
if(password->length > 0){
965
memcpy(new_buffer, password->data, password->length);
966
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
967
explicit_bzero(password->data, password->allocated);
969
memset(password->data, '\0', password->allocated);
972
if(password->data != NULL){
973
if(munlock(password->data, password->allocated) != 0){
974
error(0, errno, "Failed to unlock memory of old buffer");
976
free(password->data);
978
password->data = new_buffer;
979
password->allocated = new_potential_size;
982
const ssize_t read_length = read(fd, password->data
983
+ password->length, PIPE_BUF);
985
if(read_length == 0){ /* EOF */
986
*password_is_read = true;
990
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
991
error(0, errno, "Failed to read password from Mandos client");
996
if(read_length > 0){ /* Data has been read */
997
password->length += (size_t)read_length;
1000
/* Either data was read, or EAGAIN was indicated, meaning no data
1003
/* Re-add the fd to the epoll set */
1004
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1005
&(struct epoll_event)
1006
{ .events=EPOLLIN | EPOLLRDHUP });
1007
if(ret != 0 and errno != EEXIST){
1008
error(0, errno, "Failed to re-add file descriptor to epoll set");
1014
/* Re-add myself to the queue */
1015
if(not add_to_queue(queue, task)){
1016
error(0, errno, "Failed to add myself to queue");
1022
__attribute__((nonnull, warn_unused_result))
1023
bool add_inotify_dir_watch(task_queue *const queue,
1024
const int epoll_fd, bool *const quit_now,
1025
buffer *const password,
1026
const char *const dir,
1027
string_set *cancelled_filenames,
1028
const mono_microsecs *const current_time,
1029
bool *const mandos_client_exited,
1030
bool *const password_is_read){
1031
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1033
error(0, errno, "Failed to create inotify instance");
1037
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1038
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1041
error(0, errno, "Failed to create inotify watch on %s", dir);
1045
/* Add the inotify fd to the epoll set */
1046
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1047
&(struct epoll_event)
1048
{ .events=EPOLLIN | EPOLLRDHUP });
1049
if(ret != 0 and errno != EEXIST){
1050
error(0, errno, "Failed to add file descriptor to epoll set");
1055
const task_context read_inotify_event_task = {
1056
.func=read_inotify_event,
1061
.filename=strdup(dir),
1062
.cancelled_filenames=cancelled_filenames,
1063
.current_time=current_time,
1064
.mandos_client_exited=mandos_client_exited,
1065
.password_is_read=password_is_read,
1067
if(read_inotify_event_task.filename == NULL){
1068
error(0, errno, "Failed to strdup(\"%s\")", dir);
1073
return add_to_queue(queue, read_inotify_event_task);
1076
__attribute__((nonnull))
1077
void read_inotify_event(const task_context task,
1078
task_queue *const queue){
1079
const int fd = task.fd;
1080
const int epoll_fd = task.epoll_fd;
1081
char *const filename = task.filename;
1082
bool *quit_now = task.quit_now;
1083
buffer *const password = task.password;
1084
string_set *const cancelled_filenames = task.cancelled_filenames;
1085
const mono_microsecs *const current_time = task.current_time;
1086
bool *const mandos_client_exited = task.mandos_client_exited;
1087
bool *const password_is_read = task.password_is_read;
1089
/* "sufficient to read at least one event." - inotify(7) */
1090
const size_t ievent_size = (sizeof(struct inotify_event)
1093
struct inotify_event event;
1094
char name_buffer[NAME_MAX + 1];
1096
struct inotify_event *const ievent = &ievent_buffer.event;
1098
const ssize_t read_length = read(fd, ievent, ievent_size);
1099
if(read_length == 0){ /* EOF */
1100
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1102
cleanup_task(&task);
1105
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1106
error(0, errno, "Failed to read from inotify fd for directory %s",
1109
cleanup_task(&task);
1112
if(read_length > 0 /* Data has been read */
1113
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1114
char *question_filename = NULL;
1115
const ssize_t question_filename_length
1116
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1117
if(question_filename_length < 0){
1118
error(0, errno, "Failed to create file name from directory name"
1119
" %s and file name %s", filename, ievent->name);
1121
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1122
if(not add_to_queue(queue, (task_context){
1123
.func=open_and_parse_question,
1125
.question_filename=question_filename,
1126
.filename=question_filename,
1128
.cancelled_filenames=cancelled_filenames,
1129
.current_time=current_time,
1130
.mandos_client_exited=mandos_client_exited,
1131
.password_is_read=password_is_read,
1133
error(0, errno, "Failed to add open_and_parse_question task"
1134
" for file name %s to queue", filename);
1136
/* Force the added task (open_and_parse_question) to run
1138
queue->next_run = 1;
1140
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1141
if(not string_set_add(cancelled_filenames,
1142
question_filename)){
1143
error(0, errno, "Could not add question %s to"
1144
" cancelled_questions", question_filename);
1146
free(question_filename);
1147
cleanup_task(&task);
1150
free(question_filename);
1155
/* Either data was read, or EAGAIN was indicated, meaning no data
1158
/* Re-add myself to the queue */
1159
if(not add_to_queue(queue, task)){
1160
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1161
" queue", filename);
1163
cleanup_task(&task);
1167
/* Re-add the fd to the epoll set */
1168
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1169
&(struct epoll_event)
1170
{ .events=EPOLLIN | EPOLLRDHUP });
1171
if(ret != 0 and errno != EEXIST){
1172
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1173
" directory %s to epoll set", fd, filename);
1174
/* Force the added task (read_inotify_event) to run again, at most
1175
one second from now */
1176
if((queue->next_run == 0)
1177
or (queue->next_run > (*current_time + 1000000))){
1178
queue->next_run = *current_time + 1000000;
1183
__attribute__((nonnull))
1184
void open_and_parse_question(const task_context task,
1185
task_queue *const queue){
1186
__attribute__((cleanup(cleanup_string)))
1187
char *question_filename = task.question_filename;
1188
const int epoll_fd = task.epoll_fd;
1189
buffer *const password = task.password;
1190
string_set *const cancelled_filenames = task.cancelled_filenames;
1191
const mono_microsecs *const current_time = task.current_time;
1192
bool *const mandos_client_exited = task.mandos_client_exited;
1193
bool *const password_is_read = task.password_is_read;
1195
/* We use the GLib "Key-value file parser" functions to parse the
1196
question file. See <https://www.freedesktop.org/wiki/Software
1197
/systemd/PasswordAgents/> for specification of contents */
1198
__attribute__((nonnull))
1199
void cleanup_g_key_file(GKeyFile **key_file){
1200
if(*key_file != NULL){
1201
g_key_file_free(*key_file);
1205
__attribute__((cleanup(cleanup_g_key_file)))
1206
GKeyFile *key_file = g_key_file_new();
1207
if(key_file == NULL){
1208
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1212
GError *glib_error = NULL;
1213
if(g_key_file_load_from_file(key_file, question_filename,
1214
G_KEY_FILE_NONE, &glib_error) != TRUE){
1215
/* If a file was removed, we should ignore it, so */
1216
/* only show error message if file actually existed */
1217
if(glib_error->code != G_FILE_ERROR_NOENT){
1218
error(0, 0, "Failed to load question data from file \"%s\": %s",
1219
question_filename, glib_error->message);
1224
__attribute__((cleanup(cleanup_string)))
1225
char *socket_name = g_key_file_get_string(key_file, "Ask",
1228
if(socket_name == NULL){
1229
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1230
question_filename, glib_error->message);
1234
if(strlen(socket_name) == 0){
1235
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1240
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1242
if(glib_error != NULL){
1243
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1244
question_filename, glib_error->message);
1248
if((pid != (guint64)((pid_t)pid))
1249
or (kill((pid_t)pid, 0) != 0)){
1250
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1251
" does not exist", (uintmax_t)pid, question_filename);
1255
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1256
"NotAfter", &glib_error);
1257
if(glib_error != NULL){
1258
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1259
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1260
" %s", question_filename, glib_error->message);
1265
if(queue->next_run == 0 or (queue->next_run > notafter)){
1266
queue->next_run = notafter;
1268
if(*current_time >= notafter){
1273
const task_context connect_question_socket_task = {
1274
.func=connect_question_socket,
1275
.question_filename=strdup(question_filename),
1278
.filename=strdup(socket_name),
1279
.cancelled_filenames=task.cancelled_filenames,
1280
.mandos_client_exited=mandos_client_exited,
1281
.password_is_read=password_is_read,
1282
.current_time=current_time,
1284
if(connect_question_socket_task.question_filename == NULL
1285
or connect_question_socket_task.filename == NULL
1286
or not add_to_queue(queue, connect_question_socket_task)){
1287
error(0, errno, "Failed to add connect_question_socket for socket"
1288
" %s (from \"%s\") to queue", socket_name,
1290
cleanup_task(&connect_question_socket_task);
1293
/* Force the added task (connect_question_socket) to run
1295
queue->next_run = 1;
1298
char *const dup_filename = strdup(question_filename);
1299
const task_context cancel_old_question_task = {
1300
.func=cancel_old_question,
1301
.question_filename=dup_filename,
1303
.filename=dup_filename,
1304
.cancelled_filenames=cancelled_filenames,
1305
.current_time=current_time,
1307
if(cancel_old_question_task.question_filename == NULL
1308
or not add_to_queue(queue, cancel_old_question_task)){
1309
error(0, errno, "Failed to add cancel_old_question for file "
1310
"\"%s\" to queue", question_filename);
1311
cleanup_task(&cancel_old_question_task);
1317
__attribute__((nonnull))
1318
void cancel_old_question(const task_context task,
1319
task_queue *const queue){
1320
char *const question_filename = task.question_filename;
1321
string_set *const cancelled_filenames = task.cancelled_filenames;
1322
const mono_microsecs notafter = task.notafter;
1323
const mono_microsecs *const current_time = task.current_time;
1325
if(*current_time >= notafter){
1326
if(not string_set_add(cancelled_filenames, question_filename)){
1327
error(0, errno, "Failed to cancel question for file %s",
1330
cleanup_task(&task);
1334
if(not add_to_queue(queue, task)){
1335
error(0, errno, "Failed to add cancel_old_question for file "
1336
"%s to queue", question_filename);
1337
cleanup_task(&task);
1341
if((queue->next_run == 0) or (queue->next_run > notafter)){
1342
queue->next_run = notafter;
1346
__attribute__((nonnull))
1347
void connect_question_socket(const task_context task,
1348
task_queue *const queue){
1349
char *const question_filename = task.question_filename;
1350
char *const filename = task.filename;
1351
const int epoll_fd = task.epoll_fd;
1352
buffer *const password = task.password;
1353
string_set *const cancelled_filenames = task.cancelled_filenames;
1354
bool *const mandos_client_exited = task.mandos_client_exited;
1355
bool *const password_is_read = task.password_is_read;
1356
const mono_microsecs *const current_time = task.current_time;
1358
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1360
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1361
error(0, 0, "Socket filename is larger than"
1362
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1363
(uintmax_t)sizeof(sock_name.sun_path), filename);
1364
if(not string_set_add(cancelled_filenames, question_filename)){
1365
error(0, errno, "Failed to cancel question for file %s",
1368
cleanup_task(&task);
1372
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1373
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1376
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1377
if(not add_to_queue(queue, task)){
1378
error(0, errno, "Failed to add connect_question_socket for file"
1379
" \"%s\" and socket \"%s\" to queue", question_filename,
1381
cleanup_task(&task);
1383
/* Force the added task (connect_question_socket) to run
1385
queue->next_run = 1;
1390
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1391
if(connect(fd, (struct sockaddr *)&sock_name,
1392
(socklen_t)SUN_LEN(&sock_name)) != 0){
1393
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1394
if(not add_to_queue(queue, task)){
1395
error(0, errno, "Failed to add connect_question_socket for file"
1396
" \"%s\" and socket \"%s\" to queue", question_filename,
1398
cleanup_task(&task);
1400
/* Force the added task (connect_question_socket) to run again,
1401
at most one second from now */
1402
if((queue->next_run == 0)
1403
or (queue->next_run > (*current_time + 1000000))){
1404
queue->next_run = *current_time + 1000000;
1410
/* Not necessary, but we can try, and merely warn on failure */
1411
if(shutdown(fd, SHUT_RD) != 0){
1412
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1416
/* Add the fd to the epoll set */
1417
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1418
&(struct epoll_event){ .events=EPOLLOUT })
1420
error(0, errno, "Failed to add inotify file descriptor %d for"
1421
" socket %s to epoll set", fd, filename);
1422
if(not add_to_queue(queue, task)){
1423
error(0, errno, "Failed to add connect_question_socket for file"
1424
" \"%s\" and socket \"%s\" to queue", question_filename,
1426
cleanup_task(&task);
1428
/* Force the added task (connect_question_socket) to run again,
1429
at most one second from now */
1430
if((queue->next_run == 0)
1431
or (queue->next_run > (*current_time + 1000000))){
1432
queue->next_run = *current_time + 1000000;
1438
/* add task send_password_to_socket to queue */
1439
const task_context send_password_to_socket_task = {
1440
.func=send_password_to_socket,
1441
.question_filename=question_filename,
1446
.cancelled_filenames=cancelled_filenames,
1447
.mandos_client_exited=mandos_client_exited,
1448
.password_is_read=password_is_read,
1449
.current_time=current_time,
1452
if(not add_to_queue(queue, send_password_to_socket_task)){
1453
error(0, errno, "Failed to add send_password_to_socket for"
1454
" file \"%s\" and socket \"%s\" to queue",
1455
question_filename, filename);
1456
cleanup_task(&send_password_to_socket_task);
1460
__attribute__((nonnull))
1461
void send_password_to_socket(const task_context task,
1462
task_queue *const queue){
1463
char *const question_filename=task.question_filename;
1464
char *const filename=task.filename;
1465
const int epoll_fd=task.epoll_fd;
1466
const int fd=task.fd;
1467
buffer *const password=task.password;
1468
string_set *const cancelled_filenames=task.cancelled_filenames;
1469
bool *const mandos_client_exited = task.mandos_client_exited;
1470
bool *const password_is_read = task.password_is_read;
1471
const mono_microsecs *const current_time = task.current_time;
1473
if(*mandos_client_exited and *password_is_read){
1475
const size_t send_buffer_length = password->length + 2;
1476
char *send_buffer = malloc(send_buffer_length);
1477
if(send_buffer == NULL){
1478
error(0, errno, "Failed to allocate send_buffer");
1480
if(mlock(send_buffer, send_buffer_length) != 0){
1481
/* Warn but do not treat as fatal error */
1482
if(errno != EPERM and errno != ENOMEM){
1483
error(0, errno, "Failed to lock memory for password"
1487
/* “[…] send a single datagram to the socket consisting of the
1488
password string either prefixed with "+" or with "-"
1489
depending on whether the password entry was successful or
1490
not. You may but don't have to include a final NUL byte in
1493
— <https://www.freedesktop.org/wiki/Software/systemd/
1494
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1496
send_buffer[0] = '+'; /* Prefix with "+" */
1497
/* Always add an extra NUL */
1498
send_buffer[password->length + 1] = '\0';
1499
if(password->length > 0){
1500
memcpy(send_buffer + 1, password->data, password->length);
1503
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1505
const error_t saved_errno = errno;
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1507
explicit_bzero(send_buffer, send_buffer_length);
1509
memset(send_buffer, '\0', send_buffer_length);
1511
if(munlock(send_buffer, send_buffer_length) != 0){
1512
error(0, errno, "Failed to unlock memory of send buffer");
1515
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1516
switch(saved_errno){
1529
error(0, 0, "Password of size %" PRIuMAX " is too big",
1530
(uintmax_t)password->length);
1534
__attribute__((fallthrough));
1537
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1538
error(0, 0, "Password only partially sent to socket");
1543
__attribute__((fallthrough));
1546
error(0, saved_errno, "Failed to send() to socket %s",
1548
if(not string_set_add(cancelled_filenames,
1549
question_filename)){
1550
error(0, errno, "Failed to cancel question for file %s",
1553
cleanup_task(&task);
1558
cleanup_task(&task);
1564
/* We failed or are not ready yet; retry later */
1566
if(not add_to_queue(queue, task)){
1567
error(0, errno, "Failed to add send_password_to_socket for"
1568
" file %s and socket %s to queue", question_filename,
1570
cleanup_task(&task);
1573
/* Add the fd to the epoll set */
1574
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1575
&(struct epoll_event){ .events=EPOLLOUT })
1577
error(0, errno, "Failed to add socket file descriptor %d for"
1578
" socket %s to epoll set", fd, filename);
1579
/* Force the added task (send_password_to_socket) to run again, at
1580
most one second from now */
1581
if((queue->next_run == 0)
1582
or (queue->next_run > (*current_time + 1000000))){
1583
queue->next_run = *current_time + 1000000;
1588
__attribute__((warn_unused_result))
1589
bool add_existing_questions(task_queue *const queue,
1591
buffer *const password,
1592
string_set *cancelled_filenames,
1593
const mono_microsecs *const current_time,
1594
bool *const mandos_client_exited,
1595
bool *const password_is_read,
1596
const char *const dirname){
1597
__attribute__((cleanup(cleanup_string)))
1598
char *dir_pattern = NULL;
1599
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1600
if(ret < 0 or dir_pattern == NULL){
1601
error(0, errno, "Could not create glob pattern for directory %s",
1605
__attribute__((cleanup(globfree)))
1606
glob_t question_filenames = {};
1607
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1608
NULL, &question_filenames)){
1611
error(0, errno, "Failed to open directory %s", dirname);
1614
error(0, errno, "There are no question files in %s", dirname);
1617
error(0, errno, "Could not allocate memory for question file"
1618
" names in %s", dirname);
1622
__attribute__((fallthrough));
1625
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1626
char *const question_filename = strdup(question_filenames
1628
const task_context task = {
1629
.func=open_and_parse_question,
1631
.question_filename=question_filename,
1632
.filename=question_filename,
1634
.cancelled_filenames=cancelled_filenames,
1635
.current_time=current_time,
1636
.mandos_client_exited=mandos_client_exited,
1637
.password_is_read=password_is_read,
1640
if(question_filename == NULL
1641
or not add_to_queue(queue, task)){
1642
error(0, errno, "Failed to add open_and_parse_question for"
1643
" file %s to queue",
1644
question_filenames.gl_pathv[i]);
1645
free(question_filename);
1647
queue->next_run = 1;
1654
__attribute__((nonnull, warn_unused_result))
1655
bool wait_for_event(const int epoll_fd,
1656
const mono_microsecs queue_next_run,
1657
const mono_microsecs current_time){
1658
__attribute__((const))
1659
int milliseconds_to_wait(const mono_microsecs currtime,
1660
const mono_microsecs nextrun){
1661
if(currtime >= nextrun){
1664
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1665
if(wait_time_ms > (uintmax_t)INT_MAX){
1668
return (int)wait_time_ms;
1671
const int wait_time_ms = milliseconds_to_wait(current_time,
1674
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1675
sigset_t temporary_unblocked_sigmask;
1676
/* Get current signal mask */
1677
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1680
/* Remove SIGCHLD from the signal mask */
1681
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1684
struct epoll_event events[8]; /* Ignored */
1685
int ret = epoll_pwait(epoll_fd, events,
1686
sizeof(events) / sizeof(struct epoll_event),
1687
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1688
&temporary_unblocked_sigmask);
1689
if(ret < 0 and errno != EINTR){
1690
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1692
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1695
return clear_all_fds_from_epoll_set(epoll_fd);
1698
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1699
/* Create a new empty epoll set */
1700
__attribute__((cleanup(cleanup_close)))
1701
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1702
if(new_epoll_fd < 0){
1705
/* dup3() the new epoll set fd over the old one, replacing it */
1706
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1712
__attribute__((nonnull, warn_unused_result))
1713
bool run_queue(task_queue **const queue,
1714
string_set *const cancelled_filenames,
1715
bool *const quit_now){
1717
task_queue *new_queue = create_queue();
1718
if(new_queue == NULL){
1722
__attribute__((cleanup(string_set_clear)))
1723
string_set old_cancelled_filenames = {};
1724
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1726
/* Declare i outside the for loop, since we might need i after the
1727
loop in case we aborted in the middle */
1729
for(i=0; i < (*queue)->length and not *quit_now; i++){
1730
task_context *const task = &((*queue)->tasks[i]);
1731
const char *const question_filename = task->question_filename;
1732
/* Skip any task referencing a cancelled question filename */
1733
if(question_filename != NULL
1734
and string_set_contains(old_cancelled_filenames,
1735
question_filename)){
1739
task->func(*task, new_queue);
1743
/* we might be in the middle of the queue, so clean up any
1744
remaining tasks in the current queue */
1745
for(; i < (*queue)->length; i++){
1746
cleanup_task(&((*queue)->tasks[i]));
1760
/* End of regular code section */
1762
/* Start of tests section; here are the tests for the above code */
1764
/* This "fixture" data structure is used by the test setup and
1765
teardown functions */
1767
struct sigaction orig_sigaction;
1768
sigset_t orig_sigmask;
1771
static void test_setup(test_fixture *fixture,
1772
__attribute__((unused))
1773
gconstpointer user_data){
1774
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1775
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1778
static void test_teardown(test_fixture *fixture,
1779
__attribute__((unused))
1780
gconstpointer user_data){
1781
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1782
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1785
/* Utility function used by tests to search queue for matching task */
1786
__attribute__((pure, nonnull, warn_unused_result))
1787
static task_context *find_matching_task(const task_queue *const queue,
1788
const task_context task){
1789
/* The argument "task" structure is a pattern to match; 0 in any
1790
member means any value matches, otherwise the value must match.
1791
The filename strings are compared by strcmp(), not by pointer. */
1792
for(size_t i = 0; i < queue->length; i++){
1793
task_context *const current_task = queue->tasks+i;
1794
/* Check all members of task_context, if set to a non-zero value.
1795
If a member does not match, continue to next task in queue */
1797
/* task_func *const func */
1798
if(task.func != NULL and current_task->func != task.func){
1801
/* char *const question_filename; */
1802
if(task.question_filename != NULL
1803
and (current_task->question_filename == NULL
1804
or strcmp(current_task->question_filename,
1805
task.question_filename) != 0)){
1808
/* const pid_t pid; */
1809
if(task.pid != 0 and current_task->pid != task.pid){
1812
/* const int epoll_fd; */
1813
if(task.epoll_fd != 0
1814
and current_task->epoll_fd != task.epoll_fd){
1817
/* bool *const quit_now; */
1818
if(task.quit_now != NULL
1819
and current_task->quit_now != task.quit_now){
1823
if(task.fd != 0 and current_task->fd != task.fd){
1826
/* bool *const mandos_client_exited; */
1827
if(task.mandos_client_exited != NULL
1828
and current_task->mandos_client_exited
1829
!= task.mandos_client_exited){
1832
/* buffer *const password; */
1833
if(task.password != NULL
1834
and current_task->password != task.password){
1837
/* bool *const password_is_read; */
1838
if(task.password_is_read != NULL
1839
and current_task->password_is_read != task.password_is_read){
1842
/* char *filename; */
1843
if(task.filename != NULL
1844
and (current_task->filename == NULL
1845
or strcmp(current_task->filename, task.filename) != 0)){
1848
/* string_set *const cancelled_filenames; */
1849
if(task.cancelled_filenames != NULL
1850
and current_task->cancelled_filenames
1851
!= task.cancelled_filenames){
1854
/* const mono_microsecs notafter; */
1855
if(task.notafter != 0
1856
and current_task->notafter != task.notafter){
1859
/* const mono_microsecs *const current_time; */
1860
if(task.current_time != NULL
1861
and current_task->current_time != task.current_time){
1864
/* Current task matches all members; return it */
1865
return current_task;
1867
/* No task in queue matches passed pattern task */
1871
static void test_create_queue(__attribute__((unused))
1872
test_fixture *fixture,
1873
__attribute__((unused))
1874
gconstpointer user_data){
1875
__attribute__((cleanup(cleanup_queue)))
1876
task_queue *const queue = create_queue();
1877
g_assert_nonnull(queue);
1878
g_assert_null(queue->tasks);
1879
g_assert_true(queue->length == 0);
1880
g_assert_true(queue->next_run == 0);
1883
static task_func dummy_func;
1885
static void test_add_to_queue(__attribute__((unused))
1886
test_fixture *fixture,
1887
__attribute__((unused))
1888
gconstpointer user_data){
1889
__attribute__((cleanup(cleanup_queue)))
1890
task_queue *queue = create_queue();
1891
g_assert_nonnull(queue);
1893
g_assert_true(add_to_queue(queue,
1894
(task_context){ .func=dummy_func }));
1895
g_assert_true(queue->length == 1);
1896
g_assert_nonnull(queue->tasks);
1897
g_assert_true(queue->tasks[0].func == dummy_func);
1900
static void test_add_to_queue_overflow(__attribute__((unused))
1901
test_fixture *fixture,
1902
__attribute__((unused))
1903
gconstpointer user_data){
1904
__attribute__((cleanup(cleanup_queue)))
1905
task_queue *queue = create_queue();
1906
g_assert_nonnull(queue);
1907
g_assert_true(queue->length == 0);
1908
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1910
FILE *real_stderr = stderr;
1911
FILE *devnull = fopen("/dev/null", "we");
1912
g_assert_nonnull(devnull);
1914
const bool ret = add_to_queue(queue,
1915
(task_context){ .func=dummy_func });
1916
g_assert_true(errno == ENOMEM);
1917
g_assert_false(ret);
1918
stderr = real_stderr;
1919
g_assert_cmpint(fclose(devnull), ==, 0);
1920
queue->length = 0; /* Restore real size */
1923
static void dummy_func(__attribute__((unused))
1924
const task_context task,
1925
__attribute__((unused))
1926
task_queue *const queue){
1929
static void test_queue_has_question_empty(__attribute__((unused))
1930
test_fixture *fixture,
1931
__attribute__((unused))
1932
gconstpointer user_data){
1933
__attribute__((cleanup(cleanup_queue)))
1934
task_queue *queue = create_queue();
1935
g_assert_nonnull(queue);
1936
g_assert_false(queue_has_question(queue));
1939
static void test_queue_has_question_false(__attribute__((unused))
1940
test_fixture *fixture,
1941
__attribute__((unused))
1942
gconstpointer user_data){
1943
__attribute__((cleanup(cleanup_queue)))
1944
task_queue *queue = create_queue();
1945
g_assert_nonnull(queue);
1946
g_assert_true(add_to_queue(queue,
1947
(task_context){ .func=dummy_func }));
1948
g_assert_false(queue_has_question(queue));
1951
static void test_queue_has_question_true(__attribute__((unused))
1952
test_fixture *fixture,
1953
__attribute__((unused))
1954
gconstpointer user_data){
1955
__attribute__((cleanup(cleanup_queue)))
1956
task_queue *queue = create_queue();
1957
g_assert_nonnull(queue);
1958
char *const question_filename
1959
= strdup("/nonexistent/question_filename");
1960
g_assert_nonnull(question_filename);
1961
task_context task = {
1963
.question_filename=question_filename,
1965
g_assert_true(add_to_queue(queue, task));
1966
g_assert_true(queue_has_question(queue));
1969
static void test_queue_has_question_false2(__attribute__((unused))
1970
test_fixture *fixture,
1971
__attribute__((unused))
1972
gconstpointer user_data){
1973
__attribute__((cleanup(cleanup_queue)))
1974
task_queue *queue = create_queue();
1975
g_assert_nonnull(queue);
1976
task_context task = { .func=dummy_func };
1977
g_assert_true(add_to_queue(queue, task));
1978
g_assert_true(add_to_queue(queue, task));
1979
g_assert_cmpint((int)queue->length, ==, 2);
1980
g_assert_false(queue_has_question(queue));
1983
static void test_queue_has_question_true2(__attribute__((unused))
1984
test_fixture *fixture,
1985
__attribute__((unused))
1986
gconstpointer user_data){
1987
__attribute__((cleanup(cleanup_queue)))
1988
task_queue *queue = create_queue();
1989
g_assert_nonnull(queue);
1990
task_context task1 = { .func=dummy_func };
1991
g_assert_true(add_to_queue(queue, task1));
1992
char *const question_filename
1993
= strdup("/nonexistent/question_filename");
1994
g_assert_nonnull(question_filename);
1995
task_context task2 = {
1997
.question_filename=question_filename,
1999
g_assert_true(add_to_queue(queue, task2));
2000
g_assert_cmpint((int)queue->length, ==, 2);
2001
g_assert_true(queue_has_question(queue));
2004
static void test_cleanup_buffer(__attribute__((unused))
2005
test_fixture *fixture,
2006
__attribute__((unused))
2007
gconstpointer user_data){
2010
const size_t buffersize = 10;
2012
buf.data = malloc(buffersize);
2013
g_assert_nonnull(buf.data);
2014
if(mlock(buf.data, buffersize) != 0){
2015
g_assert_true(errno == EPERM or errno == ENOMEM);
2018
cleanup_buffer(&buf);
2019
g_assert_null(buf.data);
2023
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2024
test_fixture *fixture,
2025
__attribute__((unused))
2028
__attribute__((cleanup(string_set_clear)))
2029
string_set set = {};
2030
g_assert_false(string_set_contains(set, "")); /* Empty string */
2031
g_assert_false(string_set_contains(set, "test_string"));
2035
test_string_set_with_added_string_contains_it(__attribute__((unused))
2036
test_fixture *fixture,
2037
__attribute__((unused))
2040
__attribute__((cleanup(string_set_clear)))
2041
string_set set = {};
2042
g_assert_true(string_set_add(&set, "test_string"));
2043
g_assert_true(string_set_contains(set, "test_string"));
2047
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2048
test_fixture *fixture,
2049
__attribute__((unused))
2050
gconstpointer user_data){
2051
__attribute__((cleanup(string_set_clear)))
2052
string_set set = {};
2053
g_assert_true(string_set_add(&set, "test_string"));
2054
string_set_clear(&set);
2055
g_assert_false(string_set_contains(set, "test_string"));
2059
void test_string_set_swap_one_with_empty(__attribute__((unused))
2060
test_fixture *fixture,
2061
__attribute__((unused))
2062
gconstpointer user_data){
2063
__attribute__((cleanup(string_set_clear)))
2064
string_set set1 = {};
2065
__attribute__((cleanup(string_set_clear)))
2066
string_set set2 = {};
2067
g_assert_true(string_set_add(&set1, "test_string1"));
2068
string_set_swap(&set1, &set2);
2069
g_assert_false(string_set_contains(set1, "test_string1"));
2070
g_assert_true(string_set_contains(set2, "test_string1"));
2074
void test_string_set_swap_empty_with_one(__attribute__((unused))
2075
test_fixture *fixture,
2076
__attribute__((unused))
2077
gconstpointer user_data){
2078
__attribute__((cleanup(string_set_clear)))
2079
string_set set1 = {};
2080
__attribute__((cleanup(string_set_clear)))
2081
string_set set2 = {};
2082
g_assert_true(string_set_add(&set2, "test_string2"));
2083
string_set_swap(&set1, &set2);
2084
g_assert_true(string_set_contains(set1, "test_string2"));
2085
g_assert_false(string_set_contains(set2, "test_string2"));
2088
static void test_string_set_swap_one_with_one(__attribute__((unused))
2089
test_fixture *fixture,
2090
__attribute__((unused))
2093
__attribute__((cleanup(string_set_clear)))
2094
string_set set1 = {};
2095
__attribute__((cleanup(string_set_clear)))
2096
string_set set2 = {};
2097
g_assert_true(string_set_add(&set1, "test_string1"));
2098
g_assert_true(string_set_add(&set2, "test_string2"));
2099
string_set_swap(&set1, &set2);
2100
g_assert_false(string_set_contains(set1, "test_string1"));
2101
g_assert_true(string_set_contains(set1, "test_string2"));
2102
g_assert_false(string_set_contains(set2, "test_string2"));
2103
g_assert_true(string_set_contains(set2, "test_string1"));
2106
static bool fd_has_cloexec_and_nonblock(const int);
2108
static bool epoll_set_contains(int, int, uint32_t);
2110
static void test_start_mandos_client(test_fixture *fixture,
2111
__attribute__((unused))
2112
gconstpointer user_data){
2114
bool mandos_client_exited = false;
2115
bool quit_now = false;
2116
__attribute__((cleanup(cleanup_close)))
2117
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2118
g_assert_cmpint(epoll_fd, >=, 0);
2119
__attribute__((cleanup(cleanup_queue)))
2120
task_queue *queue = create_queue();
2121
g_assert_nonnull(queue);
2122
buffer password = {};
2123
bool password_is_read = false;
2124
const char helper_directory[] = "/nonexistent";
2125
const char *const argv[] = { "/bin/true", NULL };
2127
g_assert_true(start_mandos_client(queue, epoll_fd,
2128
&mandos_client_exited, &quit_now,
2129
&password, &password_is_read,
2130
&fixture->orig_sigaction,
2131
fixture->orig_sigmask,
2132
helper_directory, 0, 0, argv));
2134
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2136
const task_context *const added_wait_task
2137
= find_matching_task(queue, (task_context){
2138
.func=wait_for_mandos_client_exit,
2139
.mandos_client_exited=&mandos_client_exited,
2140
.quit_now=&quit_now,
2142
g_assert_nonnull(added_wait_task);
2143
g_assert_cmpint(added_wait_task->pid, >, 0);
2144
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2145
waitpid(added_wait_task->pid, NULL, 0);
2147
const task_context *const added_read_task
2148
= find_matching_task(queue, (task_context){
2149
.func=read_mandos_client_output,
2151
.password=&password,
2152
.password_is_read=&password_is_read,
2153
.quit_now=&quit_now,
2155
g_assert_nonnull(added_read_task);
2156
g_assert_cmpint(added_read_task->fd, >, 2);
2157
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2158
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2159
EPOLLIN | EPOLLRDHUP));
2162
static bool fd_has_cloexec_and_nonblock(const int fd){
2163
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2164
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2165
return ((socket_fd_flags >= 0)
2166
and (socket_fd_flags & FD_CLOEXEC)
2167
and (socket_file_flags >= 0)
2168
and (socket_file_flags & O_NONBLOCK));
2171
__attribute__((const))
2172
bool is_privileged(void){
2173
uid_t user = getuid() + 1;
2174
if(user == 0){ /* Overflow check */
2177
gid_t group = getuid() + 1;
2178
if(group == 0){ /* Overflow check */
2181
const pid_t pid = fork();
2182
if(pid == 0){ /* Child */
2183
if(setresgid((uid_t)-1, group, group) == -1){
2185
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2186
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2190
if(setresuid((uid_t)-1, user, user) == -1){
2192
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2193
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2200
error(EXIT_FAILURE, errno, "Failed to fork()");
2204
waitpid(pid, &status, 0);
2205
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2211
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2212
/* Only scan for events in this eventmask */
2213
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2214
__attribute__((cleanup(cleanup_string)))
2215
char *fdinfo_name = NULL;
2216
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2217
g_assert_cmpint(ret, >, 0);
2218
g_assert_nonnull(fdinfo_name);
2220
FILE *fdinfo = fopen(fdinfo_name, "r");
2221
g_assert_nonnull(fdinfo);
2222
uint32_t reported_events;
2227
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2230
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2231
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2232
&found_fd, &reported_events) == 2){
2237
} while(not feof(fdinfo) and not ferror(fdinfo));
2238
g_assert_cmpint(fclose(fdinfo), ==, 0);
2245
/* Don't check events if none are given */
2248
return (reported_events & eventmask) == (events & eventmask);
2251
static void test_start_mandos_client_execv(test_fixture *fixture,
2252
__attribute__((unused))
2253
gconstpointer user_data){
2254
bool mandos_client_exited = false;
2255
bool quit_now = false;
2256
__attribute__((cleanup(cleanup_close)))
2257
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2258
g_assert_cmpint(epoll_fd, >=, 0);
2259
__attribute__((cleanup(cleanup_queue)))
2260
task_queue *queue = create_queue();
2261
g_assert_nonnull(queue);
2262
__attribute__((cleanup(cleanup_buffer)))
2263
buffer password = {};
2264
const char helper_directory[] = "/nonexistent";
2265
/* Can't execv("/", ...), so this should fail */
2266
const char *const argv[] = { "/", NULL };
2269
__attribute__((cleanup(cleanup_close)))
2270
const int devnull_fd = open("/dev/null",
2271
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2272
g_assert_cmpint(devnull_fd, >=, 0);
2273
__attribute__((cleanup(cleanup_close)))
2274
const int real_stderr_fd = dup(STDERR_FILENO);
2275
g_assert_cmpint(real_stderr_fd, >=, 0);
2276
dup2(devnull_fd, STDERR_FILENO);
2278
const bool success = start_mandos_client(queue, epoll_fd,
2279
&mandos_client_exited,
2283
&fixture->orig_sigaction,
2284
fixture->orig_sigmask,
2285
helper_directory, 0, 0,
2287
dup2(real_stderr_fd, STDERR_FILENO);
2288
g_assert_true(success);
2290
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2292
struct timespec starttime, currtime;
2293
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2295
queue->next_run = 0;
2296
string_set cancelled_filenames = {};
2299
__attribute__((cleanup(cleanup_close)))
2300
const int devnull_fd = open("/dev/null",
2301
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2302
g_assert_cmpint(devnull_fd, >=, 0);
2303
__attribute__((cleanup(cleanup_close)))
2304
const int real_stderr_fd = dup(STDERR_FILENO);
2305
g_assert_cmpint(real_stderr_fd, >=, 0);
2306
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2307
dup2(devnull_fd, STDERR_FILENO);
2308
const bool success = run_queue(&queue, &cancelled_filenames,
2310
dup2(real_stderr_fd, STDERR_FILENO);
2315
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2316
} while(((queue->length) > 0)
2318
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2320
g_assert_true(quit_now);
2321
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2322
g_assert_true(mandos_client_exited);
2325
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2326
__attribute__((unused))
2329
if(not is_privileged()){
2330
g_test_skip("Not privileged");
2334
bool mandos_client_exited = false;
2335
bool quit_now = false;
2336
__attribute__((cleanup(cleanup_close)))
2337
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2338
g_assert_cmpint(epoll_fd, >=, 0);
2339
__attribute__((cleanup(cleanup_queue)))
2340
task_queue *queue = create_queue();
2341
g_assert_nonnull(queue);
2342
__attribute__((cleanup(cleanup_buffer)))
2343
buffer password = {};
2344
bool password_is_read = false;
2345
const char helper_directory[] = "/nonexistent";
2346
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2350
const bool success = start_mandos_client(queue, epoll_fd,
2351
&mandos_client_exited,
2352
&quit_now, &password,
2354
&fixture->orig_sigaction,
2355
fixture->orig_sigmask,
2356
helper_directory, user,
2358
g_assert_true(success);
2359
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2361
struct timespec starttime, currtime;
2362
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2364
queue->next_run = 0;
2365
string_set cancelled_filenames = {};
2366
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2367
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2368
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2369
} while(((queue->length) > 0)
2371
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2373
g_assert_false(quit_now);
2374
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2375
g_assert_true(mandos_client_exited);
2377
g_assert_true(password_is_read);
2378
g_assert_nonnull(password.data);
2381
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2383
g_assert_true((uid_t)id == id);
2385
g_assert_cmpuint((unsigned int)id, ==, 0);
2388
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2389
__attribute__((unused))
2392
if(not is_privileged()){
2393
g_test_skip("Not privileged");
2397
bool mandos_client_exited = false;
2398
bool quit_now = false;
2399
__attribute__((cleanup(cleanup_close)))
2400
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2401
g_assert_cmpint(epoll_fd, >=, 0);
2402
__attribute__((cleanup(cleanup_queue)))
2403
task_queue *queue = create_queue();
2404
g_assert_nonnull(queue);
2405
__attribute__((cleanup(cleanup_buffer)))
2406
buffer password = {};
2407
bool password_is_read = false;
2408
const char helper_directory[] = "/nonexistent";
2409
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2413
const bool success = start_mandos_client(queue, epoll_fd,
2414
&mandos_client_exited,
2415
&quit_now, &password,
2417
&fixture->orig_sigaction,
2418
fixture->orig_sigmask,
2419
helper_directory, user,
2421
g_assert_true(success);
2422
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2424
struct timespec starttime, currtime;
2425
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2427
queue->next_run = 0;
2428
string_set cancelled_filenames = {};
2429
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2430
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2431
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2432
} while(((queue->length) > 0)
2434
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2436
g_assert_false(quit_now);
2437
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2438
g_assert_true(mandos_client_exited);
2440
g_assert_true(password_is_read);
2441
g_assert_nonnull(password.data);
2444
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2446
g_assert_true((gid_t)id == id);
2448
g_assert_cmpuint((unsigned int)id, ==, 0);
2451
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2452
__attribute__((unused))
2455
if(not is_privileged()){
2456
g_test_skip("Not privileged");
2460
bool mandos_client_exited = false;
2461
bool quit_now = false;
2462
__attribute__((cleanup(cleanup_close)))
2463
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2464
g_assert_cmpint(epoll_fd, >=, 0);
2465
__attribute__((cleanup(cleanup_queue)))
2466
task_queue *queue = create_queue();
2467
g_assert_nonnull(queue);
2468
__attribute__((cleanup(cleanup_buffer)))
2469
buffer password = {};
2470
bool password_is_read = false;
2471
const char helper_directory[] = "/nonexistent";
2472
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2477
const bool success = start_mandos_client(queue, epoll_fd,
2478
&mandos_client_exited,
2479
&quit_now, &password,
2481
&fixture->orig_sigaction,
2482
fixture->orig_sigmask,
2483
helper_directory, user,
2485
g_assert_true(success);
2486
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2488
struct timespec starttime, currtime;
2489
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2491
queue->next_run = 0;
2492
string_set cancelled_filenames = {};
2493
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2494
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2495
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2496
} while(((queue->length) > 0)
2498
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2500
g_assert_false(quit_now);
2501
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2502
g_assert_true(mandos_client_exited);
2504
g_assert_true(password_is_read);
2505
g_assert_nonnull(password.data);
2508
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2510
g_assert_true((uid_t)id == id);
2512
g_assert_cmpuint((unsigned int)id, ==, user);
2515
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2516
__attribute__((unused))
2519
if(not is_privileged()){
2520
g_test_skip("Not privileged");
2524
bool mandos_client_exited = false;
2525
bool quit_now = false;
2526
__attribute__((cleanup(cleanup_close)))
2527
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2528
g_assert_cmpint(epoll_fd, >=, 0);
2529
__attribute__((cleanup(cleanup_queue)))
2530
task_queue *queue = create_queue();
2531
g_assert_nonnull(queue);
2532
__attribute__((cleanup(cleanup_buffer)))
2533
buffer password = {};
2534
bool password_is_read = false;
2535
const char helper_directory[] = "/nonexistent";
2536
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2541
const bool success = start_mandos_client(queue, epoll_fd,
2542
&mandos_client_exited,
2543
&quit_now, &password,
2545
&fixture->orig_sigaction,
2546
fixture->orig_sigmask,
2547
helper_directory, user,
2549
g_assert_true(success);
2550
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2552
struct timespec starttime, currtime;
2553
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2555
queue->next_run = 0;
2556
string_set cancelled_filenames = {};
2557
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2558
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2559
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2560
} while(((queue->length) > 0)
2562
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2564
g_assert_false(quit_now);
2565
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2566
g_assert_true(mandos_client_exited);
2568
g_assert_true(password_is_read);
2569
g_assert_nonnull(password.data);
2572
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2574
g_assert_true((gid_t)id == id);
2576
g_assert_cmpuint((unsigned int)id, ==, group);
2579
static void test_start_mandos_client_read(test_fixture *fixture,
2580
__attribute__((unused))
2581
gconstpointer user_data){
2582
bool mandos_client_exited = false;
2583
bool quit_now = false;
2584
__attribute__((cleanup(cleanup_close)))
2585
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2586
g_assert_cmpint(epoll_fd, >=, 0);
2587
__attribute__((cleanup(cleanup_queue)))
2588
task_queue *queue = create_queue();
2589
g_assert_nonnull(queue);
2590
__attribute__((cleanup(cleanup_buffer)))
2591
buffer password = {};
2592
bool password_is_read = false;
2593
const char dummy_test_password[] = "dummy test password";
2594
const char helper_directory[] = "/nonexistent";
2595
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2598
const bool success = start_mandos_client(queue, epoll_fd,
2599
&mandos_client_exited,
2600
&quit_now, &password,
2602
&fixture->orig_sigaction,
2603
fixture->orig_sigmask,
2604
helper_directory, 0, 0,
2606
g_assert_true(success);
2607
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2609
struct timespec starttime, currtime;
2610
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2612
queue->next_run = 0;
2613
string_set cancelled_filenames = {};
2614
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2615
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2616
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2617
} while(((queue->length) > 0)
2619
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2621
g_assert_false(quit_now);
2622
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2623
g_assert_true(mandos_client_exited);
2625
g_assert_true(password_is_read);
2626
g_assert_cmpint((int)password.length, ==,
2627
sizeof(dummy_test_password)-1);
2628
g_assert_nonnull(password.data);
2629
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2630
sizeof(dummy_test_password)-1), ==, 0);
2634
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2635
__attribute__((unused))
2638
bool mandos_client_exited = false;
2639
bool quit_now = false;
2640
__attribute__((cleanup(cleanup_close)))
2641
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2642
g_assert_cmpint(epoll_fd, >=, 0);
2643
__attribute__((cleanup(cleanup_queue)))
2644
task_queue *queue = create_queue();
2645
g_assert_nonnull(queue);
2646
__attribute__((cleanup(cleanup_buffer)))
2647
buffer password = {};
2648
bool password_is_read = false;
2649
const char helper_directory[] = "/nonexistent";
2650
const char *const argv[] = { "/bin/sh", "-c",
2651
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2653
const bool success = start_mandos_client(queue, epoll_fd,
2654
&mandos_client_exited,
2655
&quit_now, &password,
2657
&fixture->orig_sigaction,
2658
fixture->orig_sigmask,
2659
helper_directory, 0, 0,
2661
g_assert_true(success);
2662
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2664
struct timespec starttime, currtime;
2665
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2667
queue->next_run = 0;
2668
string_set cancelled_filenames = {};
2669
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2670
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2671
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2672
} while(((queue->length) > 0)
2674
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2676
g_assert_false(quit_now);
2677
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2678
g_assert_true(mandos_client_exited);
2680
g_assert_true(password_is_read);
2681
g_assert_cmpint((int)password.length, ==,
2682
sizeof(helper_directory)-1);
2683
g_assert_nonnull(password.data);
2684
g_assert_cmpint(memcmp(helper_directory, password.data,
2685
sizeof(helper_directory)-1), ==, 0);
2688
__attribute__((nonnull, warn_unused_result))
2689
static bool proc_status_sigblk_to_sigset(const char *const,
2692
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2693
__attribute__((unused))
2694
gconstpointer user_data){
2695
bool mandos_client_exited = false;
2696
bool quit_now = false;
2697
__attribute__((cleanup(cleanup_close)))
2698
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2699
g_assert_cmpint(epoll_fd, >=, 0);
2700
__attribute__((cleanup(cleanup_queue)))
2701
task_queue *queue = create_queue();
2702
g_assert_nonnull(queue);
2703
__attribute__((cleanup(cleanup_buffer)))
2704
buffer password = {};
2705
bool password_is_read = false;
2706
const char helper_directory[] = "/nonexistent";
2707
/* see proc(5) for format of /proc/self/status */
2708
const char *const argv[] = { "/usr/bin/awk",
2709
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2711
g_assert_true(start_mandos_client(queue, epoll_fd,
2712
&mandos_client_exited, &quit_now,
2713
&password, &password_is_read,
2714
&fixture->orig_sigaction,
2715
fixture->orig_sigmask,
2716
helper_directory, 0, 0, argv));
2718
struct timespec starttime, currtime;
2719
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2721
queue->next_run = 0;
2722
string_set cancelled_filenames = {};
2723
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2724
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2725
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2726
} while((not (mandos_client_exited and password_is_read))
2728
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2729
g_assert_true(mandos_client_exited);
2730
g_assert_true(password_is_read);
2732
sigset_t parsed_sigmask;
2733
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2736
for(int signum = 1; signum < NSIG; signum++){
2737
const bool has_signal = sigismember(&parsed_sigmask, signum);
2738
if(sigismember(&fixture->orig_sigmask, signum)){
2739
g_assert_true(has_signal);
2741
g_assert_false(has_signal);
2746
__attribute__((nonnull, warn_unused_result))
2747
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2748
sigset_t *const sigmask){
2749
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2750
uintmax_t scanned_sigmask;
2751
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2754
if(sigemptyset(sigmask) != 0){
2757
for(int signum = 1; signum < NSIG; signum++){
2758
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2759
if(sigaddset(sigmask, signum) != 0){
2767
static void run_task_with_stderr_to_dev_null(const task_context task,
2768
task_queue *const queue);
2771
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2772
test_fixture *fixture,
2773
__attribute__((unused))
2774
gconstpointer user_data){
2776
bool mandos_client_exited = false;
2777
bool quit_now = false;
2779
__attribute__((cleanup(cleanup_queue)))
2780
task_queue *queue = create_queue();
2781
g_assert_nonnull(queue);
2782
const task_context task = {
2783
.func=wait_for_mandos_client_exit,
2785
.mandos_client_exited=&mandos_client_exited,
2786
.quit_now=&quit_now,
2788
run_task_with_stderr_to_dev_null(task, queue);
2790
g_assert_false(mandos_client_exited);
2791
g_assert_true(quit_now);
2792
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2795
static void run_task_with_stderr_to_dev_null(const task_context task,
2796
task_queue *const queue){
2797
FILE *real_stderr = stderr;
2798
FILE *devnull = fopen("/dev/null", "we");
2799
g_assert_nonnull(devnull);
2802
task.func(task, queue);
2803
stderr = real_stderr;
2805
g_assert_cmpint(fclose(devnull), ==, 0);
2809
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2810
__attribute__((unused))
2811
gconstpointer user_data){
2812
bool mandos_client_exited = false;
2813
bool quit_now = false;
2815
pid_t create_eternal_process(void){
2816
const pid_t pid = fork();
2817
if(pid == 0){ /* Child */
2818
if(not restore_signal_handler(&fixture->orig_sigaction)){
2819
_exit(EXIT_FAILURE);
2821
if(not restore_sigmask(&fixture->orig_sigmask)){
2822
_exit(EXIT_FAILURE);
2830
pid_t pid = create_eternal_process();
2831
g_assert_true(pid != -1);
2833
__attribute__((cleanup(cleanup_queue)))
2834
task_queue *queue = create_queue();
2835
g_assert_nonnull(queue);
2836
const task_context task = {
2837
.func=wait_for_mandos_client_exit,
2839
.mandos_client_exited=&mandos_client_exited,
2840
.quit_now=&quit_now,
2842
task.func(task, queue);
2844
g_assert_false(mandos_client_exited);
2845
g_assert_false(quit_now);
2846
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2848
g_assert_nonnull(find_matching_task(queue, (task_context){
2849
.func=wait_for_mandos_client_exit,
2851
.mandos_client_exited=&mandos_client_exited,
2852
.quit_now=&quit_now,
2857
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2858
__attribute__((unused))
2861
bool mandos_client_exited = false;
2862
bool quit_now = false;
2864
pid_t create_successful_process(void){
2865
const pid_t pid = fork();
2866
if(pid == 0){ /* Child */
2867
if(not restore_signal_handler(&fixture->orig_sigaction)){
2868
_exit(EXIT_FAILURE);
2870
if(not restore_sigmask(&fixture->orig_sigmask)){
2871
_exit(EXIT_FAILURE);
2877
const pid_t pid = create_successful_process();
2878
g_assert_true(pid != -1);
2880
__attribute__((cleanup(cleanup_queue)))
2881
task_queue *queue = create_queue();
2882
g_assert_nonnull(queue);
2883
const task_context initial_task = {
2884
.func=wait_for_mandos_client_exit,
2886
.mandos_client_exited=&mandos_client_exited,
2887
.quit_now=&quit_now,
2889
g_assert_true(add_to_queue(queue, initial_task));
2891
struct timespec starttime, currtime;
2892
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2893
__attribute__((cleanup(cleanup_close)))
2894
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2896
queue->next_run = 0;
2897
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2898
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2899
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2900
} while((not mandos_client_exited)
2902
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2904
g_assert_true(mandos_client_exited);
2905
g_assert_false(quit_now);
2906
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2910
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2911
__attribute__((unused))
2914
bool mandos_client_exited = false;
2915
bool quit_now = false;
2917
pid_t create_failing_process(void){
2918
const pid_t pid = fork();
2919
if(pid == 0){ /* Child */
2920
if(not restore_signal_handler(&fixture->orig_sigaction)){
2921
_exit(EXIT_FAILURE);
2923
if(not restore_sigmask(&fixture->orig_sigmask)){
2924
_exit(EXIT_FAILURE);
2930
const pid_t pid = create_failing_process();
2931
g_assert_true(pid != -1);
2933
__attribute__((cleanup(string_set_clear)))
2934
string_set cancelled_filenames = {};
2935
__attribute__((cleanup(cleanup_close)))
2936
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2937
g_assert_cmpint(epoll_fd, >=, 0);
2938
__attribute__((cleanup(cleanup_queue)))
2939
task_queue *queue = create_queue();
2940
g_assert_nonnull(queue);
2941
g_assert_true(add_to_queue(queue, (task_context){
2942
.func=wait_for_mandos_client_exit,
2944
.mandos_client_exited=&mandos_client_exited,
2945
.quit_now=&quit_now,
2948
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2950
__attribute__((cleanup(cleanup_close)))
2951
const int devnull_fd = open("/dev/null",
2952
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2953
g_assert_cmpint(devnull_fd, >=, 0);
2954
__attribute__((cleanup(cleanup_close)))
2955
const int real_stderr_fd = dup(STDERR_FILENO);
2956
g_assert_cmpint(real_stderr_fd, >=, 0);
2958
struct timespec starttime, currtime;
2959
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2961
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2962
dup2(devnull_fd, STDERR_FILENO);
2963
const bool success = run_queue(&queue, &cancelled_filenames,
2965
dup2(real_stderr_fd, STDERR_FILENO);
2970
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2971
} while((not mandos_client_exited)
2973
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2975
g_assert_true(quit_now);
2976
g_assert_true(mandos_client_exited);
2977
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2981
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2982
__attribute__((unused))
2983
gconstpointer user_data){
2984
bool mandos_client_exited = false;
2985
bool quit_now = false;
2987
pid_t create_killed_process(void){
2988
const pid_t pid = fork();
2989
if(pid == 0){ /* Child */
2990
if(not restore_signal_handler(&fixture->orig_sigaction)){
2991
_exit(EXIT_FAILURE);
2993
if(not restore_sigmask(&fixture->orig_sigmask)){
2994
_exit(EXIT_FAILURE);
3003
const pid_t pid = create_killed_process();
3004
g_assert_true(pid != -1);
3006
__attribute__((cleanup(string_set_clear)))
3007
string_set cancelled_filenames = {};
3008
__attribute__((cleanup(cleanup_close)))
3009
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3010
g_assert_cmpint(epoll_fd, >=, 0);
3011
__attribute__((cleanup(cleanup_queue)))
3012
task_queue *queue = create_queue();
3013
g_assert_nonnull(queue);
3014
g_assert_true(add_to_queue(queue, (task_context){
3015
.func=wait_for_mandos_client_exit,
3017
.mandos_client_exited=&mandos_client_exited,
3018
.quit_now=&quit_now,
3021
__attribute__((cleanup(cleanup_close)))
3022
const int devnull_fd = open("/dev/null",
3023
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3024
g_assert_cmpint(devnull_fd, >=, 0);
3025
__attribute__((cleanup(cleanup_close)))
3026
const int real_stderr_fd = dup(STDERR_FILENO);
3027
g_assert_cmpint(real_stderr_fd, >=, 0);
3029
struct timespec starttime, currtime;
3030
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3032
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3033
dup2(devnull_fd, STDERR_FILENO);
3034
const bool success = run_queue(&queue, &cancelled_filenames,
3036
dup2(real_stderr_fd, STDERR_FILENO);
3041
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3042
} while((not mandos_client_exited)
3044
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3046
g_assert_true(mandos_client_exited);
3047
g_assert_true(quit_now);
3048
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3051
static bool epoll_set_does_not_contain(int, int);
3054
void test_read_mandos_client_output_readerror(__attribute__((unused))
3055
test_fixture *fixture,
3056
__attribute__((unused))
3059
__attribute__((cleanup(cleanup_close)))
3060
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3061
g_assert_cmpint(epoll_fd, >=, 0);
3063
__attribute__((cleanup(cleanup_buffer)))
3064
buffer password = {};
3066
/* Reading /proc/self/mem from offset 0 will always give EIO */
3067
const int fd = open("/proc/self/mem",
3068
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3070
bool password_is_read = false;
3071
bool quit_now = false;
3072
__attribute__((cleanup(cleanup_queue)))
3073
task_queue *queue = create_queue();
3074
g_assert_nonnull(queue);
3076
task_context task = {
3077
.func=read_mandos_client_output,
3080
.password=&password,
3081
.password_is_read=&password_is_read,
3082
.quit_now=&quit_now,
3084
run_task_with_stderr_to_dev_null(task, queue);
3085
g_assert_false(password_is_read);
3086
g_assert_cmpint((int)password.length, ==, 0);
3087
g_assert_true(quit_now);
3088
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3090
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3092
g_assert_cmpint(close(fd), ==, -1);
3095
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3096
return not epoll_set_contains(epoll_fd, fd, 0);
3100
void test_read_mandos_client_output_nodata(__attribute__((unused))
3101
test_fixture *fixture,
3102
__attribute__((unused))
3103
gconstpointer user_data){
3104
__attribute__((cleanup(cleanup_close)))
3105
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3106
g_assert_cmpint(epoll_fd, >=, 0);
3109
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3111
__attribute__((cleanup(cleanup_buffer)))
3112
buffer password = {};
3114
bool password_is_read = false;
3115
bool quit_now = false;
3116
__attribute__((cleanup(cleanup_queue)))
3117
task_queue *queue = create_queue();
3118
g_assert_nonnull(queue);
3120
task_context task = {
3121
.func=read_mandos_client_output,
3124
.password=&password,
3125
.password_is_read=&password_is_read,
3126
.quit_now=&quit_now,
3128
task.func(task, queue);
3129
g_assert_false(password_is_read);
3130
g_assert_cmpint((int)password.length, ==, 0);
3131
g_assert_false(quit_now);
3132
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3134
g_assert_nonnull(find_matching_task(queue, (task_context){
3135
.func=read_mandos_client_output,
3138
.password=&password,
3139
.password_is_read=&password_is_read,
3140
.quit_now=&quit_now,
3143
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3144
EPOLLIN | EPOLLRDHUP));
3146
g_assert_cmpint(close(pipefds[1]), ==, 0);
3149
static void test_read_mandos_client_output_eof(__attribute__((unused))
3150
test_fixture *fixture,
3151
__attribute__((unused))
3154
__attribute__((cleanup(cleanup_close)))
3155
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3156
g_assert_cmpint(epoll_fd, >=, 0);
3159
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3160
g_assert_cmpint(close(pipefds[1]), ==, 0);
3162
__attribute__((cleanup(cleanup_buffer)))
3163
buffer password = {};
3165
bool password_is_read = false;
3166
bool quit_now = false;
3167
__attribute__((cleanup(cleanup_queue)))
3168
task_queue *queue = create_queue();
3169
g_assert_nonnull(queue);
3171
task_context task = {
3172
.func=read_mandos_client_output,
3175
.password=&password,
3176
.password_is_read=&password_is_read,
3177
.quit_now=&quit_now,
3179
task.func(task, queue);
3180
g_assert_true(password_is_read);
3181
g_assert_cmpint((int)password.length, ==, 0);
3182
g_assert_false(quit_now);
3183
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3185
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3187
g_assert_cmpint(close(pipefds[0]), ==, -1);
3191
void test_read_mandos_client_output_once(__attribute__((unused))
3192
test_fixture *fixture,
3193
__attribute__((unused))
3194
gconstpointer user_data){
3195
__attribute__((cleanup(cleanup_close)))
3196
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3197
g_assert_cmpint(epoll_fd, >=, 0);
3200
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3202
const char dummy_test_password[] = "dummy test password";
3203
/* Start with a pre-allocated buffer */
3204
__attribute__((cleanup(cleanup_buffer)))
3206
.data=malloc(sizeof(dummy_test_password)),
3208
.allocated=sizeof(dummy_test_password),
3210
g_assert_nonnull(password.data);
3211
if(mlock(password.data, password.allocated) != 0){
3212
g_assert_true(errno == EPERM or errno == ENOMEM);
3215
bool password_is_read = false;
3216
bool quit_now = false;
3217
__attribute__((cleanup(cleanup_queue)))
3218
task_queue *queue = create_queue();
3219
g_assert_nonnull(queue);
3221
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3222
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3223
sizeof(dummy_test_password)),
3224
==, (int)sizeof(dummy_test_password));
3226
task_context task = {
3227
.func=read_mandos_client_output,
3230
.password=&password,
3231
.password_is_read=&password_is_read,
3232
.quit_now=&quit_now,
3234
task.func(task, queue);
3236
g_assert_false(password_is_read);
3237
g_assert_cmpint((int)password.length, ==,
3238
(int)sizeof(dummy_test_password));
3239
g_assert_nonnull(password.data);
3240
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3241
sizeof(dummy_test_password)), ==, 0);
3243
g_assert_false(quit_now);
3244
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3246
g_assert_nonnull(find_matching_task(queue, (task_context){
3247
.func=read_mandos_client_output,
3250
.password=&password,
3251
.password_is_read=&password_is_read,
3252
.quit_now=&quit_now,
3255
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3256
EPOLLIN | EPOLLRDHUP));
3258
g_assert_cmpint(close(pipefds[1]), ==, 0);
3262
void test_read_mandos_client_output_malloc(__attribute__((unused))
3263
test_fixture *fixture,
3264
__attribute__((unused))
3265
gconstpointer user_data){
3266
__attribute__((cleanup(cleanup_close)))
3267
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3268
g_assert_cmpint(epoll_fd, >=, 0);
3271
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3273
const char dummy_test_password[] = "dummy test password";
3274
/* Start with an empty buffer */
3275
__attribute__((cleanup(cleanup_buffer)))
3276
buffer password = {};
3278
bool password_is_read = false;
3279
bool quit_now = false;
3280
__attribute__((cleanup(cleanup_queue)))
3281
task_queue *queue = create_queue();
3282
g_assert_nonnull(queue);
3284
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3285
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3286
sizeof(dummy_test_password)),
3287
==, (int)sizeof(dummy_test_password));
3289
task_context task = {
3290
.func=read_mandos_client_output,
3293
.password=&password,
3294
.password_is_read=&password_is_read,
3295
.quit_now=&quit_now,
3297
task.func(task, queue);
3299
g_assert_false(password_is_read);
3300
g_assert_cmpint((int)password.length, ==,
3301
(int)sizeof(dummy_test_password));
3302
g_assert_nonnull(password.data);
3303
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3304
sizeof(dummy_test_password)), ==, 0);
3306
g_assert_false(quit_now);
3307
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3309
g_assert_nonnull(find_matching_task(queue, (task_context){
3310
.func=read_mandos_client_output,
3313
.password=&password,
3314
.password_is_read=&password_is_read,
3315
.quit_now=&quit_now,
3318
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3319
EPOLLIN | EPOLLRDHUP));
3321
g_assert_cmpint(close(pipefds[1]), ==, 0);
3325
void test_read_mandos_client_output_append(__attribute__((unused))
3326
test_fixture *fixture,
3327
__attribute__((unused))
3328
gconstpointer user_data){
3329
__attribute__((cleanup(cleanup_close)))
3330
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3331
g_assert_cmpint(epoll_fd, >=, 0);
3334
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3336
const char dummy_test_password[] = "dummy test password";
3337
__attribute__((cleanup(cleanup_buffer)))
3339
.data=malloc(PIPE_BUF),
3341
.allocated=PIPE_BUF,
3343
g_assert_nonnull(password.data);
3344
if(mlock(password.data, password.allocated) != 0){
3345
g_assert_true(errno == EPERM or errno == ENOMEM);
3348
memset(password.data, 'x', PIPE_BUF);
3349
char password_expected[PIPE_BUF];
3350
memcpy(password_expected, password.data, PIPE_BUF);
3352
bool password_is_read = false;
3353
bool quit_now = false;
3354
__attribute__((cleanup(cleanup_queue)))
3355
task_queue *queue = create_queue();
3356
g_assert_nonnull(queue);
3358
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3359
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3360
sizeof(dummy_test_password)),
3361
==, (int)sizeof(dummy_test_password));
3363
task_context task = {
3364
.func=read_mandos_client_output,
3367
.password=&password,
3368
.password_is_read=&password_is_read,
3369
.quit_now=&quit_now,
3371
task.func(task, queue);
3373
g_assert_false(password_is_read);
3374
g_assert_cmpint((int)password.length, ==,
3375
PIPE_BUF + sizeof(dummy_test_password));
3376
g_assert_nonnull(password.data);
3377
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3379
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3380
dummy_test_password,
3381
sizeof(dummy_test_password)), ==, 0);
3382
g_assert_false(quit_now);
3383
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3385
g_assert_nonnull(find_matching_task(queue, (task_context){
3386
.func=read_mandos_client_output,
3389
.password=&password,
3390
.password_is_read=&password_is_read,
3391
.quit_now=&quit_now,
3394
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3395
EPOLLIN | EPOLLRDHUP));
3398
static char *make_temporary_directory(void);
3400
static void test_add_inotify_dir_watch(__attribute__((unused))
3401
test_fixture *fixture,
3402
__attribute__((unused))
3403
gconstpointer user_data){
3404
__attribute__((cleanup(cleanup_close)))
3405
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3406
g_assert_cmpint(epoll_fd, >=, 0);
3407
__attribute__((cleanup(cleanup_queue)))
3408
task_queue *queue = create_queue();
3409
g_assert_nonnull(queue);
3410
__attribute__((cleanup(string_set_clear)))
3411
string_set cancelled_filenames = {};
3412
const mono_microsecs current_time = 0;
3414
bool quit_now = false;
3415
buffer password = {};
3416
bool mandos_client_exited = false;
3417
bool password_is_read = false;
3419
__attribute__((cleanup(cleanup_string)))
3420
char *tempdir = make_temporary_directory();
3421
g_assert_nonnull(tempdir);
3423
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3425
&cancelled_filenames,
3427
&mandos_client_exited,
3428
&password_is_read));
3430
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3432
const task_context *const added_read_task
3433
= find_matching_task(queue, (task_context){
3434
.func=read_inotify_event,
3436
.quit_now=&quit_now,
3437
.password=&password,
3439
.cancelled_filenames=&cancelled_filenames,
3440
.current_time=¤t_time,
3441
.mandos_client_exited=&mandos_client_exited,
3442
.password_is_read=&password_is_read,
3444
g_assert_nonnull(added_read_task);
3446
g_assert_cmpint(added_read_task->fd, >, 2);
3447
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3448
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3449
added_read_task->fd,
3450
EPOLLIN | EPOLLRDHUP));
3452
g_assert_cmpint(rmdir(tempdir), ==, 0);
3455
static char *make_temporary_directory(void){
3456
char *name = strdup("/tmp/mandosXXXXXX");
3457
g_assert_nonnull(name);
3458
char *result = mkdtemp(name);
3465
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3466
test_fixture *fixture,
3467
__attribute__((unused))
3468
gconstpointer user_data){
3469
__attribute__((cleanup(cleanup_close)))
3470
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3471
g_assert_cmpint(epoll_fd, >=, 0);
3472
__attribute__((cleanup(cleanup_queue)))
3473
task_queue *queue = create_queue();
3474
g_assert_nonnull(queue);
3475
__attribute__((cleanup(string_set_clear)))
3476
string_set cancelled_filenames = {};
3477
const mono_microsecs current_time = 0;
3479
bool quit_now = false;
3480
buffer password = {};
3481
bool mandos_client_exited = false;
3482
bool password_is_read = false;
3484
const char nonexistent_dir[] = "/nonexistent";
3486
FILE *real_stderr = stderr;
3487
FILE *devnull = fopen("/dev/null", "we");
3488
g_assert_nonnull(devnull);
3490
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3491
&password, nonexistent_dir,
3492
&cancelled_filenames,
3494
&mandos_client_exited,
3495
&password_is_read));
3496
stderr = real_stderr;
3497
g_assert_cmpint(fclose(devnull), ==, 0);
3499
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3502
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3503
test_fixture *fixture,
3504
__attribute__((unused))
3507
__attribute__((cleanup(cleanup_close)))
3508
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3509
g_assert_cmpint(epoll_fd, >=, 0);
3510
__attribute__((cleanup(cleanup_queue)))
3511
task_queue *queue = create_queue();
3512
g_assert_nonnull(queue);
3513
__attribute__((cleanup(string_set_clear)))
3514
string_set cancelled_filenames = {};
3515
const mono_microsecs current_time = 0;
3517
bool quit_now = false;
3518
buffer password = {};
3519
bool mandos_client_exited = false;
3520
bool password_is_read = false;
3522
const char not_a_directory[] = "/dev/tty";
3524
FILE *real_stderr = stderr;
3525
FILE *devnull = fopen("/dev/null", "we");
3526
g_assert_nonnull(devnull);
3528
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3529
&password, not_a_directory,
3530
&cancelled_filenames,
3532
&mandos_client_exited,
3533
&password_is_read));
3534
stderr = real_stderr;
3535
g_assert_cmpint(fclose(devnull), ==, 0);
3537
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3540
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3541
test_fixture *fixture,
3542
__attribute__((unused))
3545
__attribute__((cleanup(cleanup_close)))
3546
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3547
g_assert_cmpint(epoll_fd, >=, 0);
3548
__attribute__((cleanup(cleanup_queue)))
3549
task_queue *queue = create_queue();
3550
g_assert_nonnull(queue);
3551
__attribute__((cleanup(string_set_clear)))
3552
string_set cancelled_filenames = {};
3553
const mono_microsecs current_time = 0;
3555
bool quit_now = false;
3556
buffer password = {};
3557
bool mandos_client_exited = false;
3558
bool password_is_read = false;
3560
__attribute__((cleanup(cleanup_string)))
3561
char *tempdir = make_temporary_directory();
3562
g_assert_nonnull(tempdir);
3564
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3566
&cancelled_filenames,
3568
&mandos_client_exited,
3569
&password_is_read));
3571
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3573
const task_context *const added_read_task
3574
= find_matching_task(queue,
3575
(task_context){ .func=read_inotify_event });
3576
g_assert_nonnull(added_read_task);
3578
g_assert_cmpint(added_read_task->fd, >, 2);
3579
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3581
/* "sufficient to read at least one event." - inotify(7) */
3582
const size_t ievent_size = (sizeof(struct inotify_event)
3584
struct inotify_event *ievent = malloc(ievent_size);
3585
g_assert_nonnull(ievent);
3587
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3589
g_assert_cmpint(errno, ==, EAGAIN);
3593
g_assert_cmpint(rmdir(tempdir), ==, 0);
3596
static char *make_temporary_file_in_directory(const char
3600
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3601
test_fixture *fixture,
3602
__attribute__((unused))
3605
__attribute__((cleanup(cleanup_close)))
3606
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3607
g_assert_cmpint(epoll_fd, >=, 0);
3608
__attribute__((cleanup(cleanup_queue)))
3609
task_queue *queue = create_queue();
3610
g_assert_nonnull(queue);
3611
__attribute__((cleanup(string_set_clear)))
3612
string_set cancelled_filenames = {};
3613
const mono_microsecs current_time = 0;
3615
bool quit_now = false;
3616
buffer password = {};
3617
bool mandos_client_exited = false;
3618
bool password_is_read = false;
3620
__attribute__((cleanup(cleanup_string)))
3621
char *tempdir = make_temporary_directory();
3622
g_assert_nonnull(tempdir);
3624
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3626
&cancelled_filenames,
3628
&mandos_client_exited,
3629
&password_is_read));
3631
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3633
const task_context *const added_read_task
3634
= find_matching_task(queue,
3635
(task_context){ .func=read_inotify_event });
3636
g_assert_nonnull(added_read_task);
3638
g_assert_cmpint(added_read_task->fd, >, 2);
3639
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3641
__attribute__((cleanup(cleanup_string)))
3642
char *filename = make_temporary_file_in_directory(tempdir);
3643
g_assert_nonnull(filename);
3645
/* "sufficient to read at least one event." - inotify(7) */
3646
const size_t ievent_size = (sizeof(struct inotify_event)
3648
struct inotify_event *ievent = malloc(ievent_size);
3649
g_assert_nonnull(ievent);
3651
ssize_t read_size = 0;
3652
read_size = read(added_read_task->fd, ievent, ievent_size);
3654
g_assert_cmpint((int)read_size, >, 0);
3655
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3656
g_assert_cmpstr(ievent->name, ==, basename(filename));
3660
g_assert_cmpint(unlink(filename), ==, 0);
3661
g_assert_cmpint(rmdir(tempdir), ==, 0);
3664
static char *make_temporary_prefixed_file_in_directory(const char
3668
char *filename = NULL;
3669
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3671
g_assert_nonnull(filename);
3672
const int fd = mkostemp(filename, O_CLOEXEC);
3673
g_assert_cmpint(fd, >=, 0);
3674
g_assert_cmpint(close(fd), ==, 0);
3678
static char *make_temporary_file_in_directory(const char
3680
return make_temporary_prefixed_file_in_directory("temp", dir);
3684
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3685
test_fixture *fixture,
3686
__attribute__((unused))
3687
gconstpointer user_data){
3688
__attribute__((cleanup(cleanup_close)))
3689
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3690
g_assert_cmpint(epoll_fd, >=, 0);
3691
__attribute__((cleanup(cleanup_queue)))
3692
task_queue *queue = create_queue();
3693
g_assert_nonnull(queue);
3694
__attribute__((cleanup(string_set_clear)))
3695
string_set cancelled_filenames = {};
3696
const mono_microsecs current_time = 0;
3698
bool quit_now = false;
3699
buffer password = {};
3700
bool mandos_client_exited = false;
3701
bool password_is_read = false;
3703
__attribute__((cleanup(cleanup_string)))
3704
char *watchdir = make_temporary_directory();
3705
g_assert_nonnull(watchdir);
3707
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3708
&password, watchdir,
3709
&cancelled_filenames,
3711
&mandos_client_exited,
3712
&password_is_read));
3714
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3716
const task_context *const added_read_task
3717
= find_matching_task(queue,
3718
(task_context){ .func=read_inotify_event });
3719
g_assert_nonnull(added_read_task);
3721
g_assert_cmpint(added_read_task->fd, >, 2);
3722
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3724
char *sourcedir = make_temporary_directory();
3725
g_assert_nonnull(sourcedir);
3727
__attribute__((cleanup(cleanup_string)))
3728
char *filename = make_temporary_file_in_directory(sourcedir);
3729
g_assert_nonnull(filename);
3731
__attribute__((cleanup(cleanup_string)))
3732
char *targetfilename = NULL;
3733
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3734
basename(filename)), >, 0);
3735
g_assert_nonnull(targetfilename);
3737
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3738
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3741
/* "sufficient to read at least one event." - inotify(7) */
3742
const size_t ievent_size = (sizeof(struct inotify_event)
3744
struct inotify_event *ievent = malloc(ievent_size);
3745
g_assert_nonnull(ievent);
3747
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3749
g_assert_cmpint((int)read_size, >, 0);
3750
g_assert_true(ievent->mask & IN_MOVED_TO);
3751
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3755
g_assert_cmpint(unlink(targetfilename), ==, 0);
3756
g_assert_cmpint(rmdir(watchdir), ==, 0);
3760
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3761
test_fixture *fixture,
3762
__attribute__((unused))
3765
__attribute__((cleanup(cleanup_close)))
3766
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3767
g_assert_cmpint(epoll_fd, >=, 0);
3768
__attribute__((cleanup(cleanup_queue)))
3769
task_queue *queue = create_queue();
3770
g_assert_nonnull(queue);
3771
__attribute__((cleanup(string_set_clear)))
3772
string_set cancelled_filenames = {};
3773
const mono_microsecs current_time = 0;
3775
bool quit_now = false;
3776
buffer password = {};
3777
bool mandos_client_exited = false;
3778
bool password_is_read = false;
3780
__attribute__((cleanup(cleanup_string)))
3781
char *tempdir = make_temporary_directory();
3782
g_assert_nonnull(tempdir);
3784
__attribute__((cleanup(cleanup_string)))
3785
char *tempfilename = make_temporary_file_in_directory(tempdir);
3786
g_assert_nonnull(tempfilename);
3788
__attribute__((cleanup(cleanup_string)))
3789
char *targetdir = make_temporary_directory();
3790
g_assert_nonnull(targetdir);
3792
__attribute__((cleanup(cleanup_string)))
3793
char *targetfilename = NULL;
3794
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3795
basename(tempfilename)), >, 0);
3796
g_assert_nonnull(targetfilename);
3798
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3800
&cancelled_filenames,
3802
&mandos_client_exited,
3803
&password_is_read));
3805
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3807
const task_context *const added_read_task
3808
= find_matching_task(queue,
3809
(task_context){ .func=read_inotify_event });
3810
g_assert_nonnull(added_read_task);
3812
/* "sufficient to read at least one event." - inotify(7) */
3813
const size_t ievent_size = (sizeof(struct inotify_event)
3815
struct inotify_event *ievent = malloc(ievent_size);
3816
g_assert_nonnull(ievent);
3818
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3820
g_assert_cmpint((int)read_size, >, 0);
3821
g_assert_true(ievent->mask & IN_MOVED_FROM);
3822
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3826
g_assert_cmpint(unlink(targetfilename), ==, 0);
3827
g_assert_cmpint(rmdir(targetdir), ==, 0);
3828
g_assert_cmpint(rmdir(tempdir), ==, 0);
3832
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3833
test_fixture *fixture,
3834
__attribute__((unused))
3835
gconstpointer user_data){
3836
__attribute__((cleanup(cleanup_close)))
3837
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3838
g_assert_cmpint(epoll_fd, >=, 0);
3839
__attribute__((cleanup(cleanup_queue)))
3840
task_queue *queue = create_queue();
3841
g_assert_nonnull(queue);
3842
__attribute__((cleanup(string_set_clear)))
3843
string_set cancelled_filenames = {};
3844
const mono_microsecs current_time = 0;
3846
bool quit_now = false;
3847
buffer password = {};
3848
bool mandos_client_exited = false;
3849
bool password_is_read = false;
3851
__attribute__((cleanup(cleanup_string)))
3852
char *tempdir = make_temporary_directory();
3853
g_assert_nonnull(tempdir);
3855
__attribute__((cleanup(cleanup_string)))
3856
char *tempfile = make_temporary_file_in_directory(tempdir);
3857
g_assert_nonnull(tempfile);
3859
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3861
&cancelled_filenames,
3863
&mandos_client_exited,
3864
&password_is_read));
3865
g_assert_cmpint(unlink(tempfile), ==, 0);
3867
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3869
const task_context *const added_read_task
3870
= find_matching_task(queue,
3871
(task_context){ .func=read_inotify_event });
3872
g_assert_nonnull(added_read_task);
3874
g_assert_cmpint(added_read_task->fd, >, 2);
3875
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3877
/* "sufficient to read at least one event." - inotify(7) */
3878
const size_t ievent_size = (sizeof(struct inotify_event)
3880
struct inotify_event *ievent = malloc(ievent_size);
3881
g_assert_nonnull(ievent);
3883
ssize_t read_size = 0;
3884
read_size = read(added_read_task->fd, ievent, ievent_size);
3886
g_assert_cmpint((int)read_size, >, 0);
3887
g_assert_true(ievent->mask & IN_DELETE);
3888
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3892
g_assert_cmpint(rmdir(tempdir), ==, 0);
3896
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3897
test_fixture *fixture,
3898
__attribute__((unused))
3901
__attribute__((cleanup(cleanup_close)))
3902
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3903
g_assert_cmpint(epoll_fd, >=, 0);
3904
__attribute__((cleanup(cleanup_queue)))
3905
task_queue *queue = create_queue();
3906
g_assert_nonnull(queue);
3907
__attribute__((cleanup(string_set_clear)))
3908
string_set cancelled_filenames = {};
3909
const mono_microsecs current_time = 0;
3911
bool quit_now = false;
3912
buffer password = {};
3913
bool mandos_client_exited = false;
3914
bool password_is_read = false;
3916
__attribute__((cleanup(cleanup_string)))
3917
char *tempdir = make_temporary_directory();
3918
g_assert_nonnull(tempdir);
3920
__attribute__((cleanup(cleanup_string)))
3921
char *tempfile = make_temporary_file_in_directory(tempdir);
3922
g_assert_nonnull(tempfile);
3923
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3925
g_assert_cmpint(tempfile_fd, >, 2);
3927
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3929
&cancelled_filenames,
3931
&mandos_client_exited,
3932
&password_is_read));
3933
g_assert_cmpint(unlink(tempfile), ==, 0);
3935
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3937
const task_context *const added_read_task
3938
= find_matching_task(queue,
3939
(task_context){ .func=read_inotify_event });
3940
g_assert_nonnull(added_read_task);
3942
g_assert_cmpint(added_read_task->fd, >, 2);
3943
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3945
/* "sufficient to read at least one event." - inotify(7) */
3946
const size_t ievent_size = (sizeof(struct inotify_event)
3948
struct inotify_event *ievent = malloc(ievent_size);
3949
g_assert_nonnull(ievent);
3951
ssize_t read_size = 0;
3952
read_size = read(added_read_task->fd, ievent, ievent_size);
3954
g_assert_cmpint((int)read_size, >, 0);
3955
g_assert_true(ievent->mask & IN_DELETE);
3956
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3958
g_assert_cmpint(close(tempfile_fd), ==, 0);
3960
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3961
file not appear as an ievent, so we should not see it now. */
3962
read_size = read(added_read_task->fd, ievent, ievent_size);
3963
g_assert_cmpint((int)read_size, ==, -1);
3964
g_assert_true(errno == EAGAIN);
3968
g_assert_cmpint(rmdir(tempdir), ==, 0);
3971
static void test_read_inotify_event_readerror(__attribute__((unused))
3972
test_fixture *fixture,
3973
__attribute__((unused))
3976
__attribute__((cleanup(cleanup_close)))
3977
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3978
g_assert_cmpint(epoll_fd, >=, 0);
3979
const mono_microsecs current_time = 0;
3981
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3982
const int fd = open("/proc/self/mem",
3983
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3985
bool quit_now = false;
3986
__attribute__((cleanup(cleanup_queue)))
3987
task_queue *queue = create_queue();
3988
g_assert_nonnull(queue);
3990
task_context task = {
3991
.func=read_inotify_event,
3994
.quit_now=&quit_now,
3995
.filename=strdup("/nonexistent"),
3996
.cancelled_filenames = &(string_set){},
3998
.current_time=¤t_time,
4000
g_assert_nonnull(task.filename);
4001
run_task_with_stderr_to_dev_null(task, queue);
4002
g_assert_true(quit_now);
4003
g_assert_true(queue->next_run == 0);
4004
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4006
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4008
g_assert_cmpint(close(fd), ==, -1);
4011
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4012
test_fixture *fixture,
4013
__attribute__((unused))
4016
const mono_microsecs current_time = 17;
4019
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4020
const int epoll_fd = pipefds[0]; /* This will obviously fail */
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
g_assert_nonnull(task.filename);
4044
run_task_with_stderr_to_dev_null(task, queue);
4046
g_assert_nonnull(find_matching_task(queue, task));
4047
g_assert_true(queue->next_run == 1000000 + current_time);
4049
g_assert_cmpint(close(pipefds[0]), ==, 0);
4050
g_assert_cmpint(close(pipefds[1]), ==, 0);
4053
static void test_read_inotify_event_nodata(__attribute__((unused))
4054
test_fixture *fixture,
4055
__attribute__((unused))
4056
gconstpointer user_data){
4057
__attribute__((cleanup(cleanup_close)))
4058
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4059
g_assert_cmpint(epoll_fd, >=, 0);
4060
const mono_microsecs current_time = 0;
4063
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4065
bool quit_now = false;
4066
buffer password = {};
4067
bool mandos_client_exited = false;
4068
bool password_is_read = false;
4069
__attribute__((cleanup(cleanup_queue)))
4070
task_queue *queue = create_queue();
4071
g_assert_nonnull(queue);
4073
task_context task = {
4074
.func=read_inotify_event,
4077
.quit_now=&quit_now,
4078
.password=&password,
4079
.filename=strdup("/nonexistent"),
4080
.cancelled_filenames = &(string_set){},
4082
.current_time=¤t_time,
4083
.mandos_client_exited=&mandos_client_exited,
4084
.password_is_read=&password_is_read,
4086
g_assert_nonnull(task.filename);
4087
task.func(task, queue);
4088
g_assert_false(quit_now);
4089
g_assert_true(queue->next_run == 0);
4090
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4092
g_assert_nonnull(find_matching_task(queue, (task_context){
4093
.func=read_inotify_event,
4096
.quit_now=&quit_now,
4097
.password=&password,
4098
.filename=task.filename,
4099
.cancelled_filenames=task.cancelled_filenames,
4100
.current_time=¤t_time,
4101
.mandos_client_exited=&mandos_client_exited,
4102
.password_is_read=&password_is_read,
4105
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4106
EPOLLIN | EPOLLRDHUP));
4108
g_assert_cmpint(close(pipefds[1]), ==, 0);
4111
static void test_read_inotify_event_eof(__attribute__((unused))
4112
test_fixture *fixture,
4113
__attribute__((unused))
4114
gconstpointer user_data){
4115
__attribute__((cleanup(cleanup_close)))
4116
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4117
g_assert_cmpint(epoll_fd, >=, 0);
4118
const mono_microsecs current_time = 0;
4121
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4122
g_assert_cmpint(close(pipefds[1]), ==, 0);
4124
bool quit_now = false;
4125
buffer password = {};
4126
__attribute__((cleanup(cleanup_queue)))
4127
task_queue *queue = create_queue();
4128
g_assert_nonnull(queue);
4130
task_context task = {
4131
.func=read_inotify_event,
4134
.quit_now=&quit_now,
4135
.password=&password,
4136
.filename=strdup("/nonexistent"),
4137
.cancelled_filenames = &(string_set){},
4139
.current_time=¤t_time,
4141
run_task_with_stderr_to_dev_null(task, queue);
4142
g_assert_true(quit_now);
4143
g_assert_true(queue->next_run == 0);
4144
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4146
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4148
g_assert_cmpint(close(pipefds[0]), ==, -1);
4152
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4153
test_fixture *fixture,
4154
__attribute__((unused))
4155
gconstpointer user_data){
4156
__attribute__((cleanup(cleanup_close)))
4157
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4158
g_assert_cmpint(epoll_fd, >=, 0);
4159
const mono_microsecs current_time = 0;
4162
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4164
/* "sufficient to read at least one event." - inotify(7) */
4165
const size_t ievent_max_size = (sizeof(struct inotify_event)
4167
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4169
struct inotify_event event;
4170
char name_buffer[NAME_MAX + 1];
4172
struct inotify_event *const ievent = &ievent_buffer.event;
4174
const char dummy_file_name[] = "ask.dummy_file_name";
4175
ievent->mask = IN_CLOSE_WRITE;
4176
ievent->len = sizeof(dummy_file_name);
4177
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4178
const size_t ievent_size = (sizeof(struct inotify_event)
4179
+ sizeof(dummy_file_name));
4180
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4182
g_assert_cmpint(close(pipefds[1]), ==, 0);
4184
bool quit_now = false;
4185
buffer password = {};
4186
bool mandos_client_exited = false;
4187
bool password_is_read = false;
4188
__attribute__((cleanup(cleanup_queue)))
4189
task_queue *queue = create_queue();
4190
g_assert_nonnull(queue);
4192
task_context task = {
4193
.func=read_inotify_event,
4196
.quit_now=&quit_now,
4197
.password=&password,
4198
.filename=strdup("/nonexistent"),
4199
.cancelled_filenames = &(string_set){},
4201
.current_time=¤t_time,
4202
.mandos_client_exited=&mandos_client_exited,
4203
.password_is_read=&password_is_read,
4205
task.func(task, queue);
4206
g_assert_false(quit_now);
4207
g_assert_true(queue->next_run != 0);
4208
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4210
g_assert_nonnull(find_matching_task(queue, (task_context){
4211
.func=read_inotify_event,
4214
.quit_now=&quit_now,
4215
.password=&password,
4216
.filename=task.filename,
4217
.cancelled_filenames=task.cancelled_filenames,
4218
.current_time=¤t_time,
4219
.mandos_client_exited=&mandos_client_exited,
4220
.password_is_read=&password_is_read,
4223
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4224
EPOLLIN | EPOLLRDHUP));
4226
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4228
__attribute__((cleanup(cleanup_string)))
4229
char *filename = NULL;
4230
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4231
dummy_file_name), >, 0);
4232
g_assert_nonnull(filename);
4233
g_assert_nonnull(find_matching_task(queue, (task_context){
4234
.func=open_and_parse_question,
4237
.question_filename=filename,
4238
.password=&password,
4239
.cancelled_filenames=task.cancelled_filenames,
4240
.current_time=¤t_time,
4241
.mandos_client_exited=&mandos_client_exited,
4242
.password_is_read=&password_is_read,
4247
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4248
test_fixture *fixture,
4249
__attribute__((unused))
4250
gconstpointer user_data){
4251
__attribute__((cleanup(cleanup_close)))
4252
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4253
g_assert_cmpint(epoll_fd, >=, 0);
4254
const mono_microsecs current_time = 0;
4257
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4259
/* "sufficient to read at least one event." - inotify(7) */
4260
const size_t ievent_max_size = (sizeof(struct inotify_event)
4262
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4264
struct inotify_event event;
4265
char name_buffer[NAME_MAX + 1];
4267
struct inotify_event *const ievent = &ievent_buffer.event;
4269
const char dummy_file_name[] = "ask.dummy_file_name";
4270
ievent->mask = IN_MOVED_TO;
4271
ievent->len = sizeof(dummy_file_name);
4272
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4273
const size_t ievent_size = (sizeof(struct inotify_event)
4274
+ sizeof(dummy_file_name));
4275
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4277
g_assert_cmpint(close(pipefds[1]), ==, 0);
4279
bool quit_now = false;
4280
buffer password = {};
4281
bool mandos_client_exited = false;
4282
bool password_is_read = false;
4283
__attribute__((cleanup(cleanup_queue)))
4284
task_queue *queue = create_queue();
4285
g_assert_nonnull(queue);
4287
task_context task = {
4288
.func=read_inotify_event,
4291
.quit_now=&quit_now,
4292
.password=&password,
4293
.filename=strdup("/nonexistent"),
4294
.cancelled_filenames = &(string_set){},
4296
.current_time=¤t_time,
4297
.mandos_client_exited=&mandos_client_exited,
4298
.password_is_read=&password_is_read,
4300
task.func(task, queue);
4301
g_assert_false(quit_now);
4302
g_assert_true(queue->next_run != 0);
4303
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4305
g_assert_nonnull(find_matching_task(queue, (task_context){
4306
.func=read_inotify_event,
4309
.quit_now=&quit_now,
4310
.password=&password,
4311
.filename=task.filename,
4312
.cancelled_filenames=task.cancelled_filenames,
4313
.current_time=¤t_time,
4314
.mandos_client_exited=&mandos_client_exited,
4315
.password_is_read=&password_is_read,
4318
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4319
EPOLLIN | EPOLLRDHUP));
4321
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4323
__attribute__((cleanup(cleanup_string)))
4324
char *filename = NULL;
4325
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4326
dummy_file_name), >, 0);
4327
g_assert_nonnull(filename);
4328
g_assert_nonnull(find_matching_task(queue, (task_context){
4329
.func=open_and_parse_question,
4332
.question_filename=filename,
4333
.password=&password,
4334
.cancelled_filenames=task.cancelled_filenames,
4335
.current_time=¤t_time,
4336
.mandos_client_exited=&mandos_client_exited,
4337
.password_is_read=&password_is_read,
4342
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4343
test_fixture *fixture,
4344
__attribute__((unused))
4345
gconstpointer user_data){
4346
__attribute__((cleanup(cleanup_close)))
4347
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4348
g_assert_cmpint(epoll_fd, >=, 0);
4349
__attribute__((cleanup(string_set_clear)))
4350
string_set cancelled_filenames = {};
4351
const mono_microsecs current_time = 0;
4354
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4356
/* "sufficient to read at least one event." - inotify(7) */
4357
const size_t ievent_max_size = (sizeof(struct inotify_event)
4359
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4361
struct inotify_event event;
4362
char name_buffer[NAME_MAX + 1];
4364
struct inotify_event *const ievent = &ievent_buffer.event;
4366
const char dummy_file_name[] = "ask.dummy_file_name";
4367
ievent->mask = IN_MOVED_FROM;
4368
ievent->len = sizeof(dummy_file_name);
4369
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4370
const size_t ievent_size = (sizeof(struct inotify_event)
4371
+ sizeof(dummy_file_name));
4372
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4374
g_assert_cmpint(close(pipefds[1]), ==, 0);
4376
bool quit_now = false;
4377
buffer password = {};
4378
bool mandos_client_exited = false;
4379
bool password_is_read = false;
4380
__attribute__((cleanup(cleanup_queue)))
4381
task_queue *queue = create_queue();
4382
g_assert_nonnull(queue);
4384
task_context task = {
4385
.func=read_inotify_event,
4388
.quit_now=&quit_now,
4389
.password=&password,
4390
.filename=strdup("/nonexistent"),
4391
.cancelled_filenames=&cancelled_filenames,
4392
.current_time=¤t_time,
4393
.mandos_client_exited=&mandos_client_exited,
4394
.password_is_read=&password_is_read,
4396
task.func(task, queue);
4397
g_assert_false(quit_now);
4398
g_assert_true(queue->next_run == 0);
4399
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4401
g_assert_nonnull(find_matching_task(queue, (task_context){
4402
.func=read_inotify_event,
4405
.quit_now=&quit_now,
4406
.password=&password,
4407
.filename=task.filename,
4408
.cancelled_filenames=&cancelled_filenames,
4409
.current_time=¤t_time,
4410
.mandos_client_exited=&mandos_client_exited,
4411
.password_is_read=&password_is_read,
4414
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4415
EPOLLIN | EPOLLRDHUP));
4417
__attribute__((cleanup(cleanup_string)))
4418
char *filename = NULL;
4419
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4420
dummy_file_name), >, 0);
4421
g_assert_nonnull(filename);
4422
g_assert_true(string_set_contains(*task.cancelled_filenames,
4426
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4427
test_fixture *fixture,
4428
__attribute__((unused))
4431
__attribute__((cleanup(cleanup_close)))
4432
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4433
g_assert_cmpint(epoll_fd, >=, 0);
4434
__attribute__((cleanup(string_set_clear)))
4435
string_set cancelled_filenames = {};
4436
const mono_microsecs current_time = 0;
4439
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4441
/* "sufficient to read at least one event." - inotify(7) */
4442
const size_t ievent_max_size = (sizeof(struct inotify_event)
4444
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4446
struct inotify_event event;
4447
char name_buffer[NAME_MAX + 1];
4449
struct inotify_event *const ievent = &ievent_buffer.event;
4451
const char dummy_file_name[] = "ask.dummy_file_name";
4452
ievent->mask = IN_DELETE;
4453
ievent->len = sizeof(dummy_file_name);
4454
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4455
const size_t ievent_size = (sizeof(struct inotify_event)
4456
+ sizeof(dummy_file_name));
4457
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4459
g_assert_cmpint(close(pipefds[1]), ==, 0);
4461
bool quit_now = false;
4462
buffer password = {};
4463
bool mandos_client_exited = false;
4464
bool password_is_read = false;
4465
__attribute__((cleanup(cleanup_queue)))
4466
task_queue *queue = create_queue();
4467
g_assert_nonnull(queue);
4469
task_context task = {
4470
.func=read_inotify_event,
4473
.quit_now=&quit_now,
4474
.password=&password,
4475
.filename=strdup("/nonexistent"),
4476
.cancelled_filenames=&cancelled_filenames,
4477
.current_time=¤t_time,
4478
.mandos_client_exited=&mandos_client_exited,
4479
.password_is_read=&password_is_read,
4481
task.func(task, queue);
4482
g_assert_false(quit_now);
4483
g_assert_true(queue->next_run == 0);
4484
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4486
g_assert_nonnull(find_matching_task(queue, (task_context){
4487
.func=read_inotify_event,
4490
.quit_now=&quit_now,
4491
.password=&password,
4492
.filename=task.filename,
4493
.cancelled_filenames=&cancelled_filenames,
4494
.current_time=¤t_time,
4495
.mandos_client_exited=&mandos_client_exited,
4496
.password_is_read=&password_is_read,
4499
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4500
EPOLLIN | EPOLLRDHUP));
4502
__attribute__((cleanup(cleanup_string)))
4503
char *filename = NULL;
4504
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4505
dummy_file_name), >, 0);
4506
g_assert_nonnull(filename);
4507
g_assert_true(string_set_contains(*task.cancelled_filenames,
4512
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4513
test_fixture *fixture,
4514
__attribute__((unused))
4517
__attribute__((cleanup(cleanup_close)))
4518
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4519
g_assert_cmpint(epoll_fd, >=, 0);
4520
const mono_microsecs current_time = 0;
4523
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4525
/* "sufficient to read at least one event." - inotify(7) */
4526
const size_t ievent_max_size = (sizeof(struct inotify_event)
4528
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4530
struct inotify_event event;
4531
char name_buffer[NAME_MAX + 1];
4533
struct inotify_event *const ievent = &ievent_buffer.event;
4535
const char dummy_file_name[] = "ignored.dummy_file_name";
4536
ievent->mask = IN_CLOSE_WRITE;
4537
ievent->len = sizeof(dummy_file_name);
4538
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4539
const size_t ievent_size = (sizeof(struct inotify_event)
4540
+ sizeof(dummy_file_name));
4541
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4543
g_assert_cmpint(close(pipefds[1]), ==, 0);
4545
bool quit_now = false;
4546
buffer password = {};
4547
bool mandos_client_exited = false;
4548
bool password_is_read = false;
4549
__attribute__((cleanup(cleanup_queue)))
4550
task_queue *queue = create_queue();
4551
g_assert_nonnull(queue);
4553
task_context task = {
4554
.func=read_inotify_event,
4557
.quit_now=&quit_now,
4558
.password=&password,
4559
.filename=strdup("/nonexistent"),
4560
.cancelled_filenames = &(string_set){},
4562
.current_time=¤t_time,
4563
.mandos_client_exited=&mandos_client_exited,
4564
.password_is_read=&password_is_read,
4566
task.func(task, queue);
4567
g_assert_false(quit_now);
4568
g_assert_true(queue->next_run == 0);
4569
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4571
g_assert_nonnull(find_matching_task(queue, (task_context){
4572
.func=read_inotify_event,
4575
.quit_now=&quit_now,
4576
.password=&password,
4577
.filename=task.filename,
4578
.cancelled_filenames=task.cancelled_filenames,
4579
.current_time=¤t_time,
4580
.mandos_client_exited=&mandos_client_exited,
4581
.password_is_read=&password_is_read,
4584
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4585
EPOLLIN | EPOLLRDHUP));
4589
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4590
test_fixture *fixture,
4591
__attribute__((unused))
4592
gconstpointer user_data){
4593
__attribute__((cleanup(cleanup_close)))
4594
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4595
g_assert_cmpint(epoll_fd, >=, 0);
4596
const mono_microsecs current_time = 0;
4599
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4601
/* "sufficient to read at least one event." - inotify(7) */
4602
const size_t ievent_max_size = (sizeof(struct inotify_event)
4604
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4606
struct inotify_event event;
4607
char name_buffer[NAME_MAX + 1];
4609
struct inotify_event *const ievent = &ievent_buffer.event;
4611
const char dummy_file_name[] = "ignored.dummy_file_name";
4612
ievent->mask = IN_MOVED_TO;
4613
ievent->len = sizeof(dummy_file_name);
4614
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4615
const size_t ievent_size = (sizeof(struct inotify_event)
4616
+ sizeof(dummy_file_name));
4617
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4619
g_assert_cmpint(close(pipefds[1]), ==, 0);
4621
bool quit_now = false;
4622
buffer password = {};
4623
bool mandos_client_exited = false;
4624
bool password_is_read = false;
4625
__attribute__((cleanup(cleanup_queue)))
4626
task_queue *queue = create_queue();
4627
g_assert_nonnull(queue);
4629
task_context task = {
4630
.func=read_inotify_event,
4633
.quit_now=&quit_now,
4634
.password=&password,
4635
.filename=strdup("/nonexistent"),
4636
.cancelled_filenames = &(string_set){},
4638
.current_time=¤t_time,
4639
.mandos_client_exited=&mandos_client_exited,
4640
.password_is_read=&password_is_read,
4642
task.func(task, queue);
4643
g_assert_false(quit_now);
4644
g_assert_true(queue->next_run == 0);
4645
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4647
g_assert_nonnull(find_matching_task(queue, (task_context){
4648
.func=read_inotify_event,
4651
.quit_now=&quit_now,
4652
.password=&password,
4653
.filename=task.filename,
4654
.cancelled_filenames=task.cancelled_filenames,
4655
.current_time=¤t_time,
4656
.mandos_client_exited=&mandos_client_exited,
4657
.password_is_read=&password_is_read,
4660
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4661
EPOLLIN | EPOLLRDHUP));
4665
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4666
test_fixture *fixture,
4667
__attribute__((unused))
4670
__attribute__((cleanup(cleanup_close)))
4671
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4672
g_assert_cmpint(epoll_fd, >=, 0);
4673
__attribute__((cleanup(string_set_clear)))
4674
string_set cancelled_filenames = {};
4675
const mono_microsecs current_time = 0;
4678
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4680
/* "sufficient to read at least one event." - inotify(7) */
4681
const size_t ievent_max_size = (sizeof(struct inotify_event)
4683
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4685
struct inotify_event event;
4686
char name_buffer[NAME_MAX + 1];
4688
struct inotify_event *const ievent = &ievent_buffer.event;
4690
const char dummy_file_name[] = "ignored.dummy_file_name";
4691
ievent->mask = IN_MOVED_FROM;
4692
ievent->len = sizeof(dummy_file_name);
4693
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4694
const size_t ievent_size = (sizeof(struct inotify_event)
4695
+ sizeof(dummy_file_name));
4696
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4698
g_assert_cmpint(close(pipefds[1]), ==, 0);
4700
bool quit_now = false;
4701
buffer password = {};
4702
bool mandos_client_exited = false;
4703
bool password_is_read = false;
4704
__attribute__((cleanup(cleanup_queue)))
4705
task_queue *queue = create_queue();
4706
g_assert_nonnull(queue);
4708
task_context task = {
4709
.func=read_inotify_event,
4712
.quit_now=&quit_now,
4713
.password=&password,
4714
.filename=strdup("/nonexistent"),
4715
.cancelled_filenames=&cancelled_filenames,
4716
.current_time=¤t_time,
4717
.mandos_client_exited=&mandos_client_exited,
4718
.password_is_read=&password_is_read,
4720
task.func(task, queue);
4721
g_assert_false(quit_now);
4722
g_assert_true(queue->next_run == 0);
4723
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4725
g_assert_nonnull(find_matching_task(queue, (task_context){
4726
.func=read_inotify_event,
4729
.quit_now=&quit_now,
4730
.password=&password,
4731
.filename=task.filename,
4732
.cancelled_filenames=&cancelled_filenames,
4733
.current_time=¤t_time,
4734
.mandos_client_exited=&mandos_client_exited,
4735
.password_is_read=&password_is_read,
4738
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4739
EPOLLIN | EPOLLRDHUP));
4741
__attribute__((cleanup(cleanup_string)))
4742
char *filename = NULL;
4743
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4744
dummy_file_name), >, 0);
4745
g_assert_nonnull(filename);
4746
g_assert_false(string_set_contains(cancelled_filenames, filename));
4750
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4751
test_fixture *fixture,
4752
__attribute__((unused))
4755
__attribute__((cleanup(cleanup_close)))
4756
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4757
g_assert_cmpint(epoll_fd, >=, 0);
4758
__attribute__((cleanup(string_set_clear)))
4759
string_set cancelled_filenames = {};
4760
const mono_microsecs current_time = 0;
4763
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4765
/* "sufficient to read at least one event." - inotify(7) */
4766
const size_t ievent_max_size = (sizeof(struct inotify_event)
4768
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4770
struct inotify_event event;
4771
char name_buffer[NAME_MAX + 1];
4773
struct inotify_event *const ievent = &ievent_buffer.event;
4775
const char dummy_file_name[] = "ignored.dummy_file_name";
4776
ievent->mask = IN_DELETE;
4777
ievent->len = sizeof(dummy_file_name);
4778
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4779
const size_t ievent_size = (sizeof(struct inotify_event)
4780
+ sizeof(dummy_file_name));
4781
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4783
g_assert_cmpint(close(pipefds[1]), ==, 0);
4785
bool quit_now = false;
4786
buffer password = {};
4787
bool mandos_client_exited = false;
4788
bool password_is_read = false;
4789
__attribute__((cleanup(cleanup_queue)))
4790
task_queue *queue = create_queue();
4791
g_assert_nonnull(queue);
4793
task_context task = {
4794
.func=read_inotify_event,
4797
.quit_now=&quit_now,
4798
.password=&password,
4799
.filename=strdup("/nonexistent"),
4800
.cancelled_filenames=&cancelled_filenames,
4801
.current_time=¤t_time,
4802
.mandos_client_exited=&mandos_client_exited,
4803
.password_is_read=&password_is_read,
4805
task.func(task, queue);
4806
g_assert_false(quit_now);
4807
g_assert_true(queue->next_run == 0);
4808
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4810
g_assert_nonnull(find_matching_task(queue, (task_context){
4811
.func=read_inotify_event,
4814
.quit_now=&quit_now,
4815
.password=&password,
4816
.filename=task.filename,
4817
.cancelled_filenames=&cancelled_filenames,
4818
.current_time=¤t_time,
4819
.mandos_client_exited=&mandos_client_exited,
4820
.password_is_read=&password_is_read,
4823
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4824
EPOLLIN | EPOLLRDHUP));
4826
__attribute__((cleanup(cleanup_string)))
4827
char *filename = NULL;
4828
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4829
dummy_file_name), >, 0);
4830
g_assert_nonnull(filename);
4831
g_assert_false(string_set_contains(cancelled_filenames, filename));
4835
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4836
test_fixture *fixture,
4837
__attribute__((unused))
4838
gconstpointer user_data){
4839
__attribute__((cleanup(cleanup_close)))
4840
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4841
g_assert_cmpint(epoll_fd, >=, 0);
4842
__attribute__((cleanup(string_set_clear)))
4843
string_set cancelled_filenames = {};
4844
bool mandos_client_exited = false;
4845
bool password_is_read = false;
4846
__attribute__((cleanup(cleanup_queue)))
4847
task_queue *queue = create_queue();
4848
g_assert_nonnull(queue);
4850
char *const filename = strdup("/nonexistent");
4851
g_assert_nonnull(filename);
4852
task_context task = {
4853
.func=open_and_parse_question,
4854
.question_filename=filename,
4856
.password=(buffer[]){{}},
4858
.cancelled_filenames=&cancelled_filenames,
4859
.current_time=(mono_microsecs[]){0},
4860
.mandos_client_exited=&mandos_client_exited,
4861
.password_is_read=&password_is_read,
4863
task.func(task, queue);
4864
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4867
static void test_open_and_parse_question_EIO(__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
buffer password = {};
4877
bool mandos_client_exited = false;
4878
bool password_is_read = false;
4879
__attribute__((cleanup(cleanup_queue)))
4880
task_queue *queue = create_queue();
4881
g_assert_nonnull(queue);
4882
const mono_microsecs current_time = 0;
4884
char *filename = strdup("/proc/self/mem");
4885
task_context task = {
4886
.func=open_and_parse_question,
4887
.question_filename=filename,
4889
.password=&password,
4891
.cancelled_filenames=&cancelled_filenames,
4892
.current_time=¤t_time,
4893
.mandos_client_exited=&mandos_client_exited,
4894
.password_is_read=&password_is_read,
4896
run_task_with_stderr_to_dev_null(task, queue);
4897
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4901
test_open_and_parse_question_parse_error(__attribute__((unused))
4902
test_fixture *fixture,
4903
__attribute__((unused))
4904
gconstpointer user_data){
4905
__attribute__((cleanup(cleanup_close)))
4906
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4907
g_assert_cmpint(epoll_fd, >=, 0);
4908
__attribute__((cleanup(string_set_clear)))
4909
string_set cancelled_filenames = {};
4910
__attribute__((cleanup(cleanup_queue)))
4911
task_queue *queue = create_queue();
4912
g_assert_nonnull(queue);
4914
__attribute__((cleanup(cleanup_string)))
4915
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4916
g_assert_nonnull(tempfilename);
4917
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4918
g_assert_cmpint(tempfile, >, 0);
4919
const char bad_data[] = "this is bad syntax\n";
4920
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4921
==, sizeof(bad_data));
4922
g_assert_cmpint(close(tempfile), ==, 0);
4924
char *const filename = strdup(tempfilename);
4925
g_assert_nonnull(filename);
4926
task_context task = {
4927
.func=open_and_parse_question,
4928
.question_filename=filename,
4930
.password=(buffer[]){{}},
4932
.cancelled_filenames=&cancelled_filenames,
4933
.current_time=(mono_microsecs[]){0},
4934
.mandos_client_exited=(bool[]){false},
4935
.password_is_read=(bool[]){false},
4937
run_task_with_stderr_to_dev_null(task, queue);
4939
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4941
g_assert_cmpint(unlink(tempfilename), ==, 0);
4945
void test_open_and_parse_question_nosocket(__attribute__((unused))
4946
test_fixture *fixture,
4947
__attribute__((unused))
4948
gconstpointer user_data){
4949
__attribute__((cleanup(cleanup_close)))
4950
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4951
g_assert_cmpint(epoll_fd, >=, 0);
4952
__attribute__((cleanup(string_set_clear)))
4953
string_set cancelled_filenames = {};
4954
__attribute__((cleanup(cleanup_queue)))
4955
task_queue *queue = create_queue();
4956
g_assert_nonnull(queue);
4958
__attribute__((cleanup(cleanup_string)))
4959
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4960
g_assert_nonnull(tempfilename);
4961
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4962
g_assert_cmpint(questionfile, >, 0);
4963
FILE *qf = fdopen(questionfile, "w");
4964
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4965
g_assert_cmpint(fclose(qf), ==, 0);
4967
char *const filename = strdup(tempfilename);
4968
g_assert_nonnull(filename);
4969
task_context task = {
4970
.func=open_and_parse_question,
4971
.question_filename=filename,
4973
.password=(buffer[]){{}},
4975
.cancelled_filenames=&cancelled_filenames,
4976
.current_time=(mono_microsecs[]){0},
4977
.mandos_client_exited=(bool[]){false},
4978
.password_is_read=(bool[]){false},
4980
run_task_with_stderr_to_dev_null(task, queue);
4981
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4983
g_assert_cmpint(unlink(tempfilename), ==, 0);
4987
void test_open_and_parse_question_badsocket(__attribute__((unused))
4988
test_fixture *fixture,
4989
__attribute__((unused))
4990
gconstpointer user_data){
4991
__attribute__((cleanup(cleanup_close)))
4992
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4993
g_assert_cmpint(epoll_fd, >=, 0);
4994
__attribute__((cleanup(string_set_clear)))
4995
string_set cancelled_filenames = {};
4996
__attribute__((cleanup(cleanup_queue)))
4997
task_queue *queue = create_queue();
4998
g_assert_nonnull(queue);
5000
__attribute__((cleanup(cleanup_string)))
5001
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5002
g_assert_nonnull(tempfilename);
5003
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5004
g_assert_cmpint(questionfile, >, 0);
5005
FILE *qf = fdopen(questionfile, "w");
5006
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
5007
g_assert_cmpint(fclose(qf), ==, 0);
5009
char *const filename = strdup(tempfilename);
5010
g_assert_nonnull(filename);
5011
task_context task = {
5012
.func=open_and_parse_question,
5013
.question_filename=filename,
5015
.password=(buffer[]){{}},
5017
.cancelled_filenames=&cancelled_filenames,
5018
.current_time=(mono_microsecs[]){0},
5019
.mandos_client_exited=(bool[]){false},
5020
.password_is_read=(bool[]){false},
5022
run_task_with_stderr_to_dev_null(task, queue);
5023
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5025
g_assert_cmpint(unlink(tempfilename), ==, 0);
5029
void test_open_and_parse_question_nopid(__attribute__((unused))
5030
test_fixture *fixture,
5031
__attribute__((unused))
5032
gconstpointer user_data){
5033
__attribute__((cleanup(cleanup_close)))
5034
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5035
g_assert_cmpint(epoll_fd, >=, 0);
5036
__attribute__((cleanup(string_set_clear)))
5037
string_set cancelled_filenames = {};
5038
__attribute__((cleanup(cleanup_queue)))
5039
task_queue *queue = create_queue();
5040
g_assert_nonnull(queue);
5042
__attribute__((cleanup(cleanup_string)))
5043
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5044
g_assert_nonnull(tempfilename);
5045
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5046
g_assert_cmpint(questionfile, >, 0);
5047
FILE *qf = fdopen(questionfile, "w");
5048
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5049
g_assert_cmpint(fclose(qf), ==, 0);
5051
char *const filename = strdup(tempfilename);
5052
g_assert_nonnull(filename);
5053
task_context task = {
5054
.func=open_and_parse_question,
5055
.question_filename=filename,
5057
.password=(buffer[]){{}},
5059
.cancelled_filenames=&cancelled_filenames,
5060
.current_time=(mono_microsecs[]){0},
5061
.mandos_client_exited=(bool[]){false},
5062
.password_is_read=(bool[]){false},
5064
run_task_with_stderr_to_dev_null(task, queue);
5065
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5067
g_assert_cmpint(unlink(tempfilename), ==, 0);
5071
void test_open_and_parse_question_badpid(__attribute__((unused))
5072
test_fixture *fixture,
5073
__attribute__((unused))
5074
gconstpointer user_data){
5075
__attribute__((cleanup(cleanup_close)))
5076
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5077
g_assert_cmpint(epoll_fd, >=, 0);
5078
__attribute__((cleanup(string_set_clear)))
5079
string_set cancelled_filenames = {};
5080
__attribute__((cleanup(cleanup_queue)))
5081
task_queue *queue = create_queue();
5082
g_assert_nonnull(queue);
5084
__attribute__((cleanup(cleanup_string)))
5085
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5086
g_assert_nonnull(tempfilename);
5087
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5088
g_assert_cmpint(questionfile, >, 0);
5089
FILE *qf = fdopen(questionfile, "w");
5090
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5092
g_assert_cmpint(fclose(qf), ==, 0);
5094
char *const filename = strdup(tempfilename);
5095
g_assert_nonnull(filename);
5096
task_context task = {
5097
.func=open_and_parse_question,
5098
.question_filename=filename,
5100
.password=(buffer[]){{}},
5102
.cancelled_filenames=&cancelled_filenames,
5103
.current_time=(mono_microsecs[]){0},
5104
.mandos_client_exited=(bool[]){false},
5105
.password_is_read=(bool[]){false},
5107
run_task_with_stderr_to_dev_null(task, queue);
5108
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5110
g_assert_cmpint(unlink(tempfilename), ==, 0);
5114
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5115
test_fixture *fixture,
5116
__attribute__((unused))
5117
gconstpointer user_data){
5118
__attribute__((cleanup(cleanup_close)))
5119
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5120
g_assert_cmpint(epoll_fd, >=, 0);
5121
__attribute__((cleanup(string_set_clear)))
5122
string_set cancelled_filenames = {};
5123
buffer password = {};
5124
bool mandos_client_exited = false;
5125
bool password_is_read = false;
5126
__attribute__((cleanup(cleanup_queue)))
5127
task_queue *queue = create_queue();
5128
g_assert_nonnull(queue);
5129
const mono_microsecs current_time = 0;
5131
/* Find value of sysctl kernel.pid_max */
5132
uintmax_t pid_max = 0;
5133
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5134
g_assert_nonnull(sysctl_pid_max);
5135
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5137
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5139
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5140
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5142
__attribute__((cleanup(cleanup_string)))
5143
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5144
g_assert_nonnull(tempfilename);
5145
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5146
g_assert_cmpint(questionfile, >, 0);
5147
FILE *qf = fdopen(questionfile, "w");
5148
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5149
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5151
g_assert_cmpint(fclose(qf), ==, 0);
5153
char *const question_filename = strdup(tempfilename);
5154
g_assert_nonnull(question_filename);
5155
task_context task = {
5156
.func=open_and_parse_question,
5157
.question_filename=question_filename,
5159
.password=&password,
5160
.filename=question_filename,
5161
.cancelled_filenames=&cancelled_filenames,
5162
.current_time=¤t_time,
5163
.mandos_client_exited=&mandos_client_exited,
5164
.password_is_read=&password_is_read,
5166
run_task_with_stderr_to_dev_null(task, queue);
5167
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5169
g_assert_cmpint(unlink(tempfilename), ==, 0);
5173
test_open_and_parse_question_no_notafter(__attribute__((unused))
5174
test_fixture *fixture,
5175
__attribute__((unused))
5176
gconstpointer user_data){
5177
__attribute__((cleanup(cleanup_close)))
5178
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5179
g_assert_cmpint(epoll_fd, >=, 0);
5180
__attribute__((cleanup(string_set_clear)))
5181
string_set cancelled_filenames = {};
5182
buffer password = {};
5183
bool mandos_client_exited = false;
5184
bool password_is_read = false;
5185
__attribute__((cleanup(cleanup_queue)))
5186
task_queue *queue = create_queue();
5187
g_assert_nonnull(queue);
5188
const mono_microsecs current_time = 0;
5190
__attribute__((cleanup(cleanup_string)))
5191
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5192
g_assert_nonnull(tempfilename);
5193
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5194
g_assert_cmpint(questionfile, >, 0);
5195
FILE *qf = fdopen(questionfile, "w");
5196
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5197
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5198
g_assert_cmpint(fclose(qf), ==, 0);
5200
char *const filename = strdup(tempfilename);
5201
g_assert_nonnull(filename);
5202
task_context task = {
5203
.func=open_and_parse_question,
5204
.question_filename=filename,
5206
.password=&password,
5208
.cancelled_filenames=&cancelled_filenames,
5209
.current_time=¤t_time,
5210
.mandos_client_exited=&mandos_client_exited,
5211
.password_is_read=&password_is_read,
5213
task.func(task, queue);
5214
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5216
__attribute__((cleanup(cleanup_string)))
5217
char *socket_filename = strdup("/nonexistent");
5218
g_assert_nonnull(socket_filename);
5219
g_assert_nonnull(find_matching_task(queue, (task_context){
5220
.func=connect_question_socket,
5221
.question_filename=tempfilename,
5222
.filename=socket_filename,
5224
.password=&password,
5225
.current_time=¤t_time,
5226
.mandos_client_exited=&mandos_client_exited,
5227
.password_is_read=&password_is_read,
5230
g_assert_true(queue->next_run != 0);
5232
g_assert_cmpint(unlink(tempfilename), ==, 0);
5236
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5237
test_fixture *fixture,
5238
__attribute__((unused))
5239
gconstpointer user_data){
5240
__attribute__((cleanup(cleanup_close)))
5241
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5242
g_assert_cmpint(epoll_fd, >=, 0);
5243
__attribute__((cleanup(string_set_clear)))
5244
string_set cancelled_filenames = {};
5245
buffer password = {};
5246
bool mandos_client_exited = false;
5247
bool password_is_read = false;
5248
__attribute__((cleanup(cleanup_queue)))
5249
task_queue *queue = create_queue();
5250
g_assert_nonnull(queue);
5251
const mono_microsecs current_time = 0;
5253
__attribute__((cleanup(cleanup_string)))
5254
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5255
g_assert_nonnull(tempfilename);
5256
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5257
g_assert_cmpint(questionfile, >, 0);
5258
FILE *qf = fdopen(questionfile, "w");
5259
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5260
PRIuMAX "\nNotAfter=\n",
5261
(uintmax_t)getpid()), >, 0);
5262
g_assert_cmpint(fclose(qf), ==, 0);
5264
char *const filename = strdup(tempfilename);
5265
g_assert_nonnull(filename);
5266
task_context task = {
5267
.func=open_and_parse_question,
5268
.question_filename=filename,
5270
.password=&password,
5272
.cancelled_filenames=&cancelled_filenames,
5273
.current_time=¤t_time,
5274
.mandos_client_exited=&mandos_client_exited,
5275
.password_is_read=&password_is_read,
5277
run_task_with_stderr_to_dev_null(task, queue);
5278
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5280
__attribute__((cleanup(cleanup_string)))
5281
char *socket_filename = strdup("/nonexistent");
5282
g_assert_nonnull(find_matching_task(queue, (task_context){
5283
.func=connect_question_socket,
5284
.question_filename=tempfilename,
5285
.filename=socket_filename,
5287
.password=&password,
5288
.current_time=¤t_time,
5289
.mandos_client_exited=&mandos_client_exited,
5290
.password_is_read=&password_is_read,
5292
g_assert_true(queue->next_run != 0);
5294
g_assert_cmpint(unlink(tempfilename), ==, 0);
5298
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5300
const mono_microsecs
5302
const mono_microsecs
5304
__attribute__((cleanup(cleanup_close)))
5305
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5306
g_assert_cmpint(epoll_fd, >=, 0);
5307
__attribute__((cleanup(string_set_clear)))
5308
string_set cancelled_filenames = {};
5309
buffer password = {};
5310
bool mandos_client_exited = false;
5311
bool password_is_read = false;
5312
__attribute__((cleanup(cleanup_queue)))
5313
task_queue *queue = create_queue();
5314
g_assert_nonnull(queue);
5315
queue->next_run = next_queue_run;
5317
__attribute__((cleanup(cleanup_string)))
5318
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5319
g_assert_nonnull(tempfilename);
5320
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5321
g_assert_cmpint(questionfile, >, 0);
5322
FILE *qf = fdopen(questionfile, "w");
5323
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5324
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5325
(uintmax_t)getpid(), notafter), >, 0);
5326
g_assert_cmpint(fclose(qf), ==, 0);
5328
char *const filename = strdup(tempfilename);
5329
g_assert_nonnull(filename);
5330
task_context task = {
5331
.func=open_and_parse_question,
5332
.question_filename=filename,
5334
.password=&password,
5336
.cancelled_filenames=&cancelled_filenames,
5337
.current_time=¤t_time,
5338
.mandos_client_exited=&mandos_client_exited,
5339
.password_is_read=&password_is_read,
5341
task.func(task, queue);
5343
if(queue->length >= 1){
5344
__attribute__((cleanup(cleanup_string)))
5345
char *socket_filename = strdup("/nonexistent");
5346
g_assert_nonnull(find_matching_task(queue, (task_context){
5347
.func=connect_question_socket,
5348
.filename=socket_filename,
5350
.password=&password,
5351
.current_time=¤t_time,
5352
.cancelled_filenames=&cancelled_filenames,
5353
.mandos_client_exited=&mandos_client_exited,
5354
.password_is_read=&password_is_read,
5356
g_assert_true(queue->next_run != 0);
5360
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5361
} else if(current_time >= notafter) {
5362
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5364
g_assert_nonnull(find_matching_task(queue, (task_context){
5365
.func=cancel_old_question,
5366
.question_filename=tempfilename,
5367
.filename=tempfilename,
5369
.cancelled_filenames=&cancelled_filenames,
5370
.current_time=¤t_time,
5373
g_assert_true(queue->next_run == 1);
5375
g_assert_cmpint(unlink(tempfilename), ==, 0);
5379
test_open_and_parse_question_notafter_0(__attribute__((unused))
5380
test_fixture *fixture,
5381
__attribute__((unused))
5382
gconstpointer user_data){
5383
/* current_time, notafter, next_queue_run */
5384
assert_open_and_parse_question_with_notafter(0, 0, 0);
5388
test_open_and_parse_question_notafter_1(__attribute__((unused))
5389
test_fixture *fixture,
5390
__attribute__((unused))
5391
gconstpointer user_data){
5392
/* current_time, notafter, next_queue_run */
5393
assert_open_and_parse_question_with_notafter(0, 1, 0);
5397
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5398
test_fixture *fixture,
5399
__attribute__((unused))
5400
gconstpointer user_data){
5401
/* current_time, notafter, next_queue_run */
5402
assert_open_and_parse_question_with_notafter(0, 1, 1);
5406
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5407
test_fixture *fixture,
5408
__attribute__((unused))
5409
gconstpointer user_data){
5410
/* current_time, notafter, next_queue_run */
5411
assert_open_and_parse_question_with_notafter(0, 1, 2);
5415
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5416
test_fixture *fixture,
5417
__attribute__((unused))
5418
gconstpointer user_data){
5419
/* current_time, notafter, next_queue_run */
5420
assert_open_and_parse_question_with_notafter(1, 1, 0);
5424
test_open_and_parse_question_late_notafter(__attribute__((unused))
5425
test_fixture *fixture,
5426
__attribute__((unused))
5427
gconstpointer user_data){
5428
/* current_time, notafter, next_queue_run */
5429
assert_open_and_parse_question_with_notafter(2, 1, 0);
5432
static void assert_cancel_old_question_param(const mono_microsecs
5434
const mono_microsecs
5436
const mono_microsecs
5438
const mono_microsecs
5440
__attribute__((cleanup(string_set_clear)))
5441
string_set cancelled_filenames = {};
5442
__attribute__((cleanup(cleanup_queue)))
5443
task_queue *queue = create_queue();
5444
g_assert_nonnull(queue);
5445
queue->next_run = next_queue_run;
5447
char *const question_filename = strdup("/nonexistent");
5448
g_assert_nonnull(question_filename);
5449
task_context task = {
5450
.func=cancel_old_question,
5451
.question_filename=question_filename,
5452
.filename=question_filename,
5454
.cancelled_filenames=&cancelled_filenames,
5455
.current_time=¤t_time,
5457
task.func(task, queue);
5459
if(current_time >= notafter){
5460
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5461
g_assert_true(string_set_contains(cancelled_filenames,
5464
g_assert_nonnull(find_matching_task(queue, (task_context){
5465
.func=cancel_old_question,
5466
.question_filename=question_filename,
5467
.filename=question_filename,
5469
.cancelled_filenames=&cancelled_filenames,
5470
.current_time=¤t_time,
5473
g_assert_false(string_set_contains(cancelled_filenames,
5474
question_filename));
5476
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5477
(unsigned int)next_set_to);
5480
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5481
test_fixture *fixture,
5482
__attribute__((unused))
5483
gconstpointer user_data){
5484
/* next_queue_run unset,
5485
cancellation should happen because time has come,
5486
next_queue_run should be unchanged */
5487
/* next_queue_run, notafter, current_time, next_set_to */
5488
assert_cancel_old_question_param(0, 1, 2, 0);
5491
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5492
test_fixture *fixture,
5493
__attribute__((unused))
5494
gconstpointer user_data){
5495
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5496
and current_time is not yet notafter or greater,
5497
update value of next_queue_run to value of notafter */
5498
/* next_queue_run, notafter, current_time, next_set_to */
5499
assert_cancel_old_question_param(0, 2, 1, 2);
5502
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5503
test_fixture *fixture,
5504
__attribute__((unused))
5505
gconstpointer user_data){
5506
/* next_queue_run 1,
5507
cancellation should happen because time has come,
5508
next_queue_run should be unchanged */
5509
/* next_queue_run, notafter, current_time, next_set_to */
5510
assert_cancel_old_question_param(1, 2, 3, 1);
5513
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5514
test_fixture *fixture,
5515
__attribute__((unused))
5516
gconstpointer user_data){
5517
/* If next_queue_run is set,
5518
and current_time is not yet notafter or greater,
5519
and notafter is larger than next_queue_run
5520
next_queue_run should be unchanged */
5521
/* next_queue_run, notafter, current_time, next_set_to */
5522
assert_cancel_old_question_param(1, 3, 2, 1);
5525
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5526
test_fixture *fixture,
5527
__attribute__((unused))
5528
gconstpointer user_data){
5529
/* next_queue_run 2,
5530
cancellation should happen because time has come,
5531
next_queue_run should be unchanged */
5532
/* next_queue_run, notafter, current_time, next_set_to */
5533
assert_cancel_old_question_param(2, 1, 3, 2);
5536
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5537
test_fixture *fixture,
5538
__attribute__((unused))
5539
gconstpointer user_data){
5540
/* If next_queue_run is set,
5541
and current_time is not yet notafter or greater,
5542
and notafter is larger than next_queue_run
5543
next_queue_run should be unchanged */
5544
/* next_queue_run, notafter, current_time, next_set_to */
5545
assert_cancel_old_question_param(2, 3, 1, 2);
5548
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5549
test_fixture *fixture,
5550
__attribute__((unused))
5551
gconstpointer user_data){
5552
/* next_queue_run 3,
5553
cancellation should happen because time has come,
5554
next_queue_run should be unchanged */
5555
/* next_queue_run, notafter, current_time, next_set_to */
5556
assert_cancel_old_question_param(3, 1, 2, 3);
5559
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5560
test_fixture *fixture,
5561
__attribute__((unused))
5562
gconstpointer user_data){
5563
/* If next_queue_run is set,
5564
and current_time is not yet notafter or greater,
5565
and notafter is smaller than next_queue_run
5566
update value of next_queue_run to value of notafter */
5567
/* next_queue_run, notafter, current_time, next_set_to */
5568
assert_cancel_old_question_param(3, 2, 1, 2);
5572
test_connect_question_socket_name_too_long(__attribute__((unused))
5573
test_fixture *fixture,
5574
__attribute__((unused))
5575
gconstpointer user_data){
5576
__attribute__((cleanup(cleanup_close)))
5577
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5578
g_assert_cmpint(epoll_fd, >=, 0);
5579
const char question_filename[] = "/nonexistent/question";
5580
__attribute__((cleanup(string_set_clear)))
5581
string_set cancelled_filenames = {};
5582
__attribute__((cleanup(cleanup_queue)))
5583
task_queue *queue = create_queue();
5584
g_assert_nonnull(queue);
5585
__attribute__((cleanup(cleanup_string)))
5586
char *tempdir = make_temporary_directory();
5587
g_assert_nonnull(tempdir);
5588
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5589
char socket_name[sizeof(unix_socket.sun_path)];
5590
memset(socket_name, 'x', sizeof(socket_name));
5591
socket_name[sizeof(socket_name)-1] = '\0';
5592
char *filename = NULL;
5593
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5595
g_assert_nonnull(filename);
5597
task_context task = {
5598
.func=connect_question_socket,
5599
.question_filename=strdup(question_filename),
5601
.password=(buffer[]){{}},
5603
.cancelled_filenames=&cancelled_filenames,
5604
.mandos_client_exited=(bool[]){false},
5605
.password_is_read=(bool[]){false},
5606
.current_time=(mono_microsecs[]){0},
5608
g_assert_nonnull(task.question_filename);
5609
run_task_with_stderr_to_dev_null(task, queue);
5611
g_assert_true(string_set_contains(cancelled_filenames,
5612
question_filename));
5613
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5614
g_assert_true(queue->next_run == 0);
5616
g_assert_cmpint(rmdir(tempdir), ==, 0);
5620
void test_connect_question_socket_connect_fail(__attribute__((unused))
5621
test_fixture *fixture,
5622
__attribute__((unused))
5625
__attribute__((cleanup(cleanup_close)))
5626
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5627
g_assert_cmpint(epoll_fd, >=, 0);
5628
const char question_filename[] = "/nonexistent/question";
5629
__attribute__((cleanup(string_set_clear)))
5630
string_set cancelled_filenames = {};
5631
const mono_microsecs current_time = 3;
5632
__attribute__((cleanup(cleanup_queue)))
5633
task_queue *queue = create_queue();
5634
g_assert_nonnull(queue);
5635
__attribute__((cleanup(cleanup_string)))
5636
char *tempdir = make_temporary_directory();
5637
g_assert_nonnull(tempdir);
5638
char socket_name[] = "nonexistent";
5639
char *filename = NULL;
5640
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5642
g_assert_nonnull(filename);
5644
task_context task = {
5645
.func=connect_question_socket,
5646
.question_filename=strdup(question_filename),
5648
.password=(buffer[]){{}},
5650
.cancelled_filenames=&cancelled_filenames,
5651
.mandos_client_exited=(bool[]){false},
5652
.password_is_read=(bool[]){false},
5653
.current_time=¤t_time,
5655
g_assert_nonnull(task.question_filename);
5656
run_task_with_stderr_to_dev_null(task, queue);
5658
g_assert_nonnull(find_matching_task(queue, task));
5660
g_assert_false(string_set_contains(cancelled_filenames,
5661
question_filename));
5662
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5663
g_assert_true(queue->next_run == 1000000 + current_time);
5665
g_assert_cmpint(rmdir(tempdir), ==, 0);
5669
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5670
test_fixture *fixture,
5671
__attribute__((unused))
5672
gconstpointer user_data){
5673
__attribute__((cleanup(cleanup_close)))
5674
const int epoll_fd = open("/dev/null",
5675
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5676
__attribute__((cleanup(cleanup_string)))
5677
char *const question_filename = strdup("/nonexistent/question");
5678
g_assert_nonnull(question_filename);
5679
__attribute__((cleanup(string_set_clear)))
5680
string_set cancelled_filenames = {};
5681
const mono_microsecs current_time = 5;
5682
__attribute__((cleanup(cleanup_queue)))
5683
task_queue *queue = create_queue();
5684
g_assert_nonnull(queue);
5685
__attribute__((cleanup(cleanup_string)))
5686
char *tempdir = make_temporary_directory();
5687
g_assert_nonnull(tempdir);
5688
__attribute__((cleanup(cleanup_close)))
5689
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5690
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5691
g_assert_cmpint(sock_fd, >=, 0);
5692
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5693
const char socket_name[] = "socket_name";
5694
__attribute__((cleanup(cleanup_string)))
5695
char *filename = NULL;
5696
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5698
g_assert_nonnull(filename);
5699
g_assert_cmpint((int)strlen(filename), <,
5700
(int)sizeof(sock_name.sun_path));
5701
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5702
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5703
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5704
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5705
task_context task = {
5706
.func=connect_question_socket,
5707
.question_filename=strdup(question_filename),
5709
.password=(buffer[]){{}},
5710
.filename=strdup(filename),
5711
.cancelled_filenames=&cancelled_filenames,
5712
.mandos_client_exited=(bool[]){false},
5713
.password_is_read=(bool[]){false},
5714
.current_time=¤t_time,
5716
g_assert_nonnull(task.question_filename);
5717
run_task_with_stderr_to_dev_null(task, queue);
5719
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5720
const task_context *const added_task
5721
= find_matching_task(queue, task);
5722
g_assert_nonnull(added_task);
5723
g_assert_true(queue->next_run == 1000000 + current_time);
5725
g_assert_cmpint(unlink(filename), ==, 0);
5726
g_assert_cmpint(rmdir(tempdir), ==, 0);
5730
void test_connect_question_socket_usable(__attribute__((unused))
5731
test_fixture *fixture,
5732
__attribute__((unused))
5733
gconstpointer user_data){
5734
__attribute__((cleanup(cleanup_close)))
5735
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5736
g_assert_cmpint(epoll_fd, >=, 0);
5737
__attribute__((cleanup(cleanup_string)))
5738
char *const question_filename = strdup("/nonexistent/question");
5739
g_assert_nonnull(question_filename);
5740
__attribute__((cleanup(string_set_clear)))
5741
string_set cancelled_filenames = {};
5742
buffer password = {};
5743
bool mandos_client_exited = false;
5744
bool password_is_read = false;
5745
const mono_microsecs current_time = 0;
5746
__attribute__((cleanup(cleanup_queue)))
5747
task_queue *queue = create_queue();
5748
g_assert_nonnull(queue);
5749
__attribute__((cleanup(cleanup_string)))
5750
char *tempdir = make_temporary_directory();
5751
g_assert_nonnull(tempdir);
5752
__attribute__((cleanup(cleanup_close)))
5753
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5754
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5755
g_assert_cmpint(sock_fd, >=, 0);
5756
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5757
const char socket_name[] = "socket_name";
5758
__attribute__((cleanup(cleanup_string)))
5759
char *filename = NULL;
5760
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5762
g_assert_nonnull(filename);
5763
g_assert_cmpint((int)strlen(filename), <,
5764
(int)sizeof(sock_name.sun_path));
5765
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5766
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5767
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5768
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5769
task_context task = {
5770
.func=connect_question_socket,
5771
.question_filename=strdup(question_filename),
5773
.password=&password,
5774
.filename=strdup(filename),
5775
.cancelled_filenames=&cancelled_filenames,
5776
.mandos_client_exited=&mandos_client_exited,
5777
.password_is_read=&password_is_read,
5778
.current_time=¤t_time,
5780
g_assert_nonnull(task.question_filename);
5781
task.func(task, queue);
5783
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5784
const task_context *const added_task
5785
= find_matching_task(queue, (task_context){
5786
.func=send_password_to_socket,
5787
.question_filename=question_filename,
5790
.password=&password,
5791
.cancelled_filenames=&cancelled_filenames,
5792
.mandos_client_exited=&mandos_client_exited,
5793
.password_is_read=&password_is_read,
5794
.current_time=¤t_time,
5796
g_assert_nonnull(added_task);
5797
g_assert_cmpint(added_task->fd, >, 0);
5799
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5802
const int fd = added_task->fd;
5803
g_assert_cmpint(fd, >, 0);
5804
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5807
char write_data[PIPE_BUF];
5809
/* Construct test password buffer */
5810
/* Start with + since that is what the real procotol uses */
5811
write_data[0] = '+';
5812
/* Set a special character at string end just to mark the end */
5813
write_data[sizeof(write_data)-2] = 'y';
5814
/* Set NUL at buffer end, as suggested by the protocol */
5815
write_data[sizeof(write_data)-1] = '\0';
5816
/* Fill rest of password with 'x' */
5817
memset(write_data+1, 'x', sizeof(write_data)-3);
5818
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5819
MSG_NOSIGNAL), ==, sizeof(write_data));
5822
/* read from sock_fd */
5823
char read_data[sizeof(write_data)];
5824
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5825
==, sizeof(read_data));
5827
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5830
/* writing to sock_fd should fail */
5831
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5832
MSG_NOSIGNAL), <, 0);
5834
/* reading from fd should fail */
5835
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5836
MSG_NOSIGNAL), <, 0);
5838
g_assert_cmpint(unlink(filename), ==, 0);
5839
g_assert_cmpint(rmdir(tempdir), ==, 0);
5843
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5844
test_fixture *fixture,
5845
__attribute__((unused))
5848
__attribute__((cleanup(cleanup_close)))
5849
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5850
g_assert_cmpint(epoll_fd, >=, 0);
5851
__attribute__((cleanup(cleanup_string)))
5852
char *const question_filename = strdup("/nonexistent/question");
5853
g_assert_nonnull(question_filename);
5854
__attribute__((cleanup(cleanup_string)))
5855
char *const filename = strdup("/nonexistent/socket");
5856
g_assert_nonnull(filename);
5857
__attribute__((cleanup(string_set_clear)))
5858
string_set cancelled_filenames = {};
5859
buffer password = {};
5860
bool password_is_read = true;
5861
__attribute__((cleanup(cleanup_queue)))
5862
task_queue *queue = create_queue();
5863
g_assert_nonnull(queue);
5865
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5866
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5868
__attribute__((cleanup(cleanup_close)))
5869
const int read_socket = socketfds[0];
5870
const int write_socket = socketfds[1];
5871
task_context task = {
5872
.func=send_password_to_socket,
5873
.question_filename=strdup(question_filename),
5874
.filename=strdup(filename),
5877
.password=&password,
5878
.cancelled_filenames=&cancelled_filenames,
5879
.mandos_client_exited=(bool[]){false},
5880
.password_is_read=&password_is_read,
5881
.current_time=(mono_microsecs[]){0},
5883
g_assert_nonnull(task.question_filename);
5885
task.func(task, queue);
5887
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5889
const task_context *const added_task
5890
= find_matching_task(queue, task);
5891
g_assert_nonnull(added_task);
5892
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5893
g_assert_true(password_is_read);
5895
g_assert_cmpint(added_task->fd, >, 0);
5896
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5901
test_send_password_to_socket_password_not_read(__attribute__((unused))
5902
test_fixture *fixture,
5903
__attribute__((unused))
5906
__attribute__((cleanup(cleanup_close)))
5907
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5908
g_assert_cmpint(epoll_fd, >=, 0);
5909
__attribute__((cleanup(cleanup_string)))
5910
char *const question_filename = strdup("/nonexistent/question");
5911
g_assert_nonnull(question_filename);
5912
__attribute__((cleanup(cleanup_string)))
5913
char *const filename = strdup("/nonexistent/socket");
5914
__attribute__((cleanup(string_set_clear)))
5915
string_set cancelled_filenames = {};
5916
buffer password = {};
5917
__attribute__((cleanup(cleanup_queue)))
5918
task_queue *queue = create_queue();
5919
g_assert_nonnull(queue);
5921
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5922
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5924
__attribute__((cleanup(cleanup_close)))
5925
const int read_socket = socketfds[0];
5926
const int write_socket = socketfds[1];
5927
task_context task = {
5928
.func=send_password_to_socket,
5929
.question_filename=strdup(question_filename),
5930
.filename=strdup(filename),
5933
.password=&password,
5934
.cancelled_filenames=&cancelled_filenames,
5935
.mandos_client_exited=(bool[]){false},
5936
.password_is_read=(bool[]){false},
5937
.current_time=(mono_microsecs[]){0},
5939
g_assert_nonnull(task.question_filename);
5941
task.func(task, queue);
5943
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5945
const task_context *const added_task = find_matching_task(queue,
5947
g_assert_nonnull(added_task);
5948
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5949
g_assert_true(queue->next_run == 0);
5951
g_assert_cmpint(added_task->fd, >, 0);
5952
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5957
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5958
test_fixture *fixture,
5959
__attribute__((unused))
5960
gconstpointer user_data){
5962
g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
5964
__attribute__((cleanup(cleanup_close)))
5965
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5966
g_assert_cmpint(epoll_fd, >=, 0);
5967
const char question_filename[] = "/nonexistent/question";
5968
char *const filename = strdup("/nonexistent/socket");
5969
__attribute__((cleanup(string_set_clear)))
5970
string_set cancelled_filenames = {};
5971
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5972
__attribute__((cleanup(cleanup_buffer)))
5974
.data=malloc(oversized),
5976
.allocated=oversized,
5978
g_assert_nonnull(password.data);
5979
if(mlock(password.data, password.allocated) != 0){
5980
g_assert_true(errno == EPERM or errno == ENOMEM);
5982
/* Construct test password buffer */
5983
/* Start with + since that is what the real procotol uses */
5984
password.data[0] = '+';
5985
/* Set a special character at string end just to mark the end */
5986
password.data[oversized-3] = 'y';
5987
/* Set NUL at buffer end, as suggested by the protocol */
5988
password.data[oversized-2] = '\0';
5989
/* Fill rest of password with 'x' */
5990
memset(password.data+1, 'x', oversized-3);
5992
__attribute__((cleanup(cleanup_queue)))
5993
task_queue *queue = create_queue();
5994
g_assert_nonnull(queue);
5996
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5997
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5999
__attribute__((cleanup(cleanup_close)))
6000
const int read_socket = socketfds[0];
6001
__attribute__((cleanup(cleanup_close)))
6002
const int write_socket = socketfds[1];
6003
task_context task = {
6004
.func=send_password_to_socket,
6005
.question_filename=strdup(question_filename),
6009
.password=&password,
6010
.cancelled_filenames=&cancelled_filenames,
6011
.mandos_client_exited=(bool[]){true},
6012
.password_is_read=(bool[]){true},
6013
.current_time=(mono_microsecs[]){0},
6015
g_assert_nonnull(task.question_filename);
6017
run_task_with_stderr_to_dev_null(task, queue);
6019
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6020
g_assert_true(string_set_contains(cancelled_filenames,
6021
question_filename));
6025
static void test_send_password_to_socket_retry(__attribute__((unused))
6026
test_fixture *fixture,
6027
__attribute__((unused))
6030
__attribute__((cleanup(cleanup_close)))
6031
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6032
g_assert_cmpint(epoll_fd, >=, 0);
6033
__attribute__((cleanup(cleanup_string)))
6034
char *const question_filename = strdup("/nonexistent/question");
6035
g_assert_nonnull(question_filename);
6036
__attribute__((cleanup(cleanup_string)))
6037
char *const filename = strdup("/nonexistent/socket");
6038
g_assert_nonnull(filename);
6039
__attribute__((cleanup(string_set_clear)))
6040
string_set cancelled_filenames = {};
6041
__attribute__((cleanup(cleanup_buffer)))
6042
buffer password = {};
6044
__attribute__((cleanup(cleanup_queue)))
6045
task_queue *queue = create_queue();
6046
g_assert_nonnull(queue);
6048
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6049
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6051
__attribute__((cleanup(cleanup_close)))
6052
const int read_socket = socketfds[0];
6053
const int write_socket = socketfds[1];
6054
/* Close the server side socket to force ECONNRESET on client */
6055
g_assert_cmpint(close(read_socket), ==, 0);
6056
task_context task = {
6057
.func=send_password_to_socket,
6058
.question_filename=strdup(question_filename),
6059
.filename=strdup(filename),
6062
.password=&password,
6063
.cancelled_filenames=&cancelled_filenames,
6064
.mandos_client_exited=(bool[]){true},
6065
.password_is_read=(bool[]){true},
6066
.current_time=(mono_microsecs[]){0},
6068
g_assert_nonnull(task.question_filename);
6070
task.func(task, queue);
6072
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6074
const task_context *const added_task = find_matching_task(queue,
6076
g_assert_nonnull(added_task);
6077
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6079
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6084
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6085
test_fixture *fixture,
6086
__attribute__((unused))
6087
gconstpointer user_data){
6088
__attribute__((cleanup(cleanup_close)))
6089
const int epoll_fd = open("/dev/null",
6090
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6091
__attribute__((cleanup(cleanup_string)))
6092
char *const question_filename = strdup("/nonexistent/question");
6093
g_assert_nonnull(question_filename);
6094
__attribute__((cleanup(cleanup_string)))
6095
char *const filename = strdup("/nonexistent/socket");
6096
g_assert_nonnull(filename);
6097
__attribute__((cleanup(string_set_clear)))
6098
string_set cancelled_filenames = {};
6099
__attribute__((cleanup(cleanup_buffer)))
6100
buffer password = {};
6102
const mono_microsecs current_time = 11;
6103
__attribute__((cleanup(cleanup_queue)))
6104
task_queue *queue = create_queue();
6105
g_assert_nonnull(queue);
6107
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6108
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6110
__attribute__((cleanup(cleanup_close)))
6111
const int read_socket = socketfds[0];
6112
const int write_socket = socketfds[1];
6113
/* Close the server side socket to force ECONNRESET on client */
6114
g_assert_cmpint(close(read_socket), ==, 0);
6115
task_context task = {
6116
.func=send_password_to_socket,
6117
.question_filename=strdup(question_filename),
6118
.filename=strdup(filename),
6121
.password=&password,
6122
.cancelled_filenames=&cancelled_filenames,
6123
.mandos_client_exited=(bool[]){true},
6124
.password_is_read=(bool[]){true},
6125
.current_time=¤t_time,
6127
g_assert_nonnull(task.question_filename);
6129
run_task_with_stderr_to_dev_null(task, queue);
6131
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6133
const task_context *const added_task = find_matching_task(queue,
6135
g_assert_nonnull(added_task);
6136
g_assert_true(queue->next_run == current_time + 1000000);
6137
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6140
static void assert_send_password_to_socket_password(buffer password){
6141
__attribute__((cleanup(cleanup_close)))
6142
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6143
g_assert_cmpint(epoll_fd, >=, 0);
6144
char *const question_filename = strdup("/nonexistent/question");
6145
g_assert_nonnull(question_filename);
6146
char *const filename = strdup("/nonexistent/socket");
6147
g_assert_nonnull(filename);
6148
__attribute__((cleanup(string_set_clear)))
6149
string_set cancelled_filenames = {};
6151
__attribute__((cleanup(cleanup_queue)))
6152
task_queue *queue = create_queue();
6153
g_assert_nonnull(queue);
6155
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6156
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6158
__attribute__((cleanup(cleanup_close)))
6159
const int read_socket = socketfds[0];
6160
const int write_socket = socketfds[1];
6161
task_context task = {
6162
.func=send_password_to_socket,
6163
.question_filename=question_filename,
6167
.password=&password,
6168
.cancelled_filenames=&cancelled_filenames,
6169
.mandos_client_exited=(bool[]){true},
6170
.password_is_read=(bool[]){true},
6171
.current_time=(mono_microsecs[]){0},
6174
char *expected_written_data = malloc(password.length + 2);
6175
g_assert_nonnull(expected_written_data);
6176
expected_written_data[0] = '+';
6177
expected_written_data[password.length + 1] = '\0';
6178
if(password.length > 0){
6179
g_assert_nonnull(password.data);
6180
memcpy(expected_written_data + 1, password.data, password.length);
6183
task.func(task, queue);
6186
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6187
(int)(password.length + 2));
6188
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6190
g_assert_true(memcmp(expected_written_data, buf,
6191
password.length + 2) == 0);
6193
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6195
free(expected_written_data);
6199
test_send_password_to_socket_null_password(__attribute__((unused))
6200
test_fixture *fixture,
6201
__attribute__((unused))
6202
gconstpointer user_data){
6203
__attribute__((cleanup(cleanup_buffer)))
6204
buffer password = {};
6205
assert_send_password_to_socket_password(password);
6209
test_send_password_to_socket_empty_password(__attribute__((unused))
6210
test_fixture *fixture,
6211
__attribute__((unused))
6212
gconstpointer user_data){
6213
__attribute__((cleanup(cleanup_buffer)))
6215
.data=malloc(1), /* because malloc(0) may return NULL */
6217
.allocated=0, /* deliberate lie */
6219
g_assert_nonnull(password.data);
6220
assert_send_password_to_socket_password(password);
6224
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6225
test_fixture *fixture,
6226
__attribute__((unused))
6227
gconstpointer user_data){
6228
__attribute__((cleanup(cleanup_buffer)))
6234
if(mlock(password.data, password.allocated) != 0){
6235
g_assert_true(errno == EPERM or errno == ENOMEM);
6237
assert_send_password_to_socket_password(password);
6241
test_send_password_to_socket_text_password(__attribute__((unused))
6242
test_fixture *fixture,
6243
__attribute__((unused))
6244
gconstpointer user_data){
6245
const char dummy_test_password[] = "dummy test password";
6246
__attribute__((cleanup(cleanup_buffer)))
6248
.data = strdup(dummy_test_password),
6249
.length = strlen(dummy_test_password),
6250
.allocated = sizeof(dummy_test_password),
6252
if(mlock(password.data, password.allocated) != 0){
6253
g_assert_true(errno == EPERM or errno == ENOMEM);
6255
assert_send_password_to_socket_password(password);
6259
test_send_password_to_socket_binary_password(__attribute__((unused))
6260
test_fixture *fixture,
6261
__attribute__((unused))
6262
gconstpointer user_data){
6263
__attribute__((cleanup(cleanup_buffer)))
6269
g_assert_nonnull(password.data);
6270
if(mlock(password.data, password.allocated) != 0){
6271
g_assert_true(errno == EPERM or errno == ENOMEM);
6273
char c = 1; /* Start at 1, avoiding NUL */
6274
for(int i=0; i < 255; i++){
6275
password.data[i] = c++;
6277
assert_send_password_to_socket_password(password);
6281
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6282
test_fixture *fixture,
6283
__attribute__((unused))
6286
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6287
__attribute__((cleanup(cleanup_buffer)))
6289
.data=malloc(sizeof(test_password)),
6290
.length=sizeof(test_password),
6291
.allocated=sizeof(test_password),
6293
g_assert_nonnull(password.data);
6294
if(mlock(password.data, password.allocated) !=0){
6295
g_assert_true(errno == EPERM or errno == ENOMEM);
6297
memcpy(password.data, test_password, password.allocated);
6298
assert_send_password_to_socket_password(password);
6301
static bool assert_add_existing_questions_to_devnull(task_queue
6314
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6315
test_fixture *fixture,
6316
__attribute__((unused))
6319
__attribute__((cleanup(cleanup_queue)))
6320
task_queue *queue = create_queue();
6321
g_assert_nonnull(queue);
6322
__attribute__((cleanup(cleanup_close)))
6323
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6324
g_assert_cmpint(epoll_fd, >=, 0);
6325
__attribute__((cleanup(string_set_clear)))
6326
string_set cancelled_filenames = {};
6328
g_assert_false(assert_add_existing_questions_to_devnull
6331
(buffer[]){{}}, /* password */
6332
&cancelled_filenames,
6333
(mono_microsecs[]){0}, /* current_time */
6334
(bool[]){false}, /* mandos_client_exited */
6335
(bool[]){false}, /* password_is_read */
6336
"/nonexistent")); /* dirname */
6338
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6342
bool assert_add_existing_questions_to_devnull(task_queue
6349
*cancelled_filenames,
6350
const mono_microsecs
6351
*const current_time,
6353
mandos_client_exited,
6358
__attribute__((cleanup(cleanup_close)))
6359
const int devnull_fd = open("/dev/null",
6360
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6361
g_assert_cmpint(devnull_fd, >=, 0);
6362
__attribute__((cleanup(cleanup_close)))
6363
const int real_stderr_fd = dup(STDERR_FILENO);
6364
g_assert_cmpint(real_stderr_fd, >=, 0);
6365
dup2(devnull_fd, STDERR_FILENO);
6366
const bool ret = add_existing_questions(queue, epoll_fd, password,
6367
cancelled_filenames,
6369
mandos_client_exited,
6370
password_is_read, dirname);
6371
dup2(real_stderr_fd, STDERR_FILENO);
6376
void test_add_existing_questions_no_questions(__attribute__((unused))
6377
test_fixture *fixture,
6378
__attribute__((unused))
6381
__attribute__((cleanup(cleanup_queue)))
6382
task_queue *queue = create_queue();
6383
g_assert_nonnull(queue);
6384
__attribute__((cleanup(cleanup_close)))
6385
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6386
g_assert_cmpint(epoll_fd, >=, 0);
6387
__attribute__((cleanup(string_set_clear)))
6388
string_set cancelled_filenames = {};
6389
__attribute__((cleanup(cleanup_string)))
6390
char *tempdir = make_temporary_directory();
6391
g_assert_nonnull(tempdir);
6393
g_assert_false(assert_add_existing_questions_to_devnull
6396
(buffer[]){{}}, /* password */
6397
&cancelled_filenames,
6398
(mono_microsecs[]){0}, /* current_time */
6399
(bool[]){false}, /* mandos_client_exited */
6400
(bool[]){false}, /* password_is_read */
6403
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6405
g_assert_cmpint(rmdir(tempdir), ==, 0);
6408
static char *make_question_file_in_directory(const char *const);
6411
void test_add_existing_questions_one_question(__attribute__((unused))
6412
test_fixture *fixture,
6413
__attribute__((unused))
6416
__attribute__((cleanup(cleanup_queue)))
6417
task_queue *queue = create_queue();
6418
g_assert_nonnull(queue);
6419
__attribute__((cleanup(cleanup_close)))
6420
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6421
g_assert_cmpint(epoll_fd, >=, 0);
6422
__attribute__((cleanup(cleanup_buffer)))
6423
buffer password = {};
6424
__attribute__((cleanup(string_set_clear)))
6425
string_set cancelled_filenames = {};
6426
const mono_microsecs current_time = 0;
6427
bool mandos_client_exited = false;
6428
bool password_is_read = false;
6429
__attribute__((cleanup(cleanup_string)))
6430
char *tempdir = make_temporary_directory();
6431
g_assert_nonnull(tempdir);
6432
__attribute__((cleanup(cleanup_string)))
6433
char *question_filename
6434
= make_question_file_in_directory(tempdir);
6435
g_assert_nonnull(question_filename);
6437
g_assert_true(assert_add_existing_questions_to_devnull
6441
&cancelled_filenames,
6443
&mandos_client_exited,
6447
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6449
g_assert_nonnull(find_matching_task(queue, (task_context){
6450
.func=open_and_parse_question,
6452
.filename=question_filename,
6453
.question_filename=question_filename,
6454
.password=&password,
6455
.cancelled_filenames=&cancelled_filenames,
6456
.current_time=¤t_time,
6457
.mandos_client_exited=&mandos_client_exited,
6458
.password_is_read=&password_is_read,
6461
g_assert_true(queue->next_run == 1);
6463
g_assert_cmpint(unlink(question_filename), ==, 0);
6464
g_assert_cmpint(rmdir(tempdir), ==, 0);
6467
static char *make_question_file_in_directory(const char
6469
return make_temporary_prefixed_file_in_directory("ask.", dir);
6473
void test_add_existing_questions_two_questions(__attribute__((unused))
6474
test_fixture *fixture,
6475
__attribute__((unused))
6478
__attribute__((cleanup(cleanup_queue)))
6479
task_queue *queue = create_queue();
6480
g_assert_nonnull(queue);
6481
__attribute__((cleanup(cleanup_close)))
6482
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6483
g_assert_cmpint(epoll_fd, >=, 0);
6484
__attribute__((cleanup(cleanup_buffer)))
6485
buffer password = {};
6486
__attribute__((cleanup(string_set_clear)))
6487
string_set cancelled_filenames = {};
6488
const mono_microsecs current_time = 0;
6489
bool mandos_client_exited = false;
6490
bool password_is_read = false;
6491
__attribute__((cleanup(cleanup_string)))
6492
char *tempdir = make_temporary_directory();
6493
g_assert_nonnull(tempdir);
6494
__attribute__((cleanup(cleanup_string)))
6495
char *question_filename1
6496
= make_question_file_in_directory(tempdir);
6497
g_assert_nonnull(question_filename1);
6498
__attribute__((cleanup(cleanup_string)))
6499
char *question_filename2
6500
= make_question_file_in_directory(tempdir);
6501
g_assert_nonnull(question_filename2);
6503
g_assert_true(assert_add_existing_questions_to_devnull
6507
&cancelled_filenames,
6509
&mandos_client_exited,
6513
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6515
g_assert_true(queue->next_run == 1);
6517
__attribute__((cleanup(string_set_clear)))
6518
string_set seen_questions = {};
6520
bool queue_contains_question_opener(char *const question_filename){
6521
return(find_matching_task(queue, (task_context){
6522
.func=open_and_parse_question,
6524
.question_filename=question_filename,
6525
.password=&password,
6526
.cancelled_filenames=&cancelled_filenames,
6527
.current_time=¤t_time,
6528
.mandos_client_exited=&mandos_client_exited,
6529
.password_is_read=&password_is_read,
6533
g_assert_true(queue_contains_question_opener(question_filename1));
6534
g_assert_true(queue_contains_question_opener(question_filename2));
6536
g_assert_true(queue->next_run == 1);
6538
g_assert_cmpint(unlink(question_filename1), ==, 0);
6539
g_assert_cmpint(unlink(question_filename2), ==, 0);
6540
g_assert_cmpint(rmdir(tempdir), ==, 0);
6544
test_add_existing_questions_non_questions(__attribute__((unused))
6545
test_fixture *fixture,
6546
__attribute__((unused))
6547
gconstpointer user_data){
6548
__attribute__((cleanup(cleanup_queue)))
6549
task_queue *queue = create_queue();
6550
g_assert_nonnull(queue);
6551
__attribute__((cleanup(cleanup_close)))
6552
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6553
g_assert_cmpint(epoll_fd, >=, 0);
6554
__attribute__((cleanup(string_set_clear)))
6555
string_set cancelled_filenames = {};
6556
__attribute__((cleanup(cleanup_string)))
6557
char *tempdir = make_temporary_directory();
6558
g_assert_nonnull(tempdir);
6559
__attribute__((cleanup(cleanup_string)))
6560
char *question_filename1
6561
= make_temporary_file_in_directory(tempdir);
6562
g_assert_nonnull(question_filename1);
6563
__attribute__((cleanup(cleanup_string)))
6564
char *question_filename2
6565
= make_temporary_file_in_directory(tempdir);
6566
g_assert_nonnull(question_filename2);
6568
g_assert_false(assert_add_existing_questions_to_devnull
6571
(buffer[]){{}}, /* password */
6572
&cancelled_filenames,
6573
(mono_microsecs[]){0}, /* current_time */
6574
(bool[]){false}, /* mandos_client_exited */
6575
(bool[]){false}, /* password_is_read */
6578
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6580
g_assert_cmpint(unlink(question_filename1), ==, 0);
6581
g_assert_cmpint(unlink(question_filename2), ==, 0);
6582
g_assert_cmpint(rmdir(tempdir), ==, 0);
6586
test_add_existing_questions_both_types(__attribute__((unused))
6587
test_fixture *fixture,
6588
__attribute__((unused))
6589
gconstpointer user_data){
6590
__attribute__((cleanup(cleanup_queue)))
6591
task_queue *queue = create_queue();
6592
g_assert_nonnull(queue);
6593
__attribute__((cleanup(cleanup_close)))
6594
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6595
g_assert_cmpint(epoll_fd, >=, 0);
6596
__attribute__((cleanup(cleanup_buffer)))
6597
buffer password = {};
6598
__attribute__((cleanup(string_set_clear)))
6599
string_set cancelled_filenames = {};
6600
const mono_microsecs current_time = 0;
6601
bool mandos_client_exited = false;
6602
bool password_is_read = false;
6603
__attribute__((cleanup(cleanup_string)))
6604
char *tempdir = make_temporary_directory();
6605
g_assert_nonnull(tempdir);
6606
__attribute__((cleanup(cleanup_string)))
6607
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6608
g_assert_nonnull(tempfilename1);
6609
__attribute__((cleanup(cleanup_string)))
6610
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6611
g_assert_nonnull(tempfilename2);
6612
__attribute__((cleanup(cleanup_string)))
6613
char *question_filename
6614
= make_question_file_in_directory(tempdir);
6615
g_assert_nonnull(question_filename);
6617
g_assert_true(assert_add_existing_questions_to_devnull
6621
&cancelled_filenames,
6623
&mandos_client_exited,
6627
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6629
g_assert_nonnull(find_matching_task(queue, (task_context){
6630
.func=open_and_parse_question,
6632
.filename=question_filename,
6633
.question_filename=question_filename,
6634
.password=&password,
6635
.cancelled_filenames=&cancelled_filenames,
6636
.current_time=¤t_time,
6637
.mandos_client_exited=&mandos_client_exited,
6638
.password_is_read=&password_is_read,
6641
g_assert_true(queue->next_run == 1);
6643
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6644
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6645
g_assert_cmpint(unlink(question_filename), ==, 0);
6646
g_assert_cmpint(rmdir(tempdir), ==, 0);
6649
static void test_wait_for_event_timeout(__attribute__((unused))
6650
test_fixture *fixture,
6651
__attribute__((unused))
6652
gconstpointer user_data){
6653
__attribute__((cleanup(cleanup_close)))
6654
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6655
g_assert_cmpint(epoll_fd, >=, 0);
6657
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6660
static void test_wait_for_event_event(__attribute__((unused))
6661
test_fixture *fixture,
6662
__attribute__((unused))
6663
gconstpointer user_data){
6664
__attribute__((cleanup(cleanup_close)))
6665
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6666
g_assert_cmpint(epoll_fd, >=, 0);
6668
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6669
__attribute__((cleanup(cleanup_close)))
6670
const int read_pipe = pipefds[0];
6671
__attribute__((cleanup(cleanup_close)))
6672
const int write_pipe = pipefds[1];
6673
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6674
&(struct epoll_event)
6675
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6676
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6678
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6681
static void test_wait_for_event_sigchld(test_fixture *fixture,
6682
__attribute__((unused))
6683
gconstpointer user_data){
6684
const pid_t pid = fork();
6685
if(pid == 0){ /* Child */
6686
if(not restore_signal_handler(&fixture->orig_sigaction)){
6687
_exit(EXIT_FAILURE);
6689
if(not restore_sigmask(&fixture->orig_sigmask)){
6690
_exit(EXIT_FAILURE);
6694
g_assert_true(pid != -1);
6695
__attribute__((cleanup(cleanup_close)))
6696
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6697
g_assert_cmpint(epoll_fd, >=, 0);
6699
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6702
g_assert_true(waitpid(pid, &status, 0) == pid);
6703
g_assert_true(WIFEXITED(status));
6704
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6707
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6708
test_fixture *fixture,
6709
__attribute__((unused))
6710
gconstpointer user_data){
6711
__attribute__((cleanup(cleanup_queue)))
6712
task_queue *queue = create_queue();
6713
g_assert_nonnull(queue);
6714
queue->next_run = 1;
6715
__attribute__((cleanup(cleanup_close)))
6716
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6717
__attribute__((cleanup(string_set_clear)))
6718
string_set cancelled_filenames = {};
6719
bool quit_now = false;
6721
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6722
g_assert_false(quit_now);
6723
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6727
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6728
test_fixture *fixture,
6729
__attribute__((unused))
6732
__attribute__((cleanup(cleanup_queue)))
6733
task_queue *queue = create_queue();
6734
g_assert_nonnull(queue);
6735
__attribute__((cleanup(string_set_clear)))
6736
string_set cancelled_filenames = {};
6737
bool quit_now = false;
6738
const char question_filename[] = "/nonexistent/question_filename";
6739
g_assert_true(string_set_add(&cancelled_filenames,
6740
question_filename));
6742
g_assert_true(add_to_queue(queue,
6743
(task_context){ .func=dummy_func }));
6745
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6746
g_assert_false(quit_now);
6747
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6748
g_assert_false(string_set_contains(cancelled_filenames,
6749
question_filename));
6753
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6754
test_fixture *fixture,
6755
__attribute__((unused))
6758
__attribute__((cleanup(cleanup_queue)))
6759
task_queue *queue = create_queue();
6760
g_assert_nonnull(queue);
6761
__attribute__((cleanup(string_set_clear)))
6762
string_set cancelled_filenames = {};
6763
bool quit_now = false;
6765
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6766
__attribute__((cleanup(cleanup_close)))
6767
const int read_pipe = pipefds[0];
6768
g_assert_cmpint(close(pipefds[1]), ==, 0);
6769
const char question_filename[] = "/nonexistent/question_filename";
6770
g_assert_true(string_set_add(&cancelled_filenames,
6771
question_filename));
6772
__attribute__((nonnull))
6773
void quit_func(const task_context task,
6774
__attribute__((unused)) task_queue *const q){
6775
g_assert_nonnull(task.quit_now);
6776
*task.quit_now = true;
6778
task_context task = {
6780
.question_filename=strdup(question_filename),
6781
.quit_now=&quit_now,
6784
g_assert_nonnull(task.question_filename);
6786
g_assert_true(add_to_queue(queue, task));
6788
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6789
g_assert_false(quit_now);
6791
/* read_pipe should be closed already */
6793
bool read_pipe_closed = (close(read_pipe) == -1);
6794
read_pipe_closed &= (errno == EBADF);
6795
g_assert_true(read_pipe_closed);
6798
static void test_run_queue_one_task(__attribute__((unused))
6799
test_fixture *fixture,
6800
__attribute__((unused))
6801
gconstpointer user_data){
6802
__attribute__((cleanup(cleanup_queue)))
6803
task_queue *queue = create_queue();
6804
g_assert_nonnull(queue);
6805
__attribute__((cleanup(string_set_clear)))
6806
string_set cancelled_filenames = {};
6807
bool quit_now = false;
6809
__attribute__((nonnull))
6810
void next_run_func(__attribute__((unused))
6811
const task_context task,
6812
task_queue *const q){
6816
task_context task = {
6817
.func=next_run_func,
6819
g_assert_true(add_to_queue(queue, task));
6821
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6822
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6823
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6826
static void test_run_queue_two_tasks(__attribute__((unused))
6827
test_fixture *fixture,
6828
__attribute__((unused))
6829
gconstpointer user_data){
6830
__attribute__((cleanup(cleanup_queue)))
6831
task_queue *queue = create_queue();
6832
g_assert_nonnull(queue);
6833
queue->next_run = 1;
6834
__attribute__((cleanup(string_set_clear)))
6835
string_set cancelled_filenames = {};
6836
bool quit_now = false;
6837
bool mandos_client_exited = false;
6839
__attribute__((nonnull))
6840
void next_run_func(__attribute__((unused))
6841
const task_context task,
6842
task_queue *const q){
6846
__attribute__((nonnull))
6847
void exited_func(const task_context task,
6848
__attribute__((unused)) task_queue *const q){
6849
*task.mandos_client_exited = true;
6852
task_context task1 = {
6853
.func=next_run_func,
6855
g_assert_true(add_to_queue(queue, task1));
6857
task_context task2 = {
6859
.mandos_client_exited=&mandos_client_exited,
6861
g_assert_true(add_to_queue(queue, task2));
6863
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6864
g_assert_false(quit_now);
6865
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6866
g_assert_true(mandos_client_exited);
6867
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6870
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6871
test_fixture *fixture,
6872
__attribute__((unused))
6873
gconstpointer user_data){
6874
__attribute__((cleanup(cleanup_queue)))
6875
task_queue *queue = create_queue();
6876
g_assert_nonnull(queue);
6877
__attribute__((cleanup(string_set_clear)))
6878
string_set cancelled_filenames = {};
6879
bool quit_now = false;
6880
bool mandos_client_exited = false;
6881
bool password_is_read = false;
6883
__attribute__((nonnull))
6884
void set_exited_func(const task_context task,
6885
__attribute__((unused)) task_queue *const q){
6886
*task.mandos_client_exited = true;
6887
*task.quit_now = true;
6889
task_context task1 = {
6890
.func=set_exited_func,
6891
.quit_now=&quit_now,
6892
.mandos_client_exited=&mandos_client_exited,
6894
g_assert_true(add_to_queue(queue, task1));
6896
__attribute__((nonnull))
6897
void set_read_func(const task_context task,
6898
__attribute__((unused)) task_queue *const q){
6899
*task.quit_now = true;
6900
*task.password_is_read = true;
6902
task_context task2 = {
6903
.func=set_read_func,
6904
.quit_now=&quit_now,
6905
.password_is_read=&password_is_read,
6907
g_assert_true(add_to_queue(queue, task2));
6909
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6910
g_assert_true(quit_now);
6911
g_assert_true(mandos_client_exited xor password_is_read);
6912
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6915
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6916
test_fixture *fixture,
6917
__attribute__((unused))
6918
gconstpointer user_data){
6919
__attribute__((cleanup(cleanup_queue)))
6920
task_queue *queue = create_queue();
6921
g_assert_nonnull(queue);
6922
__attribute__((cleanup(string_set_clear)))
6923
string_set cancelled_filenames = {};
6925
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6926
__attribute__((cleanup(cleanup_close)))
6927
const int read_pipe = pipefds[0];
6928
__attribute__((cleanup(cleanup_close)))
6929
const int write_pipe = pipefds[1];
6930
bool quit_now = false;
6932
__attribute__((nonnull))
6933
void read_func(const task_context task,
6934
__attribute__((unused)) task_queue *const q){
6935
*task.quit_now = true;
6937
task_context task1 = {
6939
.quit_now=&quit_now,
6942
g_assert_true(add_to_queue(queue, task1));
6944
__attribute__((nonnull))
6945
void write_func(const task_context task,
6946
__attribute__((unused)) task_queue *const q){
6947
*task.quit_now = true;
6949
task_context task2 = {
6951
.quit_now=&quit_now,
6954
g_assert_true(add_to_queue(queue, task2));
6956
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6957
g_assert_true(quit_now);
6959
/* Either read_pipe or write_pipe should be closed already */
6961
bool close_read_pipe = (close(read_pipe) == -1);
6962
close_read_pipe &= (errno == EBADF);
6964
bool close_write_pipe = (close(write_pipe) == -1);
6965
close_write_pipe &= (errno == EBADF);
6966
g_assert_true(close_read_pipe xor close_write_pipe);
6967
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6970
static void test_setup_signal_handler(__attribute__((unused))
6971
test_fixture *fixture,
6972
__attribute__((unused))
6973
gconstpointer user_data){
6974
/* Save current SIGCHLD action, whatever it is */
6975
struct sigaction expected_sigchld_action;
6976
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6979
/* Act; i.e. run the setup_signal_handler() function */
6980
struct sigaction actual_old_sigchld_action;
6981
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6983
/* Check that the function correctly set "actual_old_sigchld_action"
6984
to the same values as the previously saved
6985
"expected_sigchld_action" */
6986
/* Check member sa_handler */
6987
g_assert_true(actual_old_sigchld_action.sa_handler
6988
== expected_sigchld_action.sa_handler);
6989
/* Check member sa_mask */
6990
for(int signum = 1; signum < NSIG; signum++){
6991
const int expected_old_block_state
6992
= sigismember(&expected_sigchld_action.sa_mask, signum);
6993
g_assert_cmpint(expected_old_block_state, >=, 0);
6994
const int actual_old_block_state
6995
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6996
g_assert_cmpint(actual_old_block_state, >=, 0);
6997
g_assert_cmpint(actual_old_block_state,
6998
==, expected_old_block_state);
7000
/* Check member sa_flags */
7001
g_assert_true((actual_old_sigchld_action.sa_flags
7002
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7003
== (expected_sigchld_action.sa_flags
7004
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7006
/* Retrieve the current signal handler for SIGCHLD as set by
7007
setup_signal_handler() */
7008
struct sigaction actual_new_sigchld_action;
7009
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7010
&actual_new_sigchld_action), ==, 0);
7011
/* Check that the signal handler (member sa_handler) is correctly
7012
set to the "handle_sigchld" function */
7013
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7014
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7015
g_assert_true(actual_new_sigchld_action.sa_handler
7017
/* Check (in member sa_mask) that at least a handful of signals are
7018
actually blocked during the signal handler */
7019
for(int signum = 1; signum < NSIG; signum++){
7020
int actual_new_block_state;
7026
actual_new_block_state
7027
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7028
g_assert_cmpint(actual_new_block_state, ==, 1);
7030
case SIGKILL: /* non-blockable */
7031
case SIGSTOP: /* non-blockable */
7032
case SIGCHLD: /* always blocked */
7037
/* Check member sa_flags */
7038
g_assert_true((actual_new_sigchld_action.sa_flags
7039
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7040
== (SA_NOCLDSTOP | SA_RESTART));
7042
/* Restore signal handler */
7043
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7047
static void test_restore_signal_handler(__attribute__((unused))
7048
test_fixture *fixture,
7049
__attribute__((unused))
7050
gconstpointer user_data){
7051
/* Save current SIGCHLD action, whatever it is */
7052
struct sigaction expected_sigchld_action;
7053
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7055
/* Since we haven't established a signal handler yet, there should
7056
not be one established. But another test may have relied on
7057
restore_signal_handler() to restore the signal handler, and if
7058
restore_signal_handler() is buggy (which we should be prepared
7059
for in this test) the signal handler may not have been restored
7060
properly; check for this: */
7061
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7063
/* Establish a signal handler */
7064
struct sigaction sigchld_action = {
7065
.sa_handler=handle_sigchld,
7066
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7068
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7069
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7071
/* Act; i.e. run the restore_signal_handler() function */
7072
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7074
/* Retrieve the restored signal handler data */
7075
struct sigaction actual_restored_sigchld_action;
7076
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7077
&actual_restored_sigchld_action), ==, 0);
7079
/* Check that the function correctly restored the signal action, as
7080
saved in "actual_restored_sigchld_action", to the same values as
7081
the previously saved "expected_sigchld_action" */
7082
/* Check member sa_handler */
7083
g_assert_true(actual_restored_sigchld_action.sa_handler
7084
== expected_sigchld_action.sa_handler);
7085
/* Check member sa_mask */
7086
for(int signum = 1; signum < NSIG; signum++){
7087
const int expected_old_block_state
7088
= sigismember(&expected_sigchld_action.sa_mask, signum);
7089
g_assert_cmpint(expected_old_block_state, >=, 0);
7090
const int actual_restored_block_state
7091
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7092
g_assert_cmpint(actual_restored_block_state, >=, 0);
7093
g_assert_cmpint(actual_restored_block_state,
7094
==, expected_old_block_state);
7096
/* Check member sa_flags */
7097
g_assert_true((actual_restored_sigchld_action.sa_flags
7098
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7099
== (expected_sigchld_action.sa_flags
7100
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7103
static void test_block_sigchld(__attribute__((unused))
7104
test_fixture *fixture,
7105
__attribute__((unused))
7106
gconstpointer user_data){
7107
/* Save original signal mask */
7108
sigset_t expected_sigmask;
7109
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7112
/* Make sure SIGCHLD is unblocked for this test */
7113
sigset_t sigchld_sigmask;
7114
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7115
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7116
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7119
/* Act; i.e. run the block_sigchld() function */
7120
sigset_t actual_old_sigmask;
7121
g_assert_true(block_sigchld(&actual_old_sigmask));
7123
/* Check the actual_old_sigmask; it should be the same as the
7124
previously saved signal mask "expected_sigmask". */
7125
for(int signum = 1; signum < NSIG; signum++){
7126
const int expected_old_block_state
7127
= sigismember(&expected_sigmask, signum);
7128
g_assert_cmpint(expected_old_block_state, >=, 0);
7129
const int actual_old_block_state
7130
= sigismember(&actual_old_sigmask, signum);
7131
g_assert_cmpint(actual_old_block_state, >=, 0);
7132
g_assert_cmpint(actual_old_block_state,
7133
==, expected_old_block_state);
7136
/* Retrieve the newly set signal mask */
7137
sigset_t actual_sigmask;
7138
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7140
/* SIGCHLD should be blocked */
7141
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7143
/* Restore signal mask */
7144
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7148
static void test_restore_sigmask(__attribute__((unused))
7149
test_fixture *fixture,
7150
__attribute__((unused))
7151
gconstpointer user_data){
7152
/* Save original signal mask */
7153
sigset_t orig_sigmask;
7154
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7156
/* Make sure SIGCHLD is blocked for this test */
7157
sigset_t sigchld_sigmask;
7158
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7159
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7160
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7163
/* Act; i.e. run the restore_sigmask() function */
7164
g_assert_true(restore_sigmask(&orig_sigmask));
7166
/* Retrieve the newly restored signal mask */
7167
sigset_t restored_sigmask;
7168
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7171
/* Check the restored_sigmask; it should be the same as the
7172
previously saved signal mask "orig_sigmask". */
7173
for(int signum = 1; signum < NSIG; signum++){
7174
const int orig_block_state = sigismember(&orig_sigmask, signum);
7175
g_assert_cmpint(orig_block_state, >=, 0);
7176
const int restored_block_state = sigismember(&restored_sigmask,
7178
g_assert_cmpint(restored_block_state, >=, 0);
7179
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7182
/* Restore signal mask */
7183
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7187
static void test_parse_arguments_noargs(__attribute__((unused))
7188
test_fixture *fixture,
7189
__attribute__((unused))
7190
gconstpointer user_data){
7194
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7196
char *agent_directory = NULL;
7197
char *helper_directory = NULL;
7200
char *mandos_argz = NULL;
7201
size_t mandos_argz_length = 0;
7203
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7204
&helper_directory, &user, &group,
7205
&mandos_argz, &mandos_argz_length));
7206
g_assert_null(agent_directory);
7207
g_assert_null(helper_directory);
7208
g_assert_true(user == 0);
7209
g_assert_true(group == 0);
7210
g_assert_null(mandos_argz);
7211
g_assert_true(mandos_argz_length == 0);
7213
for(char **arg = argv; *arg != NULL; arg++){
7218
__attribute__((nonnull))
7219
static bool parse_arguments_devnull(int argc, char *argv[],
7220
const bool exit_failure,
7221
char **agent_directory,
7222
char **helper_directory,
7226
size_t *mandos_argz_length){
7228
FILE *real_stderr = stderr;
7229
FILE *devnull = fopen("/dev/null", "we");
7230
g_assert_nonnull(devnull);
7233
const bool ret = parse_arguments(argc, argv, exit_failure,
7235
helper_directory, user, group,
7236
mandos_argz, mandos_argz_length);
7237
const error_t saved_errno = errno;
7239
stderr = real_stderr;
7240
g_assert_cmpint(fclose(devnull), ==, 0);
7242
errno = saved_errno;
7247
static void test_parse_arguments_invalid(__attribute__((unused))
7248
test_fixture *fixture,
7249
__attribute__((unused))
7250
gconstpointer user_data){
7253
strdup("--invalid"),
7255
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7257
char *agent_directory = NULL;
7258
char *helper_directory = NULL;
7261
char *mandos_argz = NULL;
7262
size_t mandos_argz_length = 0;
7264
g_assert_false(parse_arguments_devnull(argc, argv, false,
7266
&helper_directory, &user,
7267
&group, &mandos_argz,
7268
&mandos_argz_length));
7270
g_assert_true(errno == EINVAL);
7271
g_assert_null(agent_directory);
7272
g_assert_null(helper_directory);
7273
g_assert_null(mandos_argz);
7274
g_assert_true(mandos_argz_length == 0);
7276
for(char **arg = argv; *arg != NULL; arg++){
7281
static void test_parse_arguments_long_dir(__attribute__((unused))
7282
test_fixture *fixture,
7283
__attribute__((unused))
7284
gconstpointer user_data){
7287
strdup("--agent-directory"),
7290
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7292
__attribute__((cleanup(cleanup_string)))
7293
char *agent_directory = NULL;
7294
char *helper_directory = NULL;
7297
__attribute__((cleanup(cleanup_string)))
7298
char *mandos_argz = NULL;
7299
size_t mandos_argz_length = 0;
7301
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7302
&helper_directory, &user, &group,
7303
&mandos_argz, &mandos_argz_length));
7305
g_assert_cmpstr(agent_directory, ==, "/tmp");
7306
g_assert_null(helper_directory);
7307
g_assert_true(user == 0);
7308
g_assert_true(group == 0);
7309
g_assert_null(mandos_argz);
7310
g_assert_true(mandos_argz_length == 0);
7312
for(char **arg = argv; *arg != NULL; arg++){
7317
static void test_parse_arguments_short_dir(__attribute__((unused))
7318
test_fixture *fixture,
7319
__attribute__((unused))
7320
gconstpointer user_data){
7326
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7328
__attribute__((cleanup(cleanup_string)))
7329
char *agent_directory = NULL;
7330
char *helper_directory = NULL;
7333
__attribute__((cleanup(cleanup_string)))
7334
char *mandos_argz = NULL;
7335
size_t mandos_argz_length = 0;
7337
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7338
&helper_directory, &user, &group,
7339
&mandos_argz, &mandos_argz_length));
7341
g_assert_cmpstr(agent_directory, ==, "/tmp");
7342
g_assert_null(helper_directory);
7343
g_assert_true(user == 0);
7344
g_assert_true(group == 0);
7345
g_assert_null(mandos_argz);
7346
g_assert_true(mandos_argz_length == 0);
7348
for(char **arg = argv; *arg != NULL; arg++){
7354
void test_parse_arguments_helper_directory(__attribute__((unused))
7355
test_fixture *fixture,
7356
__attribute__((unused))
7357
gconstpointer user_data){
7360
strdup("--helper-directory"),
7363
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7365
char *agent_directory = NULL;
7366
__attribute__((cleanup(cleanup_string)))
7367
char *helper_directory = NULL;
7370
__attribute__((cleanup(cleanup_string)))
7371
char *mandos_argz = NULL;
7372
size_t mandos_argz_length = 0;
7374
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7375
&helper_directory, &user, &group,
7376
&mandos_argz, &mandos_argz_length));
7378
g_assert_cmpstr(helper_directory, ==, "/tmp");
7379
g_assert_null(agent_directory);
7380
g_assert_true(user == 0);
7381
g_assert_true(group == 0);
7382
g_assert_null(mandos_argz);
7383
g_assert_true(mandos_argz_length == 0);
7385
for(char **arg = argv; *arg != NULL; arg++){
7391
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7392
test_fixture *fixture,
7393
__attribute__((unused))
7394
gconstpointer user_data){
7397
strdup("--plugin-helper-dir"),
7400
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7402
char *agent_directory = NULL;
7403
__attribute__((cleanup(cleanup_string)))
7404
char *helper_directory = NULL;
7407
__attribute__((cleanup(cleanup_string)))
7408
char *mandos_argz = NULL;
7409
size_t mandos_argz_length = 0;
7411
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7412
&helper_directory, &user, &group,
7413
&mandos_argz, &mandos_argz_length));
7415
g_assert_cmpstr(helper_directory, ==, "/tmp");
7416
g_assert_null(agent_directory);
7417
g_assert_true(user == 0);
7418
g_assert_true(group == 0);
7419
g_assert_null(mandos_argz);
7420
g_assert_true(mandos_argz_length == 0);
7422
for(char **arg = argv; *arg != NULL; arg++){
7427
static void test_parse_arguments_user(__attribute__((unused))
7428
test_fixture *fixture,
7429
__attribute__((unused))
7430
gconstpointer user_data){
7436
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7438
char *agent_directory = NULL;
7439
__attribute__((cleanup(cleanup_string)))
7440
char *helper_directory = NULL;
7443
__attribute__((cleanup(cleanup_string)))
7444
char *mandos_argz = NULL;
7445
size_t mandos_argz_length = 0;
7447
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7448
&helper_directory, &user, &group,
7449
&mandos_argz, &mandos_argz_length));
7451
g_assert_null(helper_directory);
7452
g_assert_null(agent_directory);
7453
g_assert_cmpuint((unsigned int)user, ==, 1000);
7454
g_assert_true(group == 0);
7455
g_assert_null(mandos_argz);
7456
g_assert_true(mandos_argz_length == 0);
7458
for(char **arg = argv; *arg != NULL; arg++){
7463
static void test_parse_arguments_user_invalid(__attribute__((unused))
7464
test_fixture *fixture,
7465
__attribute__((unused))
7473
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7475
char *agent_directory = NULL;
7476
__attribute__((cleanup(cleanup_string)))
7477
char *helper_directory = NULL;
7480
__attribute__((cleanup(cleanup_string)))
7481
char *mandos_argz = NULL;
7482
size_t mandos_argz_length = 0;
7484
g_assert_false(parse_arguments_devnull(argc, argv, false,
7486
&helper_directory, &user,
7487
&group, &mandos_argz,
7488
&mandos_argz_length));
7490
g_assert_null(helper_directory);
7491
g_assert_null(agent_directory);
7492
g_assert_cmpuint((unsigned int)user, ==, 0);
7493
g_assert_true(group == 0);
7494
g_assert_null(mandos_argz);
7495
g_assert_true(mandos_argz_length == 0);
7497
for(char **arg = argv; *arg != NULL; arg++){
7503
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7504
test_fixture *fixture,
7505
__attribute__((unused))
7506
gconstpointer user_data){
7512
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7514
char *agent_directory = NULL;
7515
__attribute__((cleanup(cleanup_string)))
7516
char *helper_directory = NULL;
7519
__attribute__((cleanup(cleanup_string)))
7520
char *mandos_argz = NULL;
7521
size_t mandos_argz_length = 0;
7523
g_assert_false(parse_arguments_devnull(argc, argv, false,
7525
&helper_directory, &user,
7526
&group, &mandos_argz,
7527
&mandos_argz_length));
7529
g_assert_null(helper_directory);
7530
g_assert_null(agent_directory);
7531
g_assert_cmpuint((unsigned int)user, ==, 0);
7532
g_assert_true(group == 0);
7533
g_assert_null(mandos_argz);
7534
g_assert_true(mandos_argz_length == 0);
7536
for(char **arg = argv; *arg != NULL; arg++){
7541
static void test_parse_arguments_group(__attribute__((unused))
7542
test_fixture *fixture,
7543
__attribute__((unused))
7544
gconstpointer user_data){
7550
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7552
char *agent_directory = NULL;
7553
__attribute__((cleanup(cleanup_string)))
7554
char *helper_directory = NULL;
7557
__attribute__((cleanup(cleanup_string)))
7558
char *mandos_argz = NULL;
7559
size_t mandos_argz_length = 0;
7561
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7562
&helper_directory, &user, &group,
7563
&mandos_argz, &mandos_argz_length));
7565
g_assert_null(helper_directory);
7566
g_assert_null(agent_directory);
7567
g_assert_true(user == 0);
7568
g_assert_cmpuint((unsigned int)group, ==, 1000);
7569
g_assert_null(mandos_argz);
7570
g_assert_true(mandos_argz_length == 0);
7572
for(char **arg = argv; *arg != NULL; arg++){
7577
static void test_parse_arguments_group_invalid(__attribute__((unused))
7578
test_fixture *fixture,
7579
__attribute__((unused))
7587
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7589
char *agent_directory = NULL;
7590
__attribute__((cleanup(cleanup_string)))
7591
char *helper_directory = NULL;
7594
__attribute__((cleanup(cleanup_string)))
7595
char *mandos_argz = NULL;
7596
size_t mandos_argz_length = 0;
7598
g_assert_false(parse_arguments_devnull(argc, argv, false,
7600
&helper_directory, &user,
7601
&group, &mandos_argz,
7602
&mandos_argz_length));
7604
g_assert_null(helper_directory);
7605
g_assert_null(agent_directory);
7606
g_assert_true(user == 0);
7607
g_assert_true(group == 0);
7608
g_assert_null(mandos_argz);
7609
g_assert_true(mandos_argz_length == 0);
7611
for(char **arg = argv; *arg != NULL; arg++){
7617
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7618
test_fixture *fixture,
7619
__attribute__((unused))
7620
gconstpointer user_data){
7626
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7628
char *agent_directory = NULL;
7629
__attribute__((cleanup(cleanup_string)))
7630
char *helper_directory = NULL;
7633
__attribute__((cleanup(cleanup_string)))
7634
char *mandos_argz = NULL;
7635
size_t mandos_argz_length = 0;
7637
g_assert_false(parse_arguments_devnull(argc, argv, false,
7639
&helper_directory, &user,
7640
&group, &mandos_argz,
7641
&mandos_argz_length));
7643
g_assert_null(helper_directory);
7644
g_assert_null(agent_directory);
7645
g_assert_cmpuint((unsigned int)group, ==, 0);
7646
g_assert_true(group == 0);
7647
g_assert_null(mandos_argz);
7648
g_assert_true(mandos_argz_length == 0);
7650
for(char **arg = argv; *arg != NULL; arg++){
7655
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7656
test_fixture *fixture,
7657
__attribute__((unused))
7662
strdup("mandos-client"),
7664
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7666
__attribute__((cleanup(cleanup_string)))
7667
char *agent_directory = NULL;
7668
__attribute__((cleanup(cleanup_string)))
7669
char *helper_directory = NULL;
7672
__attribute__((cleanup(cleanup_string)))
7673
char *mandos_argz = NULL;
7674
size_t mandos_argz_length = 0;
7676
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7677
&helper_directory, &user, &group,
7678
&mandos_argz, &mandos_argz_length));
7680
g_assert_null(agent_directory);
7681
g_assert_null(helper_directory);
7682
g_assert_true(user == 0);
7683
g_assert_true(group == 0);
7684
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7685
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7686
mandos_argz_length),
7689
for(char **arg = argv; *arg != NULL; arg++){
7694
static void test_parse_arguments_mandos_args(__attribute__((unused))
7695
test_fixture *fixture,
7696
__attribute__((unused))
7697
gconstpointer user_data){
7700
strdup("mandos-client"),
7705
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7707
__attribute__((cleanup(cleanup_string)))
7708
char *agent_directory = NULL;
7709
__attribute__((cleanup(cleanup_string)))
7710
char *helper_directory = NULL;
7713
__attribute__((cleanup(cleanup_string)))
7714
char *mandos_argz = NULL;
7715
size_t mandos_argz_length = 0;
7717
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7718
&helper_directory, &user, &group,
7719
&mandos_argz, &mandos_argz_length));
7721
g_assert_null(agent_directory);
7722
g_assert_null(helper_directory);
7723
g_assert_true(user == 0);
7724
g_assert_true(group == 0);
7725
char *marg = mandos_argz;
7726
g_assert_cmpstr(marg, ==, "mandos-client");
7727
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7728
g_assert_cmpstr(marg, ==, "one");
7729
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7730
g_assert_cmpstr(marg, ==, "two");
7731
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7732
g_assert_cmpstr(marg, ==, "three");
7733
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7734
mandos_argz_length),
7737
for(char **arg = argv; *arg != NULL; arg++){
7742
static void test_parse_arguments_all_args(__attribute__((unused))
7743
test_fixture *fixture,
7744
__attribute__((unused))
7745
gconstpointer user_data){
7748
strdup("--agent-directory"),
7750
strdup("--helper-directory"),
7756
strdup("mandos-client"),
7761
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7763
__attribute__((cleanup(cleanup_string)))
7764
char *agent_directory = NULL;
7765
__attribute__((cleanup(cleanup_string)))
7766
char *helper_directory = NULL;
7769
__attribute__((cleanup(cleanup_string)))
7770
char *mandos_argz = NULL;
7771
size_t mandos_argz_length = 0;
7773
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7774
&helper_directory, &user, &group,
7775
&mandos_argz, &mandos_argz_length));
7777
g_assert_cmpstr(agent_directory, ==, "/tmp");
7778
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7779
g_assert_true(user == 1);
7780
g_assert_true(group == 2);
7781
char *marg = mandos_argz;
7782
g_assert_cmpstr(marg, ==, "mandos-client");
7783
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7784
g_assert_cmpstr(marg, ==, "one");
7785
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7786
g_assert_cmpstr(marg, ==, "two");
7787
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7788
g_assert_cmpstr(marg, ==, "three");
7789
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7790
mandos_argz_length),
7793
for(char **arg = argv; *arg != NULL; arg++){
7798
static void test_parse_arguments_mixed(__attribute__((unused))
7799
test_fixture *fixture,
7800
__attribute__((unused))
7801
gconstpointer user_data){
7804
strdup("mandos-client"),
7808
strdup("--agent-directory"),
7812
strdup("--helper-directory=/var/tmp"),
7814
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7816
__attribute__((cleanup(cleanup_string)))
7817
char *agent_directory = NULL;
7818
__attribute__((cleanup(cleanup_string)))
7819
char *helper_directory = NULL;
7822
__attribute__((cleanup(cleanup_string)))
7823
char *mandos_argz = NULL;
7824
size_t mandos_argz_length = 0;
7826
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7827
&helper_directory, &user, &group,
7828
&mandos_argz, &mandos_argz_length));
7830
g_assert_cmpstr(agent_directory, ==, "/tmp");
7831
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7832
g_assert_true(user == 1);
7833
g_assert_true(group == 0);
7834
char *marg = mandos_argz;
7835
g_assert_cmpstr(marg, ==, "mandos-client");
7836
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7837
g_assert_cmpstr(marg, ==, "one");
7838
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7839
g_assert_cmpstr(marg, ==, "two");
7840
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7841
g_assert_cmpstr(marg, ==, "three");
7842
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7843
mandos_argz_length),
7846
for(char **arg = argv; *arg != NULL; arg++){
7851
/* End of tests section */
7853
/* Test boilerplate section; New tests should be added to the test
7854
suite definition here, in the "run_tests" function.
7856
Finally, this section also contains the should_only_run_tests()
7857
function used by main() for deciding if tests should be run or to
7860
__attribute__((cold))
7861
static bool run_tests(int argc, char *argv[]){
7862
g_test_init(&argc, &argv, NULL);
7864
/* A macro to add a test with no setup or teardown functions */
7865
#define test_add(testpath, testfunc) \
7867
g_test_add((testpath), test_fixture, NULL, NULL, \
7868
(testfunc), NULL); \
7871
/* Test the signal-related functions first, since some other tests
7872
depend on these functions in their setups and teardowns */
7873
test_add("/signal-handling/setup", test_setup_signal_handler);
7874
test_add("/signal-handling/restore", test_restore_signal_handler);
7875
test_add("/signal-handling/block", test_block_sigchld);
7876
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7878
/* Regular non-signal-related tests; these use no setups or
7880
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7881
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7882
test_add("/parse_arguments/long-dir",
7883
test_parse_arguments_long_dir);
7884
test_add("/parse_arguments/short-dir",
7885
test_parse_arguments_short_dir);
7886
test_add("/parse_arguments/helper-directory",
7887
test_parse_arguments_helper_directory);
7888
test_add("/parse_arguments/plugin-helper-dir",
7889
test_parse_arguments_plugin_helper_dir);
7890
test_add("/parse_arguments/user", test_parse_arguments_user);
7891
test_add("/parse_arguments/user-invalid",
7892
test_parse_arguments_user_invalid);
7893
test_add("/parse_arguments/user-zero-invalid",
7894
test_parse_arguments_user_zero_invalid);
7895
test_add("/parse_arguments/group", test_parse_arguments_group);
7896
test_add("/parse_arguments/group-invalid",
7897
test_parse_arguments_group_invalid);
7898
test_add("/parse_arguments/group-zero-invalid",
7899
test_parse_arguments_group_zero_invalid);
7900
test_add("/parse_arguments/mandos-noargs",
7901
test_parse_arguments_mandos_noargs);
7902
test_add("/parse_arguments/mandos-args",
7903
test_parse_arguments_mandos_args);
7904
test_add("/parse_arguments/all-args",
7905
test_parse_arguments_all_args);
7906
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7907
test_add("/queue/create", test_create_queue);
7908
test_add("/queue/add", test_add_to_queue);
7909
test_add("/queue/add/overflow", test_add_to_queue_overflow);
7910
test_add("/queue/has_question/empty",
7911
test_queue_has_question_empty);
7912
test_add("/queue/has_question/false",
7913
test_queue_has_question_false);
7914
test_add("/queue/has_question/true", test_queue_has_question_true);
7915
test_add("/queue/has_question/false2",
7916
test_queue_has_question_false2);
7917
test_add("/queue/has_question/true2",
7918
test_queue_has_question_true2);
7919
test_add("/buffer/cleanup", test_cleanup_buffer);
7920
test_add("/string_set/net-set-contains-nothing",
7921
test_string_set_new_set_contains_nothing);
7922
test_add("/string_set/with-added-string-contains-it",
7923
test_string_set_with_added_string_contains_it);
7924
test_add("/string_set/cleared-does-not-contain-string",
7925
test_string_set_cleared_does_not_contain_str);
7926
test_add("/string_set/swap/one-with-empty",
7927
test_string_set_swap_one_with_empty);
7928
test_add("/string_set/swap/empty-with-one",
7929
test_string_set_swap_empty_with_one);
7930
test_add("/string_set/swap/one-with-one",
7931
test_string_set_swap_one_with_one);
7933
/* A macro to add a test using the setup and teardown functions */
7934
#define test_add_st(path, func) \
7936
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7940
/* Signal-related tests; these use setups and teardowns which
7941
establish, during each test run, a signal handler for, and a
7942
signal mask blocking, the SIGCHLD signal, just like main() */
7943
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7944
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7945
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7946
test_add_st("/run_queue/zeroes-next-run",
7947
test_run_queue_zeroes_next_run);
7948
test_add_st("/run_queue/clears-cancelled_filenames",
7949
test_run_queue_clears_cancelled_filenames);
7950
test_add_st("/run_queue/skips-cancelled-filenames",
7951
test_run_queue_skips_cancelled_filenames);
7952
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7953
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7954
test_add_st("/run_queue/two-tasks/quit",
7955
test_run_queue_two_tasks_quit);
7956
test_add_st("/run_queue/two-tasks-cleanup",
7957
test_run_queue_two_tasks_cleanup);
7958
test_add_st("/task-creators/start_mandos_client",
7959
test_start_mandos_client);
7960
test_add_st("/task-creators/start_mandos_client/execv",
7961
test_start_mandos_client_execv);
7962
test_add_st("/task-creators/start_mandos_client/suid/euid",
7963
test_start_mandos_client_suid_euid);
7964
test_add_st("/task-creators/start_mandos_client/suid/egid",
7965
test_start_mandos_client_suid_egid);
7966
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7967
test_start_mandos_client_suid_ruid);
7968
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7969
test_start_mandos_client_suid_rgid);
7970
test_add_st("/task-creators/start_mandos_client/read",
7971
test_start_mandos_client_read);
7972
test_add_st("/task-creators/start_mandos_client/helper-directory",
7973
test_start_mandos_client_helper_directory);
7974
test_add_st("/task-creators/start_mandos_client/sigmask",
7975
test_start_mandos_client_sigmask);
7976
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7977
test_wait_for_mandos_client_exit_badpid);
7978
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7979
test_wait_for_mandos_client_exit_noexit);
7980
test_add_st("/task/wait_for_mandos_client_exit/success",
7981
test_wait_for_mandos_client_exit_success);
7982
test_add_st("/task/wait_for_mandos_client_exit/failure",
7983
test_wait_for_mandos_client_exit_failure);
7984
test_add_st("/task/wait_for_mandos_client_exit/killed",
7985
test_wait_for_mandos_client_exit_killed);
7986
test_add_st("/task/read_mandos_client_output/readerror",
7987
test_read_mandos_client_output_readerror);
7988
test_add_st("/task/read_mandos_client_output/nodata",
7989
test_read_mandos_client_output_nodata);
7990
test_add_st("/task/read_mandos_client_output/eof",
7991
test_read_mandos_client_output_eof);
7992
test_add_st("/task/read_mandos_client_output/once",
7993
test_read_mandos_client_output_once);
7994
test_add_st("/task/read_mandos_client_output/malloc",
7995
test_read_mandos_client_output_malloc);
7996
test_add_st("/task/read_mandos_client_output/append",
7997
test_read_mandos_client_output_append);
7998
test_add_st("/task-creators/add_inotify_dir_watch",
7999
test_add_inotify_dir_watch);
8000
test_add_st("/task-creators/add_inotify_dir_watch/fail",
8001
test_add_inotify_dir_watch_fail);
8002
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8003
test_add_inotify_dir_watch_nondir);
8004
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8005
test_add_inotify_dir_watch_EAGAIN);
8006
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8007
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8008
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8009
test_add_inotify_dir_watch_IN_MOVED_TO);
8010
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8011
test_add_inotify_dir_watch_IN_MOVED_FROM);
8012
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8013
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8014
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8015
test_add_inotify_dir_watch_IN_DELETE);
8016
test_add_st("/task/read_inotify_event/readerror",
8017
test_read_inotify_event_readerror);
8018
test_add_st("/task/read_inotify_event/bad-epoll",
8019
test_read_inotify_event_bad_epoll);
8020
test_add_st("/task/read_inotify_event/nodata",
8021
test_read_inotify_event_nodata);
8022
test_add_st("/task/read_inotify_event/eof",
8023
test_read_inotify_event_eof);
8024
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8025
test_read_inotify_event_IN_CLOSE_WRITE);
8026
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8027
test_read_inotify_event_IN_MOVED_TO);
8028
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8029
test_read_inotify_event_IN_MOVED_FROM);
8030
test_add_st("/task/read_inotify_event/IN_DELETE",
8031
test_read_inotify_event_IN_DELETE);
8032
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8033
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8034
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8035
test_read_inotify_event_IN_MOVED_TO_badname);
8036
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8037
test_read_inotify_event_IN_MOVED_FROM_badname);
8038
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8039
test_read_inotify_event_IN_DELETE_badname);
8040
test_add_st("/task/open_and_parse_question/ENOENT",
8041
test_open_and_parse_question_ENOENT);
8042
test_add_st("/task/open_and_parse_question/EIO",
8043
test_open_and_parse_question_EIO);
8044
test_add_st("/task/open_and_parse_question/parse-error",
8045
test_open_and_parse_question_parse_error);
8046
test_add_st("/task/open_and_parse_question/nosocket",
8047
test_open_and_parse_question_nosocket);
8048
test_add_st("/task/open_and_parse_question/badsocket",
8049
test_open_and_parse_question_badsocket);
8050
test_add_st("/task/open_and_parse_question/nopid",
8051
test_open_and_parse_question_nopid);
8052
test_add_st("/task/open_and_parse_question/badpid",
8053
test_open_and_parse_question_badpid);
8054
test_add_st("/task/open_and_parse_question/noexist_pid",
8055
test_open_and_parse_question_noexist_pid);
8056
test_add_st("/task/open_and_parse_question/no-notafter",
8057
test_open_and_parse_question_no_notafter);
8058
test_add_st("/task/open_and_parse_question/bad-notafter",
8059
test_open_and_parse_question_bad_notafter);
8060
test_add_st("/task/open_and_parse_question/notafter-0",
8061
test_open_and_parse_question_notafter_0);
8062
test_add_st("/task/open_and_parse_question/notafter-1",
8063
test_open_and_parse_question_notafter_1);
8064
test_add_st("/task/open_and_parse_question/notafter-1-1",
8065
test_open_and_parse_question_notafter_1_1);
8066
test_add_st("/task/open_and_parse_question/notafter-1-2",
8067
test_open_and_parse_question_notafter_1_2);
8068
test_add_st("/task/open_and_parse_question/equal-notafter",
8069
test_open_and_parse_question_equal_notafter);
8070
test_add_st("/task/open_and_parse_question/late-notafter",
8071
test_open_and_parse_question_late_notafter);
8072
test_add_st("/task/cancel_old_question/0-1-2",
8073
test_cancel_old_question_0_1_2);
8074
test_add_st("/task/cancel_old_question/0-2-1",
8075
test_cancel_old_question_0_2_1);
8076
test_add_st("/task/cancel_old_question/1-2-3",
8077
test_cancel_old_question_1_2_3);
8078
test_add_st("/task/cancel_old_question/1-3-2",
8079
test_cancel_old_question_1_3_2);
8080
test_add_st("/task/cancel_old_question/2-1-3",
8081
test_cancel_old_question_2_1_3);
8082
test_add_st("/task/cancel_old_question/2-3-1",
8083
test_cancel_old_question_2_3_1);
8084
test_add_st("/task/cancel_old_question/3-1-2",
8085
test_cancel_old_question_3_1_2);
8086
test_add_st("/task/cancel_old_question/3-2-1",
8087
test_cancel_old_question_3_2_1);
8088
test_add_st("/task/connect_question_socket/name-too-long",
8089
test_connect_question_socket_name_too_long);
8090
test_add_st("/task/connect_question_socket/connect-fail",
8091
test_connect_question_socket_connect_fail);
8092
test_add_st("/task/connect_question_socket/bad-epoll",
8093
test_connect_question_socket_bad_epoll);
8094
test_add_st("/task/connect_question_socket/usable",
8095
test_connect_question_socket_usable);
8096
test_add_st("/task/send_password_to_socket/client-not-exited",
8097
test_send_password_to_socket_client_not_exited);
8098
test_add_st("/task/send_password_to_socket/password-not-read",
8099
test_send_password_to_socket_password_not_read);
8100
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8101
test_send_password_to_socket_EMSGSIZE);
8102
test_add_st("/task/send_password_to_socket/retry",
8103
test_send_password_to_socket_retry);
8104
test_add_st("/task/send_password_to_socket/bad-epoll",
8105
test_send_password_to_socket_bad_epoll);
8106
test_add_st("/task/send_password_to_socket/null-password",
8107
test_send_password_to_socket_null_password);
8108
test_add_st("/task/send_password_to_socket/empty-password",
8109
test_send_password_to_socket_empty_password);
8110
test_add_st("/task/send_password_to_socket/empty-str-password",
8111
test_send_password_to_socket_empty_str_pass);
8112
test_add_st("/task/send_password_to_socket/text-password",
8113
test_send_password_to_socket_text_password);
8114
test_add_st("/task/send_password_to_socket/binary-password",
8115
test_send_password_to_socket_binary_password);
8116
test_add_st("/task/send_password_to_socket/nuls-in-password",
8117
test_send_password_to_socket_nuls_in_password);
8118
test_add_st("/task-creators/add_existing_questions/ENOENT",
8119
test_add_existing_questions_ENOENT);
8120
test_add_st("/task-creators/add_existing_questions/no-questions",
8121
test_add_existing_questions_no_questions);
8122
test_add_st("/task-creators/add_existing_questions/one-question",
8123
test_add_existing_questions_one_question);
8124
test_add_st("/task-creators/add_existing_questions/two-questions",
8125
test_add_existing_questions_two_questions);
8126
test_add_st("/task-creators/add_existing_questions/non-questions",
8127
test_add_existing_questions_non_questions);
8128
test_add_st("/task-creators/add_existing_questions/both-types",
8129
test_add_existing_questions_both_types);
8131
return g_test_run() == 0;
8134
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8135
GOptionContext *context = g_option_context_new("");
8137
g_option_context_set_help_enabled(context, FALSE);
8138
g_option_context_set_ignore_unknown_options(context, TRUE);
8140
gboolean should_run_tests = FALSE;
8141
GOptionEntry entries[] = {
8142
{ "test", 0, 0, G_OPTION_ARG_NONE,
8143
&should_run_tests, "Run tests", NULL },
8146
g_option_context_add_main_entries(context, entries, NULL);
8148
GError *error = NULL;
8150
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8151
g_option_context_free(context);
8152
g_error("Failed to parse options: %s", error->message);
8155
g_option_context_free(context);
8156
return should_run_tests != FALSE;