1
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019 Teddy Hogeborn
6
* Copyright © 2019 Björn Påhlsson
8
* This file is part of Mandos.
10
* Mandos is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* Mandos is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
* Contact the authors at <mandos@recompile.se>.
27
#include <inttypes.h> /* uintmax_t, PRIuMAX, PRIdMAX,
28
intmax_t, uint32_t, SCNx32,
30
#include <stddef.h> /* size_t */
31
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
33
#include <stdbool.h> /* bool, true, false */
34
#include <signal.h> /* struct sigaction, sigset_t,
35
sigemptyset(), sigaddset(),
36
SIGCHLD, pthread_sigmask(),
37
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
SA_NOCLDSTOP, sigfillset(), kill(),
39
SIGTERM, sigdelset(), SIGKILL,
40
NSIG, sigismember(), SA_ONSTACK,
41
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
44
malloc(), free(), strtoumax(),
45
realloc(), setenv(), calloc(),
46
mkdtemp(), mkostemp() */
47
#include <iso646.h> /* not, or, and, xor */
48
#include <error.h> /* error() */
49
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
#include <errno.h> /* errno, error_t, EACCES,
51
ENAMETOOLONG, ENOENT, EEXIST,
52
ECHILD, EPERM, ENOMEM, EAGAIN,
53
EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
77
fork(), _exit(), dup2(),
78
STDOUT_FILENO, setresgid(),
79
setresuid(), execv(), ssize_t,
80
read(), dup3(), getuid(), dup(),
81
STDERR_FILENO, pause(), write(),
82
rmdir(), unlink(), getpid() */
83
#include <sys/mman.h> /* munlock(), mlock() */
84
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
F_GETFD, F_GETFL, FD_CLOEXEC,
86
open(), O_WRONLY, O_RDONLY */
87
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
89
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
90
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
91
IN_CLOEXEC, inotify_add_watch(),
92
IN_CLOSE_WRITE, IN_MOVED_TO,
93
IN_DELETE, struct inotify_event */
94
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
95
#include <stdio.h> /* asprintf(), FILE, fopen(),
96
getline(), sscanf(), feof(),
97
ferror(), fclose(), stderr,
98
rename(), fdopen(), fprintf(),
100
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
GError, g_key_file_load_from_file(),
102
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
103
g_key_file_get_string(), guint64,
104
g_key_file_get_uint64(),
105
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
106
g_assert_true(), g_assert_nonnull(),
107
g_assert_null(), g_assert_false(),
108
g_assert_cmpint(), g_assert_cmpuint(),
109
g_test_skip(), g_assert_cmpstr(),
110
g_test_init(), g_test_add(), g_test_run(),
111
GOptionContext, g_option_context_new(),
112
g_option_context_set_help_enabled(), FALSE,
113
g_option_context_set_ignore_unknown_options(),
114
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
115
g_option_context_add_main_entries(),
116
g_option_context_parse(),
117
g_option_context_free(), g_error() */
118
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
119
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
120
SOCK_DGRAM, SOCK_NONBLOCK,
121
SOCK_CLOEXEC, connect(),
122
struct sockaddr, socklen_t,
123
shutdown(), SHUT_RD, send(),
124
MSG_NOSIGNAL, bind(), recv(),
126
#include <glob.h> /* globfree(), glob_t, glob(),
127
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
128
GLOB_ABORTED, GLOB_NOMATCH,
131
/* End of includes */
133
/* Start of declarations of private types and functions */
135
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
136
typedef uintmax_t mono_microsecs;
138
/* "task_queue" - A queue of tasks to be run */
140
struct task_struct *tasks; /* Tasks in this queue */
141
size_t length; /* Number of tasks */
142
/* Memory allocated for "tasks", in bytes */
144
/* Time when this queue should be run, at the latest */
145
mono_microsecs next_run;
146
} __attribute__((designated_init)) task_queue;
148
/* "func_type" - A function type for task functions
150
I.e. functions for the code which runs when a task is run, all have
152
typedef void (task_func) (const struct task_struct,
154
__attribute__((nonnull));
156
/* "buffer" - A data buffer for a growing array of bytes
158
Used for the "password" variable */
163
} __attribute__((designated_init)) buffer;
165
/* "string_set" - A set type which can contain strings
167
Used by the "cancelled_filenames" variable */
169
char *argz; /* Do not access these except in */
170
size_t argz_len; /* the string_set_* functions */
171
} __attribute__((designated_init)) string_set;
173
/* "task_context" - local variables for tasks
175
This data structure distinguishes between different tasks which are
176
using the same function. This data structure is passed to every
177
task function when each task is run.
179
Note that not every task uses every struct member. */
180
typedef struct task_struct {
181
task_func *const func; /* The function run by this task */
182
char *const question_filename; /* The question file */
183
const pid_t pid; /* Mandos client process ID */
184
const int epoll_fd; /* The epoll set file descriptor */
185
bool *const quit_now; /* Set to true on fatal errors */
186
const int fd; /* General purpose file descriptor */
187
bool *const mandos_client_exited; /* Set true when client exits */
188
buffer *const password; /* As read from client process */
189
bool *const password_is_read; /* "password" is done growing */
190
char *filename; /* General purpose file name */
191
/* A set of strings of all the file names of questions which have
192
been cancelled for any reason; tasks pertaining to these question
193
files should not be run */
194
string_set *const cancelled_filenames;
195
const mono_microsecs notafter; /* "NotAfter" from question file */
196
/* Updated before each queue run; is compared with queue.next_run */
197
const mono_microsecs *const current_time;
198
} __attribute__((designated_init)) task_context;
200
/* Declare all our functions here so we can define them in any order
201
below. Note: test functions are *not* declared here, they are
202
declared in the test section. */
203
__attribute__((warn_unused_result))
204
static bool should_only_run_tests(int *, char **[]);
205
__attribute__((warn_unused_result, cold))
206
static bool run_tests(int, char *[]);
207
static void handle_sigchld(__attribute__((unused)) int sig){}
208
__attribute__((warn_unused_result, malloc))
209
task_queue *create_queue(void);
210
__attribute__((nonnull, warn_unused_result))
211
bool add_to_queue(task_queue *const, const task_context);
212
__attribute__((nonnull))
213
void cleanup_task(const task_context *const);
214
__attribute__((nonnull))
215
void cleanup_queue(task_queue *const *const);
216
__attribute__((pure, nonnull, warn_unused_result))
217
bool queue_has_question(const task_queue *const);
218
__attribute__((nonnull))
219
void cleanup_close(const int *const);
220
__attribute__((nonnull))
221
void cleanup_string(char *const *const);
222
__attribute__((nonnull))
223
void cleanup_buffer(buffer *const);
224
__attribute__((pure, nonnull, warn_unused_result))
225
bool string_set_contains(const string_set, const char *const);
226
__attribute__((nonnull, warn_unused_result))
227
bool string_set_add(string_set *const, const char *const);
228
__attribute__((nonnull))
229
void string_set_clear(string_set *);
230
void string_set_swap(string_set *const, string_set *const);
231
__attribute__((nonnull, warn_unused_result))
232
bool start_mandos_client(task_queue *const, const int, bool *const,
233
bool *const, buffer *const, bool *const,
234
const struct sigaction *const,
235
const sigset_t, const char *const,
236
const uid_t, const gid_t,
237
const char *const *const);
238
__attribute__((nonnull))
239
task_func wait_for_mandos_client_exit;
240
__attribute__((nonnull))
241
task_func read_mandos_client_output;
242
__attribute__((warn_unused_result))
243
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
244
buffer *const, const char *const,
245
string_set *, const mono_microsecs *const,
246
bool *const, bool *const);
247
__attribute__((nonnull))
248
task_func read_inotify_event;
249
__attribute__((nonnull))
250
task_func open_and_parse_question;
251
__attribute__((nonnull))
252
task_func cancel_old_question;
253
__attribute__((nonnull))
254
task_func connect_question_socket;
255
__attribute__((nonnull))
256
task_func send_password_to_socket;
257
__attribute__((warn_unused_result))
258
bool add_existing_questions(task_queue *const, const int,
259
buffer *const, string_set *,
260
const mono_microsecs *const,
261
bool *const, bool *const,
263
__attribute__((nonnull, warn_unused_result))
264
bool wait_for_event(const int, const mono_microsecs,
265
const mono_microsecs);
266
bool run_queue(task_queue **const, string_set *const, bool *const);
267
bool clear_all_fds_from_epoll_set(const int);
268
mono_microsecs get_current_time(void);
269
__attribute__((nonnull, warn_unused_result))
270
bool setup_signal_handler(struct sigaction *const);
271
__attribute__((nonnull))
272
bool restore_signal_handler(const struct sigaction *const);
273
__attribute__((nonnull, warn_unused_result))
274
bool block_sigchld(sigset_t *const);
275
__attribute__((nonnull))
276
bool restore_sigmask(const sigset_t *const);
277
__attribute__((nonnull))
278
bool parse_arguments(int, char *[], const bool, char **, char **,
279
uid_t *const , gid_t *const, char **, size_t *);
281
/* End of declarations of private types and functions */
283
/* Start of "main" section; this section LACKS TESTS!
285
Code here should be as simple as possible. */
287
/* These are required to be global by Argp */
288
const char *argp_program_version = "password-agent " VERSION;
289
const char *argp_program_bug_address = "<mandos@recompile.se>";
291
int main(int argc, char *argv[]){
293
/* If the --test option is passed, skip all normal operations and
294
instead only run the run_tests() function, which also does all
295
its own option parsing, so we don't have to do anything here. */
296
if(should_only_run_tests(&argc, &argv)){
297
if(run_tests(argc, argv)){
298
return EXIT_SUCCESS; /* All tests successful */
300
return EXIT_FAILURE; /* Some test(s) failed */
303
__attribute__((cleanup(cleanup_string)))
304
char *agent_directory = NULL;
306
__attribute__((cleanup(cleanup_string)))
307
char *helper_directory = NULL;
312
__attribute__((cleanup(cleanup_string)))
313
char *mandos_argz = NULL;
314
size_t mandos_argz_length = 0;
316
if(not parse_arguments(argc, argv, true, &agent_directory,
317
&helper_directory, &user, &group,
318
&mandos_argz, &mandos_argz_length)){
319
/* This should never happen, since "true" is passed as the third
320
argument to parse_arguments() above, which should make
321
argp_parse() call exit() if any parsing error occurs. */
322
error(EX_USAGE, errno, "Failed to parse arguments");
325
const char default_agent_directory[] = "/run/systemd/ask-password";
326
const char default_helper_directory[]
327
= "/lib/mandos/plugin-helpers";
328
const char *const default_argv[]
329
= {"/lib/mandos/plugins.d/mandos-client", NULL };
331
/* Set variables to default values if unset */
332
if(agent_directory == NULL){
333
agent_directory = strdup(default_agent_directory);
334
if(agent_directory == NULL){
335
error(EX_OSERR, errno, "Failed strdup()");
338
if(helper_directory == NULL){
339
helper_directory = strdup(default_helper_directory);
340
if(helper_directory == NULL){
341
error(EX_OSERR, errno, "Failed strdup()");
345
user = 65534; /* nobody */
348
group = 65534; /* nogroup */
350
/* If parse_opt did not create an argz vector, create one with
352
if(mandos_argz == NULL){
354
#pragma GCC diagnostic push
355
/* argz_create() takes a non-const argv for some unknown reason -
356
argz_create() isn't modifying the strings, just copying them.
357
Therefore, this cast to non-const should be safe. */
358
#pragma GCC diagnostic ignored "-Wcast-qual"
360
errno = argz_create((char *const *)default_argv, &mandos_argz,
361
&mandos_argz_length);
363
#pragma GCC diagnostic pop
366
error(EX_OSERR, errno, "Failed argz_create()");
369
/* Use argz vector to create a normal argv, usable by execv() */
371
char **mandos_argv = malloc((argz_count(mandos_argz,
373
+ 1) * sizeof(char *));
374
if(mandos_argv == NULL){
375
error_t saved_errno = errno;
377
error(EX_OSERR, saved_errno, "Failed malloc()");
379
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
381
sigset_t orig_sigmask;
382
if(not block_sigchld(&orig_sigmask)){
386
struct sigaction old_sigchld_action;
387
if(not setup_signal_handler(&old_sigchld_action)){
391
mono_microsecs current_time = 0;
393
bool mandos_client_exited = false;
394
bool quit_now = false;
395
__attribute__((cleanup(cleanup_close)))
396
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
398
error(EX_OSERR, errno, "Failed to create epoll set fd");
400
__attribute__((cleanup(cleanup_queue)))
401
task_queue *queue = create_queue();
403
error(EX_OSERR, errno, "Failed to create task queue");
406
__attribute__((cleanup(cleanup_buffer)))
407
buffer password = {};
408
bool password_is_read = false;
410
__attribute__((cleanup(string_set_clear)))
411
string_set cancelled_filenames = {};
413
/* Add tasks to queue */
414
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
415
&quit_now, &password, &password_is_read,
416
&old_sigchld_action, orig_sigmask,
417
helper_directory, user, group,
418
(const char *const *)mandos_argv)){
419
return EX_OSERR; /* Error has already been printed */
421
/* These variables were only for start_mandos_client() and are not
426
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
427
agent_directory, &cancelled_filenames,
428
¤t_time, &mandos_client_exited,
430
switch(errno){ /* Error has already been printed */
440
if(not add_existing_questions(queue, epoll_fd, &password,
441
&cancelled_filenames, ¤t_time,
442
&mandos_client_exited,
443
&password_is_read, agent_directory)){
444
return EXIT_FAILURE; /* Error has already been printed */
449
current_time = get_current_time();
450
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
451
const error_t saved_errno = errno;
452
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
456
current_time = get_current_time();
457
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
458
const error_t saved_errno = errno;
459
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
462
/* When no tasks about questions are left in the queue, break out
463
of the loop (and implicitly exit the program) */
464
} while(queue_has_question(queue));
466
restore_signal_handler(&old_sigchld_action);
467
restore_sigmask(&orig_sigmask);
472
__attribute__((warn_unused_result))
473
mono_microsecs get_current_time(void){
474
struct timespec currtime;
475
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
476
error(0, errno, "Failed to get current time");
479
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
480
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
483
/* End of "main" section */
485
/* Start of regular code section; ALL this code has tests */
487
__attribute__((nonnull))
488
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
489
char **agent_directory, char **helper_directory,
490
uid_t *const user, gid_t *const group,
491
char **mandos_argz, size_t *mandos_argz_length){
493
const struct argp_option options[] = {
494
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
495
.doc="Systemd password agent directory" },
496
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
497
.doc="Mandos Client password helper directory" },
498
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
499
.flags=OPTION_HIDDEN | OPTION_ALIAS },
500
{ .name="user", .key='u', .arg="USERID",
501
.doc="User ID the Mandos Client will use as its unprivileged"
503
{ .name="userid", .key=130, /* From plugin--runner */
504
.flags=OPTION_HIDDEN | OPTION_ALIAS },
505
{ .name="group", .key='g', .arg="GROUPID",
506
.doc="Group ID the Mandos Client will use as its unprivileged"
508
{ .name="groupid", .key=131, /* From plugin--runner */
509
.flags=OPTION_HIDDEN | OPTION_ALIAS },
510
{ .name="test", .key=255, /* See should_only_run_tests() */
511
.doc="Skip normal operation, and only run self-tests. See"
512
" --test --help.", .group=10, },
516
__attribute__((nonnull(3)))
517
error_t parse_opt(int key, char *arg, struct argp_state *state){
520
case 'd': /* --agent-directory */
521
*agent_directory = strdup(arg);
523
case 128: /* --helper-directory */
524
case 129: /* --plugin-helper-dir */
525
*helper_directory = strdup(arg);
527
case 'u': /* --user */
528
case 130: /* --userid */
531
uintmax_t tmp_id = 0;
533
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
534
if(errno != 0 or tmp == arg or *tmp != '\0'
535
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
536
return ARGP_ERR_UNKNOWN;
538
*user = (uid_t)tmp_id;
542
case 'g': /* --group */
543
case 131: /* --groupid */
546
uintmax_t tmp_id = 0;
548
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
549
if(errno != 0 or tmp == arg or *tmp != '\0'
550
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
551
return ARGP_ERR_UNKNOWN;
553
*group = (gid_t)tmp_id;
558
/* Copy arguments into argz vector */
559
return argz_create(state->argv + state->next, mandos_argz,
562
return ARGP_ERR_UNKNOWN;
567
const struct argp argp = {
570
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
571
.doc = "Mandos password agent -- runs Mandos client as a"
572
" systemd password agent",
575
errno = argp_parse(&argp, argc, argv,
576
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
581
__attribute__((nonnull, warn_unused_result))
582
bool block_sigchld(sigset_t *const orig_sigmask){
583
sigset_t sigchld_sigmask;
584
if(sigemptyset(&sigchld_sigmask) < 0){
585
error(0, errno, "Failed to empty signal set");
588
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
589
error(0, errno, "Failed to add SIGCHLD to signal set");
592
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
593
error(0, errno, "Failed to block SIGCHLD signal");
599
__attribute__((nonnull, warn_unused_result, const))
600
bool restore_sigmask(const sigset_t *const orig_sigmask){
601
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
602
error(0, errno, "Failed to restore blocked signals");
608
__attribute__((nonnull, warn_unused_result))
609
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
610
struct sigaction sigchld_action = {
611
.sa_handler=handle_sigchld,
612
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
614
/* Set all signals in "sa_mask" struct member; this makes all
615
signals automatically blocked during signal handler */
616
if(sigfillset(&sigchld_action.sa_mask) != 0){
617
error(0, errno, "Failed to do sigfillset()");
620
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
621
error(0, errno, "Failed to set SIGCHLD signal handler");
627
__attribute__((nonnull, warn_unused_result))
628
bool restore_signal_handler(const struct sigaction *const
630
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
631
error(0, errno, "Failed to restore signal handler");
637
__attribute__((warn_unused_result, malloc))
638
task_queue *create_queue(void){
639
task_queue *queue = malloc(sizeof(task_queue));
643
queue->allocated = 0;
649
__attribute__((nonnull, warn_unused_result))
650
bool add_to_queue(task_queue *const queue, const task_context task){
651
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
652
if(needed_size > (queue->allocated)){
653
task_context *const new_tasks = realloc(queue->tasks,
655
if(new_tasks == NULL){
656
error(0, errno, "Failed to allocate %" PRIuMAX
657
" bytes for queue->tasks", (uintmax_t)needed_size);
660
queue->tasks = new_tasks;
661
queue->allocated = needed_size;
663
/* Using memcpy here is necessary because doing */
664
/* queue->tasks[queue->length++] = task; */
665
/* would violate const-ness of task members */
666
memcpy(&(queue->tasks[queue->length++]), &task,
667
sizeof(task_context));
671
__attribute__((nonnull))
672
void cleanup_task(const task_context *const task){
673
const error_t saved_errno = errno;
674
/* free and close all task data */
675
free(task->question_filename);
676
if(task->filename != task->question_filename){
677
free(task->filename);
680
kill(task->pid, SIGTERM);
688
__attribute__((nonnull))
689
void free_queue(task_queue *const queue){
694
__attribute__((nonnull))
695
void cleanup_queue(task_queue *const *const queue){
699
for(size_t i = 0; i < (*queue)->length; i++){
700
const task_context *const task = ((*queue)->tasks)+i;
706
__attribute__((pure, nonnull, warn_unused_result))
707
bool queue_has_question(const task_queue *const queue){
708
for(size_t i=0; i < queue->length; i++){
709
if(queue->tasks[i].question_filename != NULL){
716
__attribute__((nonnull))
717
void cleanup_close(const int *const fd){
718
const error_t saved_errno = errno;
723
__attribute__((nonnull))
724
void cleanup_string(char *const *const ptr){
728
__attribute__((nonnull))
729
void cleanup_buffer(buffer *buf){
730
if(buf->allocated > 0){
731
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
732
explicit_bzero(buf->data, buf->allocated);
734
memset(buf->data, '\0', buf->allocated);
737
if(buf->data != NULL){
738
if(munlock(buf->data, buf->allocated) != 0){
739
error(0, errno, "Failed to unlock memory of old buffer");
748
__attribute__((pure, nonnull, warn_unused_result))
749
bool string_set_contains(const string_set set, const char *const str){
750
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
751
s = argz_next(set.argz, set.argz_len, s)){
752
if(strcmp(s, str) == 0){
759
__attribute__((nonnull, warn_unused_result))
760
bool string_set_add(string_set *const set, const char *const str){
761
if(string_set_contains(*set, str)){
764
error_t error = argz_add(&set->argz, &set->argz_len, str);
772
__attribute__((nonnull))
773
void string_set_clear(string_set *set){
779
__attribute__((nonnull))
780
void string_set_swap(string_set *const set1, string_set *const set2){
781
/* Swap contents of two string sets */
783
char *const tmp_argz = set1->argz;
784
set1->argz = set2->argz;
785
set2->argz = tmp_argz;
788
const size_t tmp_argz_len = set1->argz_len;
789
set1->argz_len = set2->argz_len;
790
set2->argz_len = tmp_argz_len;
794
__attribute__((nonnull, warn_unused_result))
795
bool start_mandos_client(task_queue *const queue,
797
bool *const mandos_client_exited,
798
bool *const quit_now, buffer *const password,
799
bool *const password_is_read,
800
const struct sigaction *const
801
old_sigchld_action, const sigset_t sigmask,
802
const char *const helper_directory,
803
const uid_t user, const gid_t group,
804
const char *const *const argv){
806
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
807
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
811
const pid_t pid = fork();
813
if(not restore_signal_handler(old_sigchld_action)){
816
if(not restore_sigmask(&sigmask)){
819
if(close(pipefds[0]) != 0){
820
error(0, errno, "Failed to close() parent pipe fd");
823
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
824
error(0, errno, "Failed to dup2() pipe fd to stdout");
827
if(close(pipefds[1]) != 0){
828
error(0, errno, "Failed to close() old child pipe fd");
831
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
832
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
833
" \"%s\", 1)", helper_directory);
836
if(group != 0 and setresgid(group, 0, 0) == -1){
837
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
838
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
841
if(user != 0 and setresuid(user, 0, 0) == -1){
842
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
843
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
847
#pragma GCC diagnostic push
848
/* For historical reasons, the "argv" argument to execv() is not
849
const, but it is safe to override this. */
850
#pragma GCC diagnostic ignored "-Wcast-qual"
852
execv(argv[0], (char **)argv);
854
#pragma GCC diagnostic pop
856
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
861
if(not add_to_queue(queue, (task_context){
862
.func=wait_for_mandos_client_exit,
864
.mandos_client_exited=mandos_client_exited,
867
error(0, errno, "Failed to add wait_for_mandos_client to queue");
872
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
873
&(struct epoll_event)
874
{ .events=EPOLLIN | EPOLLRDHUP });
875
if(ret != 0 and errno != EEXIST){
876
error(0, errno, "Failed to add file descriptor to epoll set");
881
return add_to_queue(queue, (task_context){
882
.func=read_mandos_client_output,
887
.password_is_read=password_is_read,
891
__attribute__((nonnull))
892
void wait_for_mandos_client_exit(const task_context task,
893
task_queue *const queue){
894
const pid_t pid = task.pid;
895
bool *const mandos_client_exited = task.mandos_client_exited;
896
bool *const quit_now = task.quit_now;
899
switch(waitpid(pid, &status, WNOHANG)){
900
case 0: /* Not exited yet */
901
if(not add_to_queue(queue, task)){
902
error(0, errno, "Failed to add myself to queue");
907
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
913
default: /* Has exited */
914
*mandos_client_exited = true;
915
if((not WIFEXITED(status))
916
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
917
error(0, 0, "Mandos client failed or was killed");
923
__attribute__((nonnull))
924
void read_mandos_client_output(const task_context task,
925
task_queue *const queue){
926
buffer *const password = task.password;
927
bool *const quit_now = task.quit_now;
928
bool *const password_is_read = task.password_is_read;
929
const int fd = task.fd;
930
const int epoll_fd = task.epoll_fd;
932
const size_t new_potential_size = (password->length + PIPE_BUF);
933
if(password->allocated < new_potential_size){
934
char *const new_buffer = calloc(new_potential_size, 1);
935
if(new_buffer == NULL){
936
error(0, errno, "Failed to allocate %" PRIuMAX
937
" bytes for password", (uintmax_t)new_potential_size);
942
if(mlock(new_buffer, new_potential_size) != 0){
943
/* Warn but do not treat as fatal error */
944
if(errno != EPERM and errno != ENOMEM){
945
error(0, errno, "Failed to lock memory for password");
948
if(password->length > 0){
949
memcpy(new_buffer, password->data, password->length);
950
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
951
explicit_bzero(password->data, password->allocated);
953
memset(password->data, '\0', password->allocated);
956
if(password->data != NULL){
957
if(munlock(password->data, password->allocated) != 0){
958
error(0, errno, "Failed to unlock memory of old buffer");
960
free(password->data);
962
password->data = new_buffer;
963
password->allocated = new_potential_size;
966
const ssize_t read_length = read(fd, password->data
967
+ password->length, PIPE_BUF);
969
if(read_length == 0){ /* EOF */
970
*password_is_read = true;
974
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
975
error(0, errno, "Failed to read password from Mandos client");
980
if(read_length > 0){ /* Data has been read */
981
password->length += (size_t)read_length;
984
/* Either data was read, or EAGAIN was indicated, meaning no data
987
/* Re-add the fd to the epoll set */
988
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
989
&(struct epoll_event)
990
{ .events=EPOLLIN | EPOLLRDHUP });
991
if(ret != 0 and errno != EEXIST){
992
error(0, errno, "Failed to re-add file descriptor to epoll set");
998
/* Re-add myself to the queue */
999
if(not add_to_queue(queue, task)){
1000
error(0, errno, "Failed to add myself to queue");
1006
__attribute__((nonnull, warn_unused_result))
1007
bool add_inotify_dir_watch(task_queue *const queue,
1008
const int epoll_fd, bool *const quit_now,
1009
buffer *const password,
1010
const char *const dir,
1011
string_set *cancelled_filenames,
1012
const mono_microsecs *const current_time,
1013
bool *const mandos_client_exited,
1014
bool *const password_is_read){
1015
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1017
error(0, errno, "Failed to create inotify instance");
1021
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1022
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1025
error(0, errno, "Failed to create inotify watch on %s", dir);
1029
/* Add the inotify fd to the epoll set */
1030
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1031
&(struct epoll_event)
1032
{ .events=EPOLLIN | EPOLLRDHUP });
1033
if(ret != 0 and errno != EEXIST){
1034
error(0, errno, "Failed to add file descriptor to epoll set");
1039
const task_context read_inotify_event_task = {
1040
.func=read_inotify_event,
1045
.filename=strdup(dir),
1046
.cancelled_filenames=cancelled_filenames,
1047
.current_time=current_time,
1048
.mandos_client_exited=mandos_client_exited,
1049
.password_is_read=password_is_read,
1051
if(read_inotify_event_task.filename == NULL){
1052
error(0, errno, "Failed to strdup(\"%s\")", dir);
1057
return add_to_queue(queue, read_inotify_event_task);
1060
__attribute__((nonnull))
1061
void read_inotify_event(const task_context task,
1062
task_queue *const queue){
1063
const int fd = task.fd;
1064
const int epoll_fd = task.epoll_fd;
1065
char *const filename = task.filename;
1066
bool *quit_now = task.quit_now;
1067
buffer *const password = task.password;
1068
string_set *const cancelled_filenames = task.cancelled_filenames;
1069
const mono_microsecs *const current_time = task.current_time;
1070
bool *const mandos_client_exited = task.mandos_client_exited;
1071
bool *const password_is_read = task.password_is_read;
1073
/* "sufficient to read at least one event." - inotify(7) */
1074
const size_t ievent_size = (sizeof(struct inotify_event)
1077
struct inotify_event event;
1078
char name_buffer[NAME_MAX + 1];
1080
struct inotify_event *const ievent = &ievent_buffer.event;
1082
const ssize_t read_length = read(fd, ievent, ievent_size);
1083
if(read_length == 0){ /* EOF */
1084
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1086
cleanup_task(&task);
1089
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1090
error(0, errno, "Failed to read from inotify fd for directory %s",
1093
cleanup_task(&task);
1096
if(read_length > 0 /* Data has been read */
1097
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1098
char *question_filename = NULL;
1099
const ssize_t question_filename_length
1100
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1101
if(question_filename_length < 0){
1102
error(0, errno, "Failed to create file name from directory name"
1103
" %s and file name %s", filename, ievent->name);
1105
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1106
if(not add_to_queue(queue, (task_context){
1107
.func=open_and_parse_question,
1109
.question_filename=question_filename,
1110
.filename=question_filename,
1112
.cancelled_filenames=cancelled_filenames,
1113
.current_time=current_time,
1114
.mandos_client_exited=mandos_client_exited,
1115
.password_is_read=password_is_read,
1117
error(0, errno, "Failed to add open_and_parse_question task"
1118
" for file name %s to queue", filename);
1120
/* Force the added task (open_and_parse_question) to run
1122
queue->next_run = 1;
1124
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1125
if(not string_set_add(cancelled_filenames,
1126
question_filename)){
1127
error(0, errno, "Could not add question %s to"
1128
" cancelled_questions", question_filename);
1130
free(question_filename);
1131
cleanup_task(&task);
1134
free(question_filename);
1139
/* Either data was read, or EAGAIN was indicated, meaning no data
1142
/* Re-add myself to the queue */
1143
if(not add_to_queue(queue, task)){
1144
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1145
" queue", filename);
1147
cleanup_task(&task);
1151
/* Re-add the fd to the epoll set */
1152
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1153
&(struct epoll_event)
1154
{ .events=EPOLLIN | EPOLLRDHUP });
1155
if(ret != 0 and errno != EEXIST){
1156
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1157
" directory %s to epoll set", fd, filename);
1158
/* Force the added task (read_inotify_event) to run again, at most
1159
one second from now */
1160
if((queue->next_run == 0)
1161
or (queue->next_run > (*current_time + 1000000))){
1162
queue->next_run = *current_time + 1000000;
1167
__attribute__((nonnull))
1168
void open_and_parse_question(const task_context task,
1169
task_queue *const queue){
1170
__attribute__((cleanup(cleanup_string)))
1171
char *question_filename = task.question_filename;
1172
const int epoll_fd = task.epoll_fd;
1173
buffer *const password = task.password;
1174
string_set *const cancelled_filenames = task.cancelled_filenames;
1175
const mono_microsecs *const current_time = task.current_time;
1176
bool *const mandos_client_exited = task.mandos_client_exited;
1177
bool *const password_is_read = task.password_is_read;
1179
/* We use the GLib "Key-value file parser" functions to parse the
1180
question file. See <https://www.freedesktop.org/wiki/Software
1181
/systemd/PasswordAgents/> for specification of contents */
1182
__attribute__((nonnull))
1183
void cleanup_g_key_file(GKeyFile **key_file){
1184
if(*key_file != NULL){
1185
g_key_file_free(*key_file);
1189
__attribute__((cleanup(cleanup_g_key_file)))
1190
GKeyFile *key_file = g_key_file_new();
1191
if(key_file == NULL){
1192
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1196
GError *glib_error = NULL;
1197
if(g_key_file_load_from_file(key_file, question_filename,
1198
G_KEY_FILE_NONE, &glib_error) != TRUE){
1199
/* If a file was removed, we should ignore it, so */
1200
/* only show error message if file actually existed */
1201
if(glib_error->code != G_FILE_ERROR_NOENT){
1202
error(0, 0, "Failed to load question data from file \"%s\": %s",
1203
question_filename, glib_error->message);
1208
__attribute__((cleanup(cleanup_string)))
1209
char *socket_name = g_key_file_get_string(key_file, "Ask",
1212
if(socket_name == NULL){
1213
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1214
question_filename, glib_error->message);
1218
if(strlen(socket_name) == 0){
1219
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1224
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1226
if(glib_error != NULL){
1227
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1228
question_filename, glib_error->message);
1232
if((pid != (guint64)((pid_t)pid))
1233
or (kill((pid_t)pid, 0) != 0)){
1234
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1235
" does not exist", (uintmax_t)pid, question_filename);
1239
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1240
"NotAfter", &glib_error);
1241
if(glib_error != NULL){
1242
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1243
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1244
" %s", question_filename, glib_error->message);
1249
if(queue->next_run == 0 or (queue->next_run > notafter)){
1250
queue->next_run = notafter;
1252
if(*current_time >= notafter){
1257
const task_context connect_question_socket_task = {
1258
.func=connect_question_socket,
1259
.question_filename=strdup(question_filename),
1262
.filename=strdup(socket_name),
1263
.cancelled_filenames=task.cancelled_filenames,
1264
.mandos_client_exited=mandos_client_exited,
1265
.password_is_read=password_is_read,
1266
.current_time=current_time,
1268
if(connect_question_socket_task.question_filename == NULL
1269
or connect_question_socket_task.filename == NULL
1270
or not add_to_queue(queue, connect_question_socket_task)){
1271
error(0, errno, "Failed to add connect_question_socket for socket"
1272
" %s (from \"%s\") to queue", socket_name,
1274
cleanup_task(&connect_question_socket_task);
1277
/* Force the added task (connect_question_socket) to run
1279
queue->next_run = 1;
1282
char *const dup_filename = strdup(question_filename);
1283
const task_context cancel_old_question_task = {
1284
.func=cancel_old_question,
1285
.question_filename=dup_filename,
1287
.filename=dup_filename,
1288
.cancelled_filenames=cancelled_filenames,
1289
.current_time=current_time,
1291
if(cancel_old_question_task.question_filename == NULL
1292
or not add_to_queue(queue, cancel_old_question_task)){
1293
error(0, errno, "Failed to add cancel_old_question for file "
1294
"\"%s\" to queue", question_filename);
1295
cleanup_task(&cancel_old_question_task);
1301
__attribute__((nonnull))
1302
void cancel_old_question(const task_context task,
1303
task_queue *const queue){
1304
char *const question_filename = task.question_filename;
1305
string_set *const cancelled_filenames = task.cancelled_filenames;
1306
const mono_microsecs notafter = task.notafter;
1307
const mono_microsecs *const current_time = task.current_time;
1309
if(*current_time >= notafter){
1310
if(not string_set_add(cancelled_filenames, question_filename)){
1311
error(0, errno, "Failed to cancel question for file %s",
1314
cleanup_task(&task);
1318
if(not add_to_queue(queue, task)){
1319
error(0, errno, "Failed to add cancel_old_question for file "
1320
"%s to queue", question_filename);
1321
cleanup_task(&task);
1325
if((queue->next_run == 0) or (queue->next_run > notafter)){
1326
queue->next_run = notafter;
1330
__attribute__((nonnull))
1331
void connect_question_socket(const task_context task,
1332
task_queue *const queue){
1333
char *const question_filename = task.question_filename;
1334
char *const filename = task.filename;
1335
const int epoll_fd = task.epoll_fd;
1336
buffer *const password = task.password;
1337
string_set *const cancelled_filenames = task.cancelled_filenames;
1338
bool *const mandos_client_exited = task.mandos_client_exited;
1339
bool *const password_is_read = task.password_is_read;
1340
const mono_microsecs *const current_time = task.current_time;
1342
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1344
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1345
error(0, 0, "Socket filename is larger than"
1346
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1347
(uintmax_t)sizeof(sock_name.sun_path), filename);
1348
if(not string_set_add(cancelled_filenames, question_filename)){
1349
error(0, errno, "Failed to cancel question for file %s",
1352
cleanup_task(&task);
1356
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1357
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1360
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1361
if(not add_to_queue(queue, task)){
1362
error(0, errno, "Failed to add connect_question_socket for file"
1363
" \"%s\" and socket \"%s\" to queue", question_filename,
1365
cleanup_task(&task);
1367
/* Force the added task (connect_question_socket) to run
1369
queue->next_run = 1;
1374
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1375
if(connect(fd, (struct sockaddr *)&sock_name,
1376
(socklen_t)SUN_LEN(&sock_name)) != 0){
1377
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1378
if(not add_to_queue(queue, task)){
1379
error(0, errno, "Failed to add connect_question_socket for file"
1380
" \"%s\" and socket \"%s\" to queue", question_filename,
1382
cleanup_task(&task);
1384
/* Force the added task (connect_question_socket) to run again,
1385
at most one second from now */
1386
if((queue->next_run == 0)
1387
or (queue->next_run > (*current_time + 1000000))){
1388
queue->next_run = *current_time + 1000000;
1394
/* Not necessary, but we can try, and merely warn on failure */
1395
if(shutdown(fd, SHUT_RD) != 0){
1396
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1400
/* Add the fd to the epoll set */
1401
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1402
&(struct epoll_event){ .events=EPOLLOUT })
1404
error(0, errno, "Failed to add inotify file descriptor %d for"
1405
" socket %s to epoll set", fd, filename);
1406
if(not add_to_queue(queue, task)){
1407
error(0, errno, "Failed to add connect_question_socket for file"
1408
" \"%s\" and socket \"%s\" to queue", question_filename,
1410
cleanup_task(&task);
1412
/* Force the added task (connect_question_socket) to run again,
1413
at most one second from now */
1414
if((queue->next_run == 0)
1415
or (queue->next_run > (*current_time + 1000000))){
1416
queue->next_run = *current_time + 1000000;
1422
/* add task send_password_to_socket to queue */
1423
const task_context send_password_to_socket_task = {
1424
.func=send_password_to_socket,
1425
.question_filename=question_filename,
1430
.cancelled_filenames=cancelled_filenames,
1431
.mandos_client_exited=mandos_client_exited,
1432
.password_is_read=password_is_read,
1433
.current_time=current_time,
1436
if(not add_to_queue(queue, send_password_to_socket_task)){
1437
error(0, errno, "Failed to add send_password_to_socket for"
1438
" file \"%s\" and socket \"%s\" to queue",
1439
question_filename, filename);
1440
cleanup_task(&send_password_to_socket_task);
1444
__attribute__((nonnull))
1445
void send_password_to_socket(const task_context task,
1446
task_queue *const queue){
1447
char *const question_filename=task.question_filename;
1448
char *const filename=task.filename;
1449
const int epoll_fd=task.epoll_fd;
1450
const int fd=task.fd;
1451
buffer *const password=task.password;
1452
string_set *const cancelled_filenames=task.cancelled_filenames;
1453
bool *const mandos_client_exited = task.mandos_client_exited;
1454
bool *const password_is_read = task.password_is_read;
1455
const mono_microsecs *const current_time = task.current_time;
1457
if(*mandos_client_exited and *password_is_read){
1459
const size_t send_buffer_length = password->length + 2;
1460
char *send_buffer = malloc(send_buffer_length);
1461
if(send_buffer == NULL){
1462
error(0, errno, "Failed to allocate send_buffer");
1464
if(mlock(send_buffer, send_buffer_length) != 0){
1465
/* Warn but do not treat as fatal error */
1466
if(errno != EPERM and errno != ENOMEM){
1467
error(0, errno, "Failed to lock memory for password"
1471
/* “[…] send a single datagram to the socket consisting of the
1472
password string either prefixed with "+" or with "-"
1473
depending on whether the password entry was successful or
1474
not. You may but don't have to include a final NUL byte in
1477
— <https://www.freedesktop.org/wiki/Software/systemd/
1478
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1480
send_buffer[0] = '+'; /* Prefix with "+" */
1481
/* Always add an extra NUL */
1482
send_buffer[password->length + 1] = '\0';
1483
if(password->length > 0){
1484
memcpy(send_buffer + 1, password->data, password->length);
1487
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1489
const error_t saved_errno = errno;
1490
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1491
explicit_bzero(send_buffer, send_buffer_length);
1493
memset(send_buffer, '\0', send_buffer_length);
1495
if(munlock(send_buffer, send_buffer_length) != 0){
1496
error(0, errno, "Failed to unlock memory of send buffer");
1499
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1500
switch(saved_errno){
1513
error(0, 0, "Password of size %" PRIuMAX " is too big",
1514
(uintmax_t)password->length);
1518
__attribute__((fallthrough));
1521
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1522
error(0, 0, "Password only partially sent to socket");
1527
__attribute__((fallthrough));
1530
error(0, saved_errno, "Failed to send() to socket %s",
1532
if(not string_set_add(cancelled_filenames,
1533
question_filename)){
1534
error(0, errno, "Failed to cancel question for file %s",
1537
cleanup_task(&task);
1542
cleanup_task(&task);
1548
/* We failed or are not ready yet; retry later */
1550
if(not add_to_queue(queue, task)){
1551
error(0, errno, "Failed to add send_password_to_socket for"
1552
" file %s and socket %s to queue", question_filename,
1554
cleanup_task(&task);
1557
/* Add the fd to the epoll set */
1558
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1559
&(struct epoll_event){ .events=EPOLLOUT })
1561
error(0, errno, "Failed to add socket file descriptor %d for"
1562
" socket %s to epoll set", fd, filename);
1563
/* Force the added task (send_password_to_socket) to run again, at
1564
most one second from now */
1565
if((queue->next_run == 0)
1566
or (queue->next_run > (*current_time + 1000000))){
1567
queue->next_run = *current_time + 1000000;
1572
__attribute__((warn_unused_result))
1573
bool add_existing_questions(task_queue *const queue,
1575
buffer *const password,
1576
string_set *cancelled_filenames,
1577
const mono_microsecs *const current_time,
1578
bool *const mandos_client_exited,
1579
bool *const password_is_read,
1580
const char *const dirname){
1581
__attribute__((cleanup(cleanup_string)))
1582
char *dir_pattern = NULL;
1583
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1584
if(ret < 0 or dir_pattern == NULL){
1585
error(0, errno, "Could not create glob pattern for directory %s",
1589
__attribute__((cleanup(globfree)))
1590
glob_t question_filenames = {};
1591
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1592
NULL, &question_filenames)){
1595
error(0, errno, "Failed to open directory %s", dirname);
1598
error(0, errno, "There are no question files in %s", dirname);
1601
error(0, errno, "Could not allocate memory for question file"
1602
" names in %s", dirname);
1606
__attribute__((fallthrough));
1609
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1610
char *const question_filename = strdup(question_filenames
1612
const task_context task = {
1613
.func=open_and_parse_question,
1615
.question_filename=question_filename,
1616
.filename=question_filename,
1618
.cancelled_filenames=cancelled_filenames,
1619
.current_time=current_time,
1620
.mandos_client_exited=mandos_client_exited,
1621
.password_is_read=password_is_read,
1624
if(question_filename == NULL
1625
or not add_to_queue(queue, task)){
1626
error(0, errno, "Failed to add open_and_parse_question for"
1627
" file %s to queue",
1628
question_filenames.gl_pathv[i]);
1629
free(question_filename);
1631
queue->next_run = 1;
1638
__attribute__((nonnull, warn_unused_result))
1639
bool wait_for_event(const int epoll_fd,
1640
const mono_microsecs queue_next_run,
1641
const mono_microsecs current_time){
1642
__attribute__((const))
1643
int milliseconds_to_wait(const mono_microsecs currtime,
1644
const mono_microsecs nextrun){
1645
if(currtime >= nextrun){
1648
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1649
if(wait_time_ms > (uintmax_t)INT_MAX){
1652
return (int)wait_time_ms;
1655
const int wait_time_ms = milliseconds_to_wait(current_time,
1658
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1659
sigset_t temporary_unblocked_sigmask;
1660
/* Get current signal mask */
1661
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1664
/* Remove SIGCHLD from the signal mask */
1665
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1668
struct epoll_event events[8]; /* Ignored */
1669
int ret = epoll_pwait(epoll_fd, events,
1670
sizeof(events) / sizeof(struct epoll_event),
1671
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1672
&temporary_unblocked_sigmask);
1673
if(ret < 0 and errno != EINTR){
1674
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1676
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1679
return clear_all_fds_from_epoll_set(epoll_fd);
1682
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1683
/* Create a new empty epoll set */
1684
__attribute__((cleanup(cleanup_close)))
1685
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1686
if(new_epoll_fd < 0){
1689
/* dup3() the new epoll set fd over the old one, replacing it */
1690
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1696
__attribute__((nonnull, warn_unused_result))
1697
bool run_queue(task_queue **const queue,
1698
string_set *const cancelled_filenames,
1699
bool *const quit_now){
1701
task_queue *new_queue = create_queue();
1702
if(new_queue == NULL){
1706
__attribute__((cleanup(string_set_clear)))
1707
string_set old_cancelled_filenames = {};
1708
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1710
/* Declare i outside the for loop, since we might need i after the
1711
loop in case we aborted in the middle */
1713
for(i=0; i < (*queue)->length and not *quit_now; i++){
1714
task_context *const task = &((*queue)->tasks[i]);
1715
const char *const question_filename = task->question_filename;
1716
/* Skip any task referencing a cancelled question filename */
1717
if(question_filename != NULL
1718
and string_set_contains(old_cancelled_filenames,
1719
question_filename)){
1723
task->func(*task, new_queue);
1727
/* we might be in the middle of the queue, so clean up any
1728
remaining tasks in the current queue */
1729
for(; i < (*queue)->length; i++){
1730
cleanup_task(&((*queue)->tasks[i]));
1744
/* End of regular code section */
1746
/* Start of tests section; here are the tests for the above code */
1748
/* This "fixture" data structure is used by the test setup and
1749
teardown functions */
1751
struct sigaction orig_sigaction;
1752
sigset_t orig_sigmask;
1755
static void test_setup(test_fixture *fixture,
1756
__attribute__((unused))
1757
gconstpointer user_data){
1758
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1759
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1762
static void test_teardown(test_fixture *fixture,
1763
__attribute__((unused))
1764
gconstpointer user_data){
1765
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1766
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1769
/* Utility function used by tests to search queue for matching task */
1770
__attribute__((pure, nonnull, warn_unused_result))
1771
static task_context *find_matching_task(const task_queue *const queue,
1772
const task_context task){
1773
/* The argument "task" structure is a pattern to match; 0 in any
1774
member means any value matches, otherwise the value must match.
1775
The filename strings are compared by strcmp(), not by pointer. */
1776
for(size_t i = 0; i < queue->length; i++){
1777
task_context *const current_task = queue->tasks+i;
1778
/* Check all members of task_context, if set to a non-zero value.
1779
If a member does not match, continue to next task in queue */
1781
/* task_func *const func */
1782
if(task.func != NULL and current_task->func != task.func){
1785
/* char *const question_filename; */
1786
if(task.question_filename != NULL
1787
and (current_task->question_filename == NULL
1788
or strcmp(current_task->question_filename,
1789
task.question_filename) != 0)){
1792
/* const pid_t pid; */
1793
if(task.pid != 0 and current_task->pid != task.pid){
1796
/* const int epoll_fd; */
1797
if(task.epoll_fd != 0
1798
and current_task->epoll_fd != task.epoll_fd){
1801
/* bool *const quit_now; */
1802
if(task.quit_now != NULL
1803
and current_task->quit_now != task.quit_now){
1807
if(task.fd != 0 and current_task->fd != task.fd){
1810
/* bool *const mandos_client_exited; */
1811
if(task.mandos_client_exited != NULL
1812
and current_task->mandos_client_exited
1813
!= task.mandos_client_exited){
1816
/* buffer *const password; */
1817
if(task.password != NULL
1818
and current_task->password != task.password){
1821
/* bool *const password_is_read; */
1822
if(task.password_is_read != NULL
1823
and current_task->password_is_read != task.password_is_read){
1826
/* char *filename; */
1827
if(task.filename != NULL
1828
and (current_task->filename == NULL
1829
or strcmp(current_task->filename, task.filename) != 0)){
1832
/* string_set *const cancelled_filenames; */
1833
if(task.cancelled_filenames != NULL
1834
and current_task->cancelled_filenames
1835
!= task.cancelled_filenames){
1838
/* const mono_microsecs notafter; */
1839
if(task.notafter != 0
1840
and current_task->notafter != task.notafter){
1843
/* const mono_microsecs *const current_time; */
1844
if(task.current_time != NULL
1845
and current_task->current_time != task.current_time){
1848
/* Current task matches all members; return it */
1849
return current_task;
1851
/* No task in queue matches passed pattern task */
1855
static void test_create_queue(__attribute__((unused))
1856
test_fixture *fixture,
1857
__attribute__((unused))
1858
gconstpointer user_data){
1859
__attribute__((cleanup(cleanup_queue)))
1860
task_queue *const queue = create_queue();
1861
g_assert_nonnull(queue);
1862
g_assert_null(queue->tasks);
1863
g_assert_true(queue->length == 0);
1864
g_assert_true(queue->next_run == 0);
1867
static task_func dummy_func;
1869
static void test_add_to_queue(__attribute__((unused))
1870
test_fixture *fixture,
1871
__attribute__((unused))
1872
gconstpointer user_data){
1873
__attribute__((cleanup(cleanup_queue)))
1874
task_queue *queue = create_queue();
1875
g_assert_nonnull(queue);
1877
g_assert_true(add_to_queue(queue,
1878
(task_context){ .func=dummy_func }));
1879
g_assert_true(queue->length == 1);
1880
g_assert_nonnull(queue->tasks);
1881
g_assert_true(queue->tasks[0].func == dummy_func);
1884
static void dummy_func(__attribute__((unused))
1885
const task_context task,
1886
__attribute__((unused))
1887
task_queue *const queue){
1890
static void test_queue_has_question_empty(__attribute__((unused))
1891
test_fixture *fixture,
1892
__attribute__((unused))
1893
gconstpointer user_data){
1894
__attribute__((cleanup(cleanup_queue)))
1895
task_queue *queue = create_queue();
1896
g_assert_nonnull(queue);
1897
g_assert_false(queue_has_question(queue));
1900
static void test_queue_has_question_false(__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(add_to_queue(queue,
1908
(task_context){ .func=dummy_func }));
1909
g_assert_false(queue_has_question(queue));
1912
static void test_queue_has_question_true(__attribute__((unused))
1913
test_fixture *fixture,
1914
__attribute__((unused))
1915
gconstpointer user_data){
1916
__attribute__((cleanup(cleanup_queue)))
1917
task_queue *queue = create_queue();
1918
g_assert_nonnull(queue);
1919
char *const question_filename
1920
= strdup("/nonexistent/question_filename");
1921
g_assert_nonnull(question_filename);
1922
task_context task = {
1924
.question_filename=question_filename,
1926
g_assert_true(add_to_queue(queue, task));
1927
g_assert_true(queue_has_question(queue));
1930
static void test_queue_has_question_false2(__attribute__((unused))
1931
test_fixture *fixture,
1932
__attribute__((unused))
1933
gconstpointer user_data){
1934
__attribute__((cleanup(cleanup_queue)))
1935
task_queue *queue = create_queue();
1936
g_assert_nonnull(queue);
1937
task_context task = { .func=dummy_func };
1938
g_assert_true(add_to_queue(queue, task));
1939
g_assert_true(add_to_queue(queue, task));
1940
g_assert_cmpint((int)queue->length, ==, 2);
1941
g_assert_false(queue_has_question(queue));
1944
static void test_queue_has_question_true2(__attribute__((unused))
1945
test_fixture *fixture,
1946
__attribute__((unused))
1947
gconstpointer user_data){
1948
__attribute__((cleanup(cleanup_queue)))
1949
task_queue *queue = create_queue();
1950
g_assert_nonnull(queue);
1951
task_context task1 = { .func=dummy_func };
1952
g_assert_true(add_to_queue(queue, task1));
1953
char *const question_filename
1954
= strdup("/nonexistent/question_filename");
1955
g_assert_nonnull(question_filename);
1956
task_context task2 = {
1958
.question_filename=question_filename,
1960
g_assert_true(add_to_queue(queue, task2));
1961
g_assert_cmpint((int)queue->length, ==, 2);
1962
g_assert_true(queue_has_question(queue));
1965
static void test_cleanup_buffer(__attribute__((unused))
1966
test_fixture *fixture,
1967
__attribute__((unused))
1968
gconstpointer user_data){
1971
const size_t buffersize = 10;
1973
buf.data = malloc(buffersize);
1974
g_assert_nonnull(buf.data);
1975
if(mlock(buf.data, buffersize) != 0){
1976
g_assert_true(errno == EPERM or errno == ENOMEM);
1979
cleanup_buffer(&buf);
1980
g_assert_null(buf.data);
1984
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1985
test_fixture *fixture,
1986
__attribute__((unused))
1989
__attribute__((cleanup(string_set_clear)))
1990
string_set set = {};
1991
g_assert_false(string_set_contains(set, "")); /* Empty string */
1992
g_assert_false(string_set_contains(set, "test_string"));
1996
test_string_set_with_added_string_contains_it(__attribute__((unused))
1997
test_fixture *fixture,
1998
__attribute__((unused))
2001
__attribute__((cleanup(string_set_clear)))
2002
string_set set = {};
2003
g_assert_true(string_set_add(&set, "test_string"));
2004
g_assert_true(string_set_contains(set, "test_string"));
2008
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2009
test_fixture *fixture,
2010
__attribute__((unused))
2011
gconstpointer user_data){
2012
__attribute__((cleanup(string_set_clear)))
2013
string_set set = {};
2014
g_assert_true(string_set_add(&set, "test_string"));
2015
string_set_clear(&set);
2016
g_assert_false(string_set_contains(set, "test_string"));
2020
void test_string_set_swap_one_with_empty(__attribute__((unused))
2021
test_fixture *fixture,
2022
__attribute__((unused))
2023
gconstpointer user_data){
2024
__attribute__((cleanup(string_set_clear)))
2025
string_set set1 = {};
2026
__attribute__((cleanup(string_set_clear)))
2027
string_set set2 = {};
2028
g_assert_true(string_set_add(&set1, "test_string1"));
2029
string_set_swap(&set1, &set2);
2030
g_assert_false(string_set_contains(set1, "test_string1"));
2031
g_assert_true(string_set_contains(set2, "test_string1"));
2035
void test_string_set_swap_empty_with_one(__attribute__((unused))
2036
test_fixture *fixture,
2037
__attribute__((unused))
2038
gconstpointer user_data){
2039
__attribute__((cleanup(string_set_clear)))
2040
string_set set1 = {};
2041
__attribute__((cleanup(string_set_clear)))
2042
string_set set2 = {};
2043
g_assert_true(string_set_add(&set2, "test_string2"));
2044
string_set_swap(&set1, &set2);
2045
g_assert_true(string_set_contains(set1, "test_string2"));
2046
g_assert_false(string_set_contains(set2, "test_string2"));
2049
static void test_string_set_swap_one_with_one(__attribute__((unused))
2050
test_fixture *fixture,
2051
__attribute__((unused))
2054
__attribute__((cleanup(string_set_clear)))
2055
string_set set1 = {};
2056
__attribute__((cleanup(string_set_clear)))
2057
string_set set2 = {};
2058
g_assert_true(string_set_add(&set1, "test_string1"));
2059
g_assert_true(string_set_add(&set2, "test_string2"));
2060
string_set_swap(&set1, &set2);
2061
g_assert_false(string_set_contains(set1, "test_string1"));
2062
g_assert_true(string_set_contains(set1, "test_string2"));
2063
g_assert_false(string_set_contains(set2, "test_string2"));
2064
g_assert_true(string_set_contains(set2, "test_string1"));
2067
static bool fd_has_cloexec_and_nonblock(const int);
2069
static bool epoll_set_contains(int, int, uint32_t);
2071
static void test_start_mandos_client(test_fixture *fixture,
2072
__attribute__((unused))
2073
gconstpointer user_data){
2075
bool mandos_client_exited = false;
2076
bool quit_now = false;
2077
__attribute__((cleanup(cleanup_close)))
2078
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2079
g_assert_cmpint(epoll_fd, >=, 0);
2080
__attribute__((cleanup(cleanup_queue)))
2081
task_queue *queue = create_queue();
2082
g_assert_nonnull(queue);
2083
buffer password = {};
2084
bool password_is_read = false;
2085
const char helper_directory[] = "/nonexistent";
2086
const char *const argv[] = { "/bin/true", NULL };
2088
g_assert_true(start_mandos_client(queue, epoll_fd,
2089
&mandos_client_exited, &quit_now,
2090
&password, &password_is_read,
2091
&fixture->orig_sigaction,
2092
fixture->orig_sigmask,
2093
helper_directory, 0, 0, argv));
2095
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2097
const task_context *const added_wait_task
2098
= find_matching_task(queue, (task_context){
2099
.func=wait_for_mandos_client_exit,
2100
.mandos_client_exited=&mandos_client_exited,
2101
.quit_now=&quit_now,
2103
g_assert_nonnull(added_wait_task);
2104
g_assert_cmpint(added_wait_task->pid, >, 0);
2105
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2106
waitpid(added_wait_task->pid, NULL, 0);
2108
const task_context *const added_read_task
2109
= find_matching_task(queue, (task_context){
2110
.func=read_mandos_client_output,
2112
.password=&password,
2113
.password_is_read=&password_is_read,
2114
.quit_now=&quit_now,
2116
g_assert_nonnull(added_read_task);
2117
g_assert_cmpint(added_read_task->fd, >, 2);
2118
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2119
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2120
EPOLLIN | EPOLLRDHUP));
2123
static bool fd_has_cloexec_and_nonblock(const int fd){
2124
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2125
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2126
return ((socket_fd_flags >= 0)
2127
and (socket_fd_flags & FD_CLOEXEC)
2128
and (socket_file_flags >= 0)
2129
and (socket_file_flags & O_NONBLOCK));
2132
__attribute__((const))
2133
bool is_privileged(void){
2134
uid_t user = getuid() + 1;
2135
if(user == 0){ /* Overflow check */
2138
gid_t group = getuid() + 1;
2139
if(group == 0){ /* Overflow check */
2142
const pid_t pid = fork();
2143
if(pid == 0){ /* Child */
2144
if(setresgid((uid_t)-1, group, group) == -1){
2146
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2147
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2151
if(setresuid((uid_t)-1, user, user) == -1){
2153
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2154
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2161
waitpid(pid, &status, 0);
2162
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2168
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2169
/* Only scan for events in this eventmask */
2170
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2171
__attribute__((cleanup(cleanup_string)))
2172
char *fdinfo_name = NULL;
2173
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2174
g_assert_cmpint(ret, >, 0);
2175
g_assert_nonnull(fdinfo_name);
2177
FILE *fdinfo = fopen(fdinfo_name, "r");
2178
g_assert_nonnull(fdinfo);
2179
uint32_t reported_events;
2184
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2187
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2188
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2189
&found_fd, &reported_events) == 2){
2194
} while(not feof(fdinfo) and not ferror(fdinfo));
2195
g_assert_cmpint(fclose(fdinfo), ==, 0);
2202
/* Don't check events if none are given */
2205
return (reported_events & eventmask) == (events & eventmask);
2208
static void test_start_mandos_client_execv(test_fixture *fixture,
2209
__attribute__((unused))
2210
gconstpointer user_data){
2211
bool mandos_client_exited = false;
2212
bool quit_now = false;
2213
__attribute__((cleanup(cleanup_close)))
2214
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2215
g_assert_cmpint(epoll_fd, >=, 0);
2216
__attribute__((cleanup(cleanup_queue)))
2217
task_queue *queue = create_queue();
2218
g_assert_nonnull(queue);
2219
__attribute__((cleanup(cleanup_buffer)))
2220
buffer password = {};
2221
const char helper_directory[] = "/nonexistent";
2222
/* Can't execv("/", ...), so this should fail */
2223
const char *const argv[] = { "/", NULL };
2226
__attribute__((cleanup(cleanup_close)))
2227
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2228
g_assert_cmpint(devnull_fd, >=, 0);
2229
__attribute__((cleanup(cleanup_close)))
2230
const int real_stderr_fd = dup(STDERR_FILENO);
2231
g_assert_cmpint(real_stderr_fd, >=, 0);
2232
dup2(devnull_fd, STDERR_FILENO);
2234
const bool success = start_mandos_client(queue, epoll_fd,
2235
&mandos_client_exited,
2239
&fixture->orig_sigaction,
2240
fixture->orig_sigmask,
2241
helper_directory, 0, 0,
2243
dup2(real_stderr_fd, STDERR_FILENO);
2244
g_assert_true(success);
2246
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2248
struct timespec starttime, currtime;
2249
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2251
queue->next_run = 0;
2252
string_set cancelled_filenames = {};
2255
__attribute__((cleanup(cleanup_close)))
2256
const int devnull_fd = open("/dev/null",
2257
O_WRONLY | O_CLOEXEC);
2258
g_assert_cmpint(devnull_fd, >=, 0);
2259
__attribute__((cleanup(cleanup_close)))
2260
const int real_stderr_fd = dup(STDERR_FILENO);
2261
g_assert_cmpint(real_stderr_fd, >=, 0);
2262
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2263
dup2(devnull_fd, STDERR_FILENO);
2264
const bool success = run_queue(&queue, &cancelled_filenames,
2266
dup2(real_stderr_fd, STDERR_FILENO);
2271
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2272
} while(((queue->length) > 0)
2274
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2276
g_assert_true(quit_now);
2277
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2278
g_assert_true(mandos_client_exited);
2281
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2282
__attribute__((unused))
2285
if(not is_privileged()){
2286
g_test_skip("Not privileged");
2290
bool mandos_client_exited = false;
2291
bool quit_now = false;
2292
__attribute__((cleanup(cleanup_close)))
2293
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2294
g_assert_cmpint(epoll_fd, >=, 0);
2295
__attribute__((cleanup(cleanup_queue)))
2296
task_queue *queue = create_queue();
2297
g_assert_nonnull(queue);
2298
__attribute__((cleanup(cleanup_buffer)))
2299
buffer password = {};
2300
bool password_is_read = false;
2301
const char helper_directory[] = "/nonexistent";
2302
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2306
const bool success = start_mandos_client(queue, epoll_fd,
2307
&mandos_client_exited,
2308
&quit_now, &password,
2310
&fixture->orig_sigaction,
2311
fixture->orig_sigmask,
2312
helper_directory, user,
2314
g_assert_true(success);
2315
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2317
struct timespec starttime, currtime;
2318
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2320
queue->next_run = 0;
2321
string_set cancelled_filenames = {};
2322
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2323
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2324
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2325
} while(((queue->length) > 0)
2327
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2329
g_assert_false(quit_now);
2330
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2331
g_assert_true(mandos_client_exited);
2333
g_assert_true(password_is_read);
2334
g_assert_nonnull(password.data);
2337
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2339
g_assert_true((uid_t)id == id);
2341
g_assert_cmpuint((unsigned int)id, ==, 0);
2344
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2345
__attribute__((unused))
2348
if(not is_privileged()){
2349
g_test_skip("Not privileged");
2353
bool mandos_client_exited = false;
2354
bool quit_now = false;
2355
__attribute__((cleanup(cleanup_close)))
2356
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2357
g_assert_cmpint(epoll_fd, >=, 0);
2358
__attribute__((cleanup(cleanup_queue)))
2359
task_queue *queue = create_queue();
2360
g_assert_nonnull(queue);
2361
__attribute__((cleanup(cleanup_buffer)))
2362
buffer password = {};
2363
bool password_is_read = false;
2364
const char helper_directory[] = "/nonexistent";
2365
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2369
const bool success = start_mandos_client(queue, epoll_fd,
2370
&mandos_client_exited,
2371
&quit_now, &password,
2373
&fixture->orig_sigaction,
2374
fixture->orig_sigmask,
2375
helper_directory, user,
2377
g_assert_true(success);
2378
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2380
struct timespec starttime, currtime;
2381
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2383
queue->next_run = 0;
2384
string_set cancelled_filenames = {};
2385
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2386
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2387
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2388
} while(((queue->length) > 0)
2390
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2392
g_assert_false(quit_now);
2393
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2394
g_assert_true(mandos_client_exited);
2396
g_assert_true(password_is_read);
2397
g_assert_nonnull(password.data);
2400
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2402
g_assert_true((gid_t)id == id);
2404
g_assert_cmpuint((unsigned int)id, ==, 0);
2407
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2408
__attribute__((unused))
2411
if(not is_privileged()){
2412
g_test_skip("Not privileged");
2416
bool mandos_client_exited = false;
2417
bool quit_now = false;
2418
__attribute__((cleanup(cleanup_close)))
2419
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2420
g_assert_cmpint(epoll_fd, >=, 0);
2421
__attribute__((cleanup(cleanup_queue)))
2422
task_queue *queue = create_queue();
2423
g_assert_nonnull(queue);
2424
__attribute__((cleanup(cleanup_buffer)))
2425
buffer password = {};
2426
bool password_is_read = false;
2427
const char helper_directory[] = "/nonexistent";
2428
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2433
const bool success = start_mandos_client(queue, epoll_fd,
2434
&mandos_client_exited,
2435
&quit_now, &password,
2437
&fixture->orig_sigaction,
2438
fixture->orig_sigmask,
2439
helper_directory, user,
2441
g_assert_true(success);
2442
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2444
struct timespec starttime, currtime;
2445
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2447
queue->next_run = 0;
2448
string_set cancelled_filenames = {};
2449
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2450
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2451
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2452
} while(((queue->length) > 0)
2454
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2456
g_assert_false(quit_now);
2457
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2458
g_assert_true(mandos_client_exited);
2460
g_assert_true(password_is_read);
2461
g_assert_nonnull(password.data);
2464
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2466
g_assert_true((uid_t)id == id);
2468
g_assert_cmpuint((unsigned int)id, ==, user);
2471
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2472
__attribute__((unused))
2475
if(not is_privileged()){
2476
g_test_skip("Not privileged");
2480
bool mandos_client_exited = false;
2481
bool quit_now = false;
2482
__attribute__((cleanup(cleanup_close)))
2483
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2484
g_assert_cmpint(epoll_fd, >=, 0);
2485
__attribute__((cleanup(cleanup_queue)))
2486
task_queue *queue = create_queue();
2487
g_assert_nonnull(queue);
2488
__attribute__((cleanup(cleanup_buffer)))
2489
buffer password = {};
2490
bool password_is_read = false;
2491
const char helper_directory[] = "/nonexistent";
2492
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2497
const bool success = start_mandos_client(queue, epoll_fd,
2498
&mandos_client_exited,
2499
&quit_now, &password,
2501
&fixture->orig_sigaction,
2502
fixture->orig_sigmask,
2503
helper_directory, user,
2505
g_assert_true(success);
2506
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2508
struct timespec starttime, currtime;
2509
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2511
queue->next_run = 0;
2512
string_set cancelled_filenames = {};
2513
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2514
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2515
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2516
} while(((queue->length) > 0)
2518
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2520
g_assert_false(quit_now);
2521
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2522
g_assert_true(mandos_client_exited);
2524
g_assert_true(password_is_read);
2525
g_assert_nonnull(password.data);
2528
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2530
g_assert_true((gid_t)id == id);
2532
g_assert_cmpuint((unsigned int)id, ==, group);
2535
static void test_start_mandos_client_read(test_fixture *fixture,
2536
__attribute__((unused))
2537
gconstpointer user_data){
2538
bool mandos_client_exited = false;
2539
bool quit_now = false;
2540
__attribute__((cleanup(cleanup_close)))
2541
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2542
g_assert_cmpint(epoll_fd, >=, 0);
2543
__attribute__((cleanup(cleanup_queue)))
2544
task_queue *queue = create_queue();
2545
g_assert_nonnull(queue);
2546
__attribute__((cleanup(cleanup_buffer)))
2547
buffer password = {};
2548
bool password_is_read = false;
2549
const char dummy_test_password[] = "dummy test password";
2550
const char helper_directory[] = "/nonexistent";
2551
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2554
const bool success = start_mandos_client(queue, epoll_fd,
2555
&mandos_client_exited,
2556
&quit_now, &password,
2558
&fixture->orig_sigaction,
2559
fixture->orig_sigmask,
2560
helper_directory, 0, 0,
2562
g_assert_true(success);
2563
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2565
struct timespec starttime, currtime;
2566
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2568
queue->next_run = 0;
2569
string_set cancelled_filenames = {};
2570
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2571
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2572
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2573
} while(((queue->length) > 0)
2575
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2577
g_assert_false(quit_now);
2578
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2579
g_assert_true(mandos_client_exited);
2581
g_assert_true(password_is_read);
2582
g_assert_cmpint((int)password.length, ==,
2583
sizeof(dummy_test_password)-1);
2584
g_assert_nonnull(password.data);
2585
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2586
sizeof(dummy_test_password)-1), ==, 0);
2590
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2591
__attribute__((unused))
2594
bool mandos_client_exited = false;
2595
bool quit_now = false;
2596
__attribute__((cleanup(cleanup_close)))
2597
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2598
g_assert_cmpint(epoll_fd, >=, 0);
2599
__attribute__((cleanup(cleanup_queue)))
2600
task_queue *queue = create_queue();
2601
g_assert_nonnull(queue);
2602
__attribute__((cleanup(cleanup_buffer)))
2603
buffer password = {};
2604
bool password_is_read = false;
2605
const char helper_directory[] = "/nonexistent";
2606
const char *const argv[] = { "/bin/sh", "-c",
2607
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2609
const bool success = start_mandos_client(queue, epoll_fd,
2610
&mandos_client_exited,
2611
&quit_now, &password,
2613
&fixture->orig_sigaction,
2614
fixture->orig_sigmask,
2615
helper_directory, 0, 0,
2617
g_assert_true(success);
2618
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2620
struct timespec starttime, currtime;
2621
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2623
queue->next_run = 0;
2624
string_set cancelled_filenames = {};
2625
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2626
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2627
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2628
} while(((queue->length) > 0)
2630
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2632
g_assert_false(quit_now);
2633
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2634
g_assert_true(mandos_client_exited);
2636
g_assert_true(password_is_read);
2637
g_assert_cmpint((int)password.length, ==,
2638
sizeof(helper_directory)-1);
2639
g_assert_nonnull(password.data);
2640
g_assert_cmpint(memcmp(helper_directory, password.data,
2641
sizeof(helper_directory)-1), ==, 0);
2644
__attribute__((nonnull, warn_unused_result))
2645
static bool proc_status_sigblk_to_sigset(const char *const,
2648
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2649
__attribute__((unused))
2650
gconstpointer user_data){
2651
bool mandos_client_exited = false;
2652
bool quit_now = false;
2653
__attribute__((cleanup(cleanup_close)))
2654
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2655
g_assert_cmpint(epoll_fd, >=, 0);
2656
__attribute__((cleanup(cleanup_queue)))
2657
task_queue *queue = create_queue();
2658
g_assert_nonnull(queue);
2659
__attribute__((cleanup(cleanup_buffer)))
2660
buffer password = {};
2661
bool password_is_read = false;
2662
const char helper_directory[] = "/nonexistent";
2663
/* see proc(5) for format of /proc/self/status */
2664
const char *const argv[] = { "/usr/bin/awk",
2665
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2667
g_assert_true(start_mandos_client(queue, epoll_fd,
2668
&mandos_client_exited, &quit_now,
2669
&password, &password_is_read,
2670
&fixture->orig_sigaction,
2671
fixture->orig_sigmask,
2672
helper_directory, 0, 0, argv));
2674
struct timespec starttime, currtime;
2675
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2677
queue->next_run = 0;
2678
string_set cancelled_filenames = {};
2679
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2680
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2681
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2682
} while((not (mandos_client_exited and password_is_read))
2684
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2685
g_assert_true(mandos_client_exited);
2686
g_assert_true(password_is_read);
2688
sigset_t parsed_sigmask;
2689
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2692
for(int signum = 1; signum < NSIG; signum++){
2693
const bool has_signal = sigismember(&parsed_sigmask, signum);
2694
if(sigismember(&fixture->orig_sigmask, signum)){
2695
g_assert_true(has_signal);
2697
g_assert_false(has_signal);
2702
__attribute__((nonnull, warn_unused_result))
2703
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2704
sigset_t *const sigmask){
2705
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2706
uintmax_t scanned_sigmask;
2707
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2710
if(sigemptyset(sigmask) != 0){
2713
for(int signum = 1; signum < NSIG; signum++){
2714
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2715
if(sigaddset(sigmask, signum) != 0){
2723
static void run_task_with_stderr_to_dev_null(const task_context task,
2724
task_queue *const queue);
2727
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2728
test_fixture *fixture,
2729
__attribute__((unused))
2730
gconstpointer user_data){
2732
bool mandos_client_exited = false;
2733
bool quit_now = false;
2735
__attribute__((cleanup(cleanup_queue)))
2736
task_queue *queue = create_queue();
2737
g_assert_nonnull(queue);
2738
const task_context task = {
2739
.func=wait_for_mandos_client_exit,
2741
.mandos_client_exited=&mandos_client_exited,
2742
.quit_now=&quit_now,
2744
run_task_with_stderr_to_dev_null(task, queue);
2746
g_assert_false(mandos_client_exited);
2747
g_assert_true(quit_now);
2748
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2751
static void run_task_with_stderr_to_dev_null(const task_context task,
2752
task_queue *const queue){
2753
FILE *real_stderr = stderr;
2754
FILE *devnull = fopen("/dev/null", "we");
2755
g_assert_nonnull(devnull);
2758
task.func(task, queue);
2759
stderr = real_stderr;
2761
g_assert_cmpint(fclose(devnull), ==, 0);
2765
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2766
__attribute__((unused))
2767
gconstpointer user_data){
2768
bool mandos_client_exited = false;
2769
bool quit_now = false;
2771
pid_t create_eternal_process(void){
2772
const pid_t pid = fork();
2773
if(pid == 0){ /* Child */
2774
if(not restore_signal_handler(&fixture->orig_sigaction)){
2775
_exit(EXIT_FAILURE);
2777
if(not restore_sigmask(&fixture->orig_sigmask)){
2778
_exit(EXIT_FAILURE);
2786
pid_t pid = create_eternal_process();
2787
g_assert_true(pid != -1);
2789
__attribute__((cleanup(cleanup_queue)))
2790
task_queue *queue = create_queue();
2791
g_assert_nonnull(queue);
2792
const task_context task = {
2793
.func=wait_for_mandos_client_exit,
2795
.mandos_client_exited=&mandos_client_exited,
2796
.quit_now=&quit_now,
2798
task.func(task, queue);
2800
g_assert_false(mandos_client_exited);
2801
g_assert_false(quit_now);
2802
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2804
g_assert_nonnull(find_matching_task(queue, (task_context){
2805
.func=wait_for_mandos_client_exit,
2807
.mandos_client_exited=&mandos_client_exited,
2808
.quit_now=&quit_now,
2813
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2814
__attribute__((unused))
2817
bool mandos_client_exited = false;
2818
bool quit_now = false;
2820
pid_t create_successful_process(void){
2821
const pid_t pid = fork();
2822
if(pid == 0){ /* Child */
2823
if(not restore_signal_handler(&fixture->orig_sigaction)){
2824
_exit(EXIT_FAILURE);
2826
if(not restore_sigmask(&fixture->orig_sigmask)){
2827
_exit(EXIT_FAILURE);
2833
const pid_t pid = create_successful_process();
2834
g_assert_true(pid != -1);
2836
__attribute__((cleanup(cleanup_queue)))
2837
task_queue *queue = create_queue();
2838
g_assert_nonnull(queue);
2839
const task_context initial_task = {
2840
.func=wait_for_mandos_client_exit,
2842
.mandos_client_exited=&mandos_client_exited,
2843
.quit_now=&quit_now,
2845
g_assert_true(add_to_queue(queue, initial_task));
2847
struct timespec starttime, currtime;
2848
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2849
__attribute__((cleanup(cleanup_close)))
2850
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2852
queue->next_run = 0;
2853
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2854
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2855
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2856
} while((not mandos_client_exited)
2858
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2860
g_assert_true(mandos_client_exited);
2861
g_assert_false(quit_now);
2862
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2866
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2867
__attribute__((unused))
2870
bool mandos_client_exited = false;
2871
bool quit_now = false;
2873
pid_t create_failing_process(void){
2874
const pid_t pid = fork();
2875
if(pid == 0){ /* Child */
2876
if(not restore_signal_handler(&fixture->orig_sigaction)){
2877
_exit(EXIT_FAILURE);
2879
if(not restore_sigmask(&fixture->orig_sigmask)){
2880
_exit(EXIT_FAILURE);
2886
const pid_t pid = create_failing_process();
2887
g_assert_true(pid != -1);
2889
__attribute__((cleanup(string_set_clear)))
2890
string_set cancelled_filenames = {};
2891
__attribute__((cleanup(cleanup_close)))
2892
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2893
g_assert_cmpint(epoll_fd, >=, 0);
2894
__attribute__((cleanup(cleanup_queue)))
2895
task_queue *queue = create_queue();
2896
g_assert_nonnull(queue);
2897
g_assert_true(add_to_queue(queue, (task_context){
2898
.func=wait_for_mandos_client_exit,
2900
.mandos_client_exited=&mandos_client_exited,
2901
.quit_now=&quit_now,
2904
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2906
__attribute__((cleanup(cleanup_close)))
2907
const int devnull_fd = open("/dev/null",
2908
O_WRONLY | O_CLOEXEC);
2909
g_assert_cmpint(devnull_fd, >=, 0);
2910
__attribute__((cleanup(cleanup_close)))
2911
const int real_stderr_fd = dup(STDERR_FILENO);
2912
g_assert_cmpint(real_stderr_fd, >=, 0);
2914
struct timespec starttime, currtime;
2915
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2917
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2918
dup2(devnull_fd, STDERR_FILENO);
2919
const bool success = run_queue(&queue, &cancelled_filenames,
2921
dup2(real_stderr_fd, STDERR_FILENO);
2926
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2927
} while((not mandos_client_exited)
2929
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2931
g_assert_true(quit_now);
2932
g_assert_true(mandos_client_exited);
2933
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2937
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2938
__attribute__((unused))
2939
gconstpointer user_data){
2940
bool mandos_client_exited = false;
2941
bool quit_now = false;
2943
pid_t create_killed_process(void){
2944
const pid_t pid = fork();
2945
if(pid == 0){ /* Child */
2946
if(not restore_signal_handler(&fixture->orig_sigaction)){
2947
_exit(EXIT_FAILURE);
2949
if(not restore_sigmask(&fixture->orig_sigmask)){
2950
_exit(EXIT_FAILURE);
2959
const pid_t pid = create_killed_process();
2960
g_assert_true(pid != -1);
2962
__attribute__((cleanup(string_set_clear)))
2963
string_set cancelled_filenames = {};
2964
__attribute__((cleanup(cleanup_close)))
2965
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2966
g_assert_cmpint(epoll_fd, >=, 0);
2967
__attribute__((cleanup(cleanup_queue)))
2968
task_queue *queue = create_queue();
2969
g_assert_nonnull(queue);
2970
g_assert_true(add_to_queue(queue, (task_context){
2971
.func=wait_for_mandos_client_exit,
2973
.mandos_client_exited=&mandos_client_exited,
2974
.quit_now=&quit_now,
2977
__attribute__((cleanup(cleanup_close)))
2978
const int devnull_fd = open("/dev/null",
2979
O_WRONLY | O_CLOEXEC);
2980
g_assert_cmpint(devnull_fd, >=, 0);
2981
__attribute__((cleanup(cleanup_close)))
2982
const int real_stderr_fd = dup(STDERR_FILENO);
2983
g_assert_cmpint(real_stderr_fd, >=, 0);
2985
struct timespec starttime, currtime;
2986
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2988
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2989
dup2(devnull_fd, STDERR_FILENO);
2990
const bool success = run_queue(&queue, &cancelled_filenames,
2992
dup2(real_stderr_fd, STDERR_FILENO);
2997
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2998
} while((not mandos_client_exited)
3000
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3002
g_assert_true(mandos_client_exited);
3003
g_assert_true(quit_now);
3004
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3007
static bool epoll_set_does_not_contain(int, int);
3010
void test_read_mandos_client_output_readerror(__attribute__((unused))
3011
test_fixture *fixture,
3012
__attribute__((unused))
3015
__attribute__((cleanup(cleanup_close)))
3016
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3017
g_assert_cmpint(epoll_fd, >=, 0);
3019
__attribute__((cleanup(cleanup_buffer)))
3020
buffer password = {};
3022
/* Reading /proc/self/mem from offset 0 will always give EIO */
3023
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3025
bool password_is_read = false;
3026
bool quit_now = false;
3027
__attribute__((cleanup(cleanup_queue)))
3028
task_queue *queue = create_queue();
3029
g_assert_nonnull(queue);
3031
task_context task = {
3032
.func=read_mandos_client_output,
3035
.password=&password,
3036
.password_is_read=&password_is_read,
3037
.quit_now=&quit_now,
3039
run_task_with_stderr_to_dev_null(task, queue);
3040
g_assert_false(password_is_read);
3041
g_assert_cmpint((int)password.length, ==, 0);
3042
g_assert_true(quit_now);
3043
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3045
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3047
g_assert_cmpint(close(fd), ==, -1);
3050
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3051
return not epoll_set_contains(epoll_fd, fd, 0);
3055
void test_read_mandos_client_output_nodata(__attribute__((unused))
3056
test_fixture *fixture,
3057
__attribute__((unused))
3058
gconstpointer user_data){
3059
__attribute__((cleanup(cleanup_close)))
3060
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3061
g_assert_cmpint(epoll_fd, >=, 0);
3064
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3066
__attribute__((cleanup(cleanup_buffer)))
3067
buffer password = {};
3069
bool password_is_read = false;
3070
bool quit_now = false;
3071
__attribute__((cleanup(cleanup_queue)))
3072
task_queue *queue = create_queue();
3073
g_assert_nonnull(queue);
3075
task_context task = {
3076
.func=read_mandos_client_output,
3079
.password=&password,
3080
.password_is_read=&password_is_read,
3081
.quit_now=&quit_now,
3083
task.func(task, queue);
3084
g_assert_false(password_is_read);
3085
g_assert_cmpint((int)password.length, ==, 0);
3086
g_assert_false(quit_now);
3087
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3089
g_assert_nonnull(find_matching_task(queue, (task_context){
3090
.func=read_mandos_client_output,
3093
.password=&password,
3094
.password_is_read=&password_is_read,
3095
.quit_now=&quit_now,
3098
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3099
EPOLLIN | EPOLLRDHUP));
3101
g_assert_cmpint(close(pipefds[1]), ==, 0);
3104
static void test_read_mandos_client_output_eof(__attribute__((unused))
3105
test_fixture *fixture,
3106
__attribute__((unused))
3109
__attribute__((cleanup(cleanup_close)))
3110
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3111
g_assert_cmpint(epoll_fd, >=, 0);
3114
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3115
g_assert_cmpint(close(pipefds[1]), ==, 0);
3117
__attribute__((cleanup(cleanup_buffer)))
3118
buffer password = {};
3120
bool password_is_read = false;
3121
bool quit_now = false;
3122
__attribute__((cleanup(cleanup_queue)))
3123
task_queue *queue = create_queue();
3124
g_assert_nonnull(queue);
3126
task_context task = {
3127
.func=read_mandos_client_output,
3130
.password=&password,
3131
.password_is_read=&password_is_read,
3132
.quit_now=&quit_now,
3134
task.func(task, queue);
3135
g_assert_true(password_is_read);
3136
g_assert_cmpint((int)password.length, ==, 0);
3137
g_assert_false(quit_now);
3138
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3140
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3142
g_assert_cmpint(close(pipefds[0]), ==, -1);
3146
void test_read_mandos_client_output_once(__attribute__((unused))
3147
test_fixture *fixture,
3148
__attribute__((unused))
3149
gconstpointer user_data){
3150
__attribute__((cleanup(cleanup_close)))
3151
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3152
g_assert_cmpint(epoll_fd, >=, 0);
3155
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3157
const char dummy_test_password[] = "dummy test password";
3158
/* Start with a pre-allocated buffer */
3159
__attribute__((cleanup(cleanup_buffer)))
3161
.data=malloc(sizeof(dummy_test_password)),
3163
.allocated=sizeof(dummy_test_password),
3165
g_assert_nonnull(password.data);
3166
if(mlock(password.data, password.allocated) != 0){
3167
g_assert_true(errno == EPERM or errno == ENOMEM);
3170
bool password_is_read = false;
3171
bool quit_now = false;
3172
__attribute__((cleanup(cleanup_queue)))
3173
task_queue *queue = create_queue();
3174
g_assert_nonnull(queue);
3176
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3177
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3178
sizeof(dummy_test_password)),
3179
==, (int)sizeof(dummy_test_password));
3181
task_context task = {
3182
.func=read_mandos_client_output,
3185
.password=&password,
3186
.password_is_read=&password_is_read,
3187
.quit_now=&quit_now,
3189
task.func(task, queue);
3191
g_assert_false(password_is_read);
3192
g_assert_cmpint((int)password.length, ==,
3193
(int)sizeof(dummy_test_password));
3194
g_assert_nonnull(password.data);
3195
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3196
sizeof(dummy_test_password)), ==, 0);
3198
g_assert_false(quit_now);
3199
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3201
g_assert_nonnull(find_matching_task(queue, (task_context){
3202
.func=read_mandos_client_output,
3205
.password=&password,
3206
.password_is_read=&password_is_read,
3207
.quit_now=&quit_now,
3210
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3211
EPOLLIN | EPOLLRDHUP));
3213
g_assert_cmpint(close(pipefds[1]), ==, 0);
3217
void test_read_mandos_client_output_malloc(__attribute__((unused))
3218
test_fixture *fixture,
3219
__attribute__((unused))
3220
gconstpointer user_data){
3221
__attribute__((cleanup(cleanup_close)))
3222
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3223
g_assert_cmpint(epoll_fd, >=, 0);
3226
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3228
const char dummy_test_password[] = "dummy test password";
3229
/* Start with an empty buffer */
3230
__attribute__((cleanup(cleanup_buffer)))
3231
buffer password = {};
3233
bool password_is_read = false;
3234
bool quit_now = false;
3235
__attribute__((cleanup(cleanup_queue)))
3236
task_queue *queue = create_queue();
3237
g_assert_nonnull(queue);
3239
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3240
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3241
sizeof(dummy_test_password)),
3242
==, (int)sizeof(dummy_test_password));
3244
task_context task = {
3245
.func=read_mandos_client_output,
3248
.password=&password,
3249
.password_is_read=&password_is_read,
3250
.quit_now=&quit_now,
3252
task.func(task, queue);
3254
g_assert_false(password_is_read);
3255
g_assert_cmpint((int)password.length, ==,
3256
(int)sizeof(dummy_test_password));
3257
g_assert_nonnull(password.data);
3258
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3259
sizeof(dummy_test_password)), ==, 0);
3261
g_assert_false(quit_now);
3262
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3264
g_assert_nonnull(find_matching_task(queue, (task_context){
3265
.func=read_mandos_client_output,
3268
.password=&password,
3269
.password_is_read=&password_is_read,
3270
.quit_now=&quit_now,
3273
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3274
EPOLLIN | EPOLLRDHUP));
3276
g_assert_cmpint(close(pipefds[1]), ==, 0);
3280
void test_read_mandos_client_output_append(__attribute__((unused))
3281
test_fixture *fixture,
3282
__attribute__((unused))
3283
gconstpointer user_data){
3284
__attribute__((cleanup(cleanup_close)))
3285
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3286
g_assert_cmpint(epoll_fd, >=, 0);
3289
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3291
const char dummy_test_password[] = "dummy test password";
3292
__attribute__((cleanup(cleanup_buffer)))
3294
.data=malloc(PIPE_BUF),
3296
.allocated=PIPE_BUF,
3298
g_assert_nonnull(password.data);
3299
if(mlock(password.data, password.allocated) != 0){
3300
g_assert_true(errno == EPERM or errno == ENOMEM);
3303
memset(password.data, 'x', PIPE_BUF);
3304
char password_expected[PIPE_BUF];
3305
memcpy(password_expected, password.data, PIPE_BUF);
3307
bool password_is_read = false;
3308
bool quit_now = false;
3309
__attribute__((cleanup(cleanup_queue)))
3310
task_queue *queue = create_queue();
3311
g_assert_nonnull(queue);
3313
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3314
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3315
sizeof(dummy_test_password)),
3316
==, (int)sizeof(dummy_test_password));
3318
task_context task = {
3319
.func=read_mandos_client_output,
3322
.password=&password,
3323
.password_is_read=&password_is_read,
3324
.quit_now=&quit_now,
3326
task.func(task, queue);
3328
g_assert_false(password_is_read);
3329
g_assert_cmpint((int)password.length, ==,
3330
PIPE_BUF + sizeof(dummy_test_password));
3331
g_assert_nonnull(password.data);
3332
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3334
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3335
dummy_test_password,
3336
sizeof(dummy_test_password)), ==, 0);
3337
g_assert_false(quit_now);
3338
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3340
g_assert_nonnull(find_matching_task(queue, (task_context){
3341
.func=read_mandos_client_output,
3344
.password=&password,
3345
.password_is_read=&password_is_read,
3346
.quit_now=&quit_now,
3349
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3350
EPOLLIN | EPOLLRDHUP));
3353
static char *make_temporary_directory(void);
3355
static void test_add_inotify_dir_watch(__attribute__((unused))
3356
test_fixture *fixture,
3357
__attribute__((unused))
3358
gconstpointer user_data){
3359
__attribute__((cleanup(cleanup_close)))
3360
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3361
g_assert_cmpint(epoll_fd, >=, 0);
3362
__attribute__((cleanup(cleanup_queue)))
3363
task_queue *queue = create_queue();
3364
g_assert_nonnull(queue);
3365
__attribute__((cleanup(string_set_clear)))
3366
string_set cancelled_filenames = {};
3367
const mono_microsecs current_time = 0;
3369
bool quit_now = false;
3370
buffer password = {};
3371
bool mandos_client_exited = false;
3372
bool password_is_read = false;
3374
__attribute__((cleanup(cleanup_string)))
3375
char *tempdir = make_temporary_directory();
3376
g_assert_nonnull(tempdir);
3378
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3380
&cancelled_filenames,
3382
&mandos_client_exited,
3383
&password_is_read));
3385
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3387
const task_context *const added_read_task
3388
= find_matching_task(queue, (task_context){
3389
.func=read_inotify_event,
3391
.quit_now=&quit_now,
3392
.password=&password,
3394
.cancelled_filenames=&cancelled_filenames,
3395
.current_time=¤t_time,
3396
.mandos_client_exited=&mandos_client_exited,
3397
.password_is_read=&password_is_read,
3399
g_assert_nonnull(added_read_task);
3401
g_assert_cmpint(added_read_task->fd, >, 2);
3402
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3403
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3404
added_read_task->fd,
3405
EPOLLIN | EPOLLRDHUP));
3407
g_assert_cmpint(rmdir(tempdir), ==, 0);
3410
static char *make_temporary_directory(void){
3411
char *name = strdup("/tmp/mandosXXXXXX");
3412
g_assert_nonnull(name);
3413
char *result = mkdtemp(name);
3420
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3421
test_fixture *fixture,
3422
__attribute__((unused))
3423
gconstpointer user_data){
3424
__attribute__((cleanup(cleanup_close)))
3425
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3426
g_assert_cmpint(epoll_fd, >=, 0);
3427
__attribute__((cleanup(cleanup_queue)))
3428
task_queue *queue = create_queue();
3429
g_assert_nonnull(queue);
3430
__attribute__((cleanup(string_set_clear)))
3431
string_set cancelled_filenames = {};
3432
const mono_microsecs current_time = 0;
3434
bool quit_now = false;
3435
buffer password = {};
3436
bool mandos_client_exited = false;
3437
bool password_is_read = false;
3439
const char nonexistent_dir[] = "/nonexistent";
3441
FILE *real_stderr = stderr;
3442
FILE *devnull = fopen("/dev/null", "we");
3443
g_assert_nonnull(devnull);
3445
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3446
&password, nonexistent_dir,
3447
&cancelled_filenames,
3449
&mandos_client_exited,
3450
&password_is_read));
3451
stderr = real_stderr;
3452
g_assert_cmpint(fclose(devnull), ==, 0);
3454
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3457
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3458
test_fixture *fixture,
3459
__attribute__((unused))
3462
__attribute__((cleanup(cleanup_close)))
3463
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3464
g_assert_cmpint(epoll_fd, >=, 0);
3465
__attribute__((cleanup(cleanup_queue)))
3466
task_queue *queue = create_queue();
3467
g_assert_nonnull(queue);
3468
__attribute__((cleanup(string_set_clear)))
3469
string_set cancelled_filenames = {};
3470
const mono_microsecs current_time = 0;
3472
bool quit_now = false;
3473
buffer password = {};
3474
bool mandos_client_exited = false;
3475
bool password_is_read = false;
3477
const char not_a_directory[] = "/dev/tty";
3479
FILE *real_stderr = stderr;
3480
FILE *devnull = fopen("/dev/null", "we");
3481
g_assert_nonnull(devnull);
3483
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3484
&password, not_a_directory,
3485
&cancelled_filenames,
3487
&mandos_client_exited,
3488
&password_is_read));
3489
stderr = real_stderr;
3490
g_assert_cmpint(fclose(devnull), ==, 0);
3492
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3495
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3496
test_fixture *fixture,
3497
__attribute__((unused))
3500
__attribute__((cleanup(cleanup_close)))
3501
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3502
g_assert_cmpint(epoll_fd, >=, 0);
3503
__attribute__((cleanup(cleanup_queue)))
3504
task_queue *queue = create_queue();
3505
g_assert_nonnull(queue);
3506
__attribute__((cleanup(string_set_clear)))
3507
string_set cancelled_filenames = {};
3508
const mono_microsecs current_time = 0;
3510
bool quit_now = false;
3511
buffer password = {};
3512
bool mandos_client_exited = false;
3513
bool password_is_read = false;
3515
__attribute__((cleanup(cleanup_string)))
3516
char *tempdir = make_temporary_directory();
3517
g_assert_nonnull(tempdir);
3519
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3521
&cancelled_filenames,
3523
&mandos_client_exited,
3524
&password_is_read));
3526
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3528
const task_context *const added_read_task
3529
= find_matching_task(queue,
3530
(task_context){ .func=read_inotify_event });
3531
g_assert_nonnull(added_read_task);
3533
g_assert_cmpint(added_read_task->fd, >, 2);
3534
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3536
/* "sufficient to read at least one event." - inotify(7) */
3537
const size_t ievent_size = (sizeof(struct inotify_event)
3539
struct inotify_event *ievent = malloc(ievent_size);
3540
g_assert_nonnull(ievent);
3542
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3544
g_assert_cmpint(errno, ==, EAGAIN);
3548
g_assert_cmpint(rmdir(tempdir), ==, 0);
3551
static char *make_temporary_file_in_directory(const char
3555
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3556
test_fixture *fixture,
3557
__attribute__((unused))
3560
__attribute__((cleanup(cleanup_close)))
3561
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3562
g_assert_cmpint(epoll_fd, >=, 0);
3563
__attribute__((cleanup(cleanup_queue)))
3564
task_queue *queue = create_queue();
3565
g_assert_nonnull(queue);
3566
__attribute__((cleanup(string_set_clear)))
3567
string_set cancelled_filenames = {};
3568
const mono_microsecs current_time = 0;
3570
bool quit_now = false;
3571
buffer password = {};
3572
bool mandos_client_exited = false;
3573
bool password_is_read = false;
3575
__attribute__((cleanup(cleanup_string)))
3576
char *tempdir = make_temporary_directory();
3577
g_assert_nonnull(tempdir);
3579
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3581
&cancelled_filenames,
3583
&mandos_client_exited,
3584
&password_is_read));
3586
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3588
const task_context *const added_read_task
3589
= find_matching_task(queue,
3590
(task_context){ .func=read_inotify_event });
3591
g_assert_nonnull(added_read_task);
3593
g_assert_cmpint(added_read_task->fd, >, 2);
3594
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3596
__attribute__((cleanup(cleanup_string)))
3597
char *filename = make_temporary_file_in_directory(tempdir);
3598
g_assert_nonnull(filename);
3600
/* "sufficient to read at least one event." - inotify(7) */
3601
const size_t ievent_size = (sizeof(struct inotify_event)
3603
struct inotify_event *ievent = malloc(ievent_size);
3604
g_assert_nonnull(ievent);
3606
ssize_t read_size = 0;
3607
read_size = read(added_read_task->fd, ievent, ievent_size);
3609
g_assert_cmpint((int)read_size, >, 0);
3610
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3611
g_assert_cmpstr(ievent->name, ==, basename(filename));
3615
g_assert_cmpint(unlink(filename), ==, 0);
3616
g_assert_cmpint(rmdir(tempdir), ==, 0);
3619
static char *make_temporary_prefixed_file_in_directory(const char
3623
char *filename = NULL;
3624
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3626
g_assert_nonnull(filename);
3627
const int fd = mkostemp(filename, O_CLOEXEC);
3628
g_assert_cmpint(fd, >=, 0);
3629
g_assert_cmpint(close(fd), ==, 0);
3633
static char *make_temporary_file_in_directory(const char
3635
return make_temporary_prefixed_file_in_directory("temp", dir);
3639
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3640
test_fixture *fixture,
3641
__attribute__((unused))
3642
gconstpointer user_data){
3643
__attribute__((cleanup(cleanup_close)))
3644
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3645
g_assert_cmpint(epoll_fd, >=, 0);
3646
__attribute__((cleanup(cleanup_queue)))
3647
task_queue *queue = create_queue();
3648
g_assert_nonnull(queue);
3649
__attribute__((cleanup(string_set_clear)))
3650
string_set cancelled_filenames = {};
3651
const mono_microsecs current_time = 0;
3653
bool quit_now = false;
3654
buffer password = {};
3655
bool mandos_client_exited = false;
3656
bool password_is_read = false;
3658
__attribute__((cleanup(cleanup_string)))
3659
char *watchdir = make_temporary_directory();
3660
g_assert_nonnull(watchdir);
3662
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3663
&password, watchdir,
3664
&cancelled_filenames,
3666
&mandos_client_exited,
3667
&password_is_read));
3669
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3671
const task_context *const added_read_task
3672
= find_matching_task(queue,
3673
(task_context){ .func=read_inotify_event });
3674
g_assert_nonnull(added_read_task);
3676
g_assert_cmpint(added_read_task->fd, >, 2);
3677
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3679
char *sourcedir = make_temporary_directory();
3680
g_assert_nonnull(sourcedir);
3682
__attribute__((cleanup(cleanup_string)))
3683
char *filename = make_temporary_file_in_directory(sourcedir);
3684
g_assert_nonnull(filename);
3686
__attribute__((cleanup(cleanup_string)))
3687
char *targetfilename = NULL;
3688
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3689
basename(filename)), >, 0);
3690
g_assert_nonnull(targetfilename);
3692
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3693
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3696
/* "sufficient to read at least one event." - inotify(7) */
3697
const size_t ievent_size = (sizeof(struct inotify_event)
3699
struct inotify_event *ievent = malloc(ievent_size);
3700
g_assert_nonnull(ievent);
3702
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3704
g_assert_cmpint((int)read_size, >, 0);
3705
g_assert_true(ievent->mask & IN_MOVED_TO);
3706
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3710
g_assert_cmpint(unlink(targetfilename), ==, 0);
3711
g_assert_cmpint(rmdir(watchdir), ==, 0);
3715
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3716
test_fixture *fixture,
3717
__attribute__((unused))
3720
__attribute__((cleanup(cleanup_close)))
3721
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3722
g_assert_cmpint(epoll_fd, >=, 0);
3723
__attribute__((cleanup(cleanup_queue)))
3724
task_queue *queue = create_queue();
3725
g_assert_nonnull(queue);
3726
__attribute__((cleanup(string_set_clear)))
3727
string_set cancelled_filenames = {};
3728
const mono_microsecs current_time = 0;
3730
bool quit_now = false;
3731
buffer password = {};
3732
bool mandos_client_exited = false;
3733
bool password_is_read = false;
3735
__attribute__((cleanup(cleanup_string)))
3736
char *tempdir = make_temporary_directory();
3737
g_assert_nonnull(tempdir);
3739
__attribute__((cleanup(cleanup_string)))
3740
char *tempfilename = make_temporary_file_in_directory(tempdir);
3741
g_assert_nonnull(tempfilename);
3743
__attribute__((cleanup(cleanup_string)))
3744
char *targetdir = make_temporary_directory();
3745
g_assert_nonnull(targetdir);
3747
__attribute__((cleanup(cleanup_string)))
3748
char *targetfilename = NULL;
3749
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3750
basename(tempfilename)), >, 0);
3751
g_assert_nonnull(targetfilename);
3753
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3755
&cancelled_filenames,
3757
&mandos_client_exited,
3758
&password_is_read));
3760
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3762
const task_context *const added_read_task
3763
= find_matching_task(queue,
3764
(task_context){ .func=read_inotify_event });
3765
g_assert_nonnull(added_read_task);
3767
/* "sufficient to read at least one event." - inotify(7) */
3768
const size_t ievent_size = (sizeof(struct inotify_event)
3770
struct inotify_event *ievent = malloc(ievent_size);
3771
g_assert_nonnull(ievent);
3773
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3775
g_assert_cmpint((int)read_size, >, 0);
3776
g_assert_true(ievent->mask & IN_MOVED_FROM);
3777
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3781
g_assert_cmpint(unlink(targetfilename), ==, 0);
3782
g_assert_cmpint(rmdir(targetdir), ==, 0);
3783
g_assert_cmpint(rmdir(tempdir), ==, 0);
3787
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3788
test_fixture *fixture,
3789
__attribute__((unused))
3790
gconstpointer user_data){
3791
__attribute__((cleanup(cleanup_close)))
3792
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3793
g_assert_cmpint(epoll_fd, >=, 0);
3794
__attribute__((cleanup(cleanup_queue)))
3795
task_queue *queue = create_queue();
3796
g_assert_nonnull(queue);
3797
__attribute__((cleanup(string_set_clear)))
3798
string_set cancelled_filenames = {};
3799
const mono_microsecs current_time = 0;
3801
bool quit_now = false;
3802
buffer password = {};
3803
bool mandos_client_exited = false;
3804
bool password_is_read = false;
3806
__attribute__((cleanup(cleanup_string)))
3807
char *tempdir = make_temporary_directory();
3808
g_assert_nonnull(tempdir);
3810
__attribute__((cleanup(cleanup_string)))
3811
char *tempfile = make_temporary_file_in_directory(tempdir);
3812
g_assert_nonnull(tempfile);
3814
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3816
&cancelled_filenames,
3818
&mandos_client_exited,
3819
&password_is_read));
3820
g_assert_cmpint(unlink(tempfile), ==, 0);
3822
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3824
const task_context *const added_read_task
3825
= find_matching_task(queue,
3826
(task_context){ .func=read_inotify_event });
3827
g_assert_nonnull(added_read_task);
3829
g_assert_cmpint(added_read_task->fd, >, 2);
3830
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3832
/* "sufficient to read at least one event." - inotify(7) */
3833
const size_t ievent_size = (sizeof(struct inotify_event)
3835
struct inotify_event *ievent = malloc(ievent_size);
3836
g_assert_nonnull(ievent);
3838
ssize_t read_size = 0;
3839
read_size = read(added_read_task->fd, ievent, ievent_size);
3841
g_assert_cmpint((int)read_size, >, 0);
3842
g_assert_true(ievent->mask & IN_DELETE);
3843
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3847
g_assert_cmpint(rmdir(tempdir), ==, 0);
3851
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3852
test_fixture *fixture,
3853
__attribute__((unused))
3856
__attribute__((cleanup(cleanup_close)))
3857
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3858
g_assert_cmpint(epoll_fd, >=, 0);
3859
__attribute__((cleanup(cleanup_queue)))
3860
task_queue *queue = create_queue();
3861
g_assert_nonnull(queue);
3862
__attribute__((cleanup(string_set_clear)))
3863
string_set cancelled_filenames = {};
3864
const mono_microsecs current_time = 0;
3866
bool quit_now = false;
3867
buffer password = {};
3868
bool mandos_client_exited = false;
3869
bool password_is_read = false;
3871
__attribute__((cleanup(cleanup_string)))
3872
char *tempdir = make_temporary_directory();
3873
g_assert_nonnull(tempdir);
3875
__attribute__((cleanup(cleanup_string)))
3876
char *tempfile = make_temporary_file_in_directory(tempdir);
3877
g_assert_nonnull(tempfile);
3878
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3880
g_assert_cmpint(tempfile_fd, >, 2);
3882
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3884
&cancelled_filenames,
3886
&mandos_client_exited,
3887
&password_is_read));
3888
g_assert_cmpint(unlink(tempfile), ==, 0);
3890
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3892
const task_context *const added_read_task
3893
= find_matching_task(queue,
3894
(task_context){ .func=read_inotify_event });
3895
g_assert_nonnull(added_read_task);
3897
g_assert_cmpint(added_read_task->fd, >, 2);
3898
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3900
/* "sufficient to read at least one event." - inotify(7) */
3901
const size_t ievent_size = (sizeof(struct inotify_event)
3903
struct inotify_event *ievent = malloc(ievent_size);
3904
g_assert_nonnull(ievent);
3906
ssize_t read_size = 0;
3907
read_size = read(added_read_task->fd, ievent, ievent_size);
3909
g_assert_cmpint((int)read_size, >, 0);
3910
g_assert_true(ievent->mask & IN_DELETE);
3911
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3913
g_assert_cmpint(close(tempfile_fd), ==, 0);
3915
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3916
file not appear as an ievent, so we should not see it now. */
3917
read_size = read(added_read_task->fd, ievent, ievent_size);
3918
g_assert_cmpint((int)read_size, ==, -1);
3919
g_assert_true(errno == EAGAIN);
3923
g_assert_cmpint(rmdir(tempdir), ==, 0);
3926
static void test_read_inotify_event_readerror(__attribute__((unused))
3927
test_fixture *fixture,
3928
__attribute__((unused))
3931
__attribute__((cleanup(cleanup_close)))
3932
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3933
g_assert_cmpint(epoll_fd, >=, 0);
3934
const mono_microsecs current_time = 0;
3936
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3937
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3939
bool quit_now = false;
3940
__attribute__((cleanup(cleanup_queue)))
3941
task_queue *queue = create_queue();
3942
g_assert_nonnull(queue);
3944
task_context task = {
3945
.func=read_inotify_event,
3948
.quit_now=&quit_now,
3949
.filename=strdup("/nonexistent"),
3950
.cancelled_filenames = &(string_set){},
3952
.current_time=¤t_time,
3954
g_assert_nonnull(task.filename);
3955
run_task_with_stderr_to_dev_null(task, queue);
3956
g_assert_true(quit_now);
3957
g_assert_true(queue->next_run == 0);
3958
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3960
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3962
g_assert_cmpint(close(fd), ==, -1);
3965
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3966
test_fixture *fixture,
3967
__attribute__((unused))
3970
const mono_microsecs current_time = 17;
3973
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3974
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3976
bool quit_now = false;
3977
buffer password = {};
3978
bool mandos_client_exited = false;
3979
bool password_is_read = false;
3980
__attribute__((cleanup(cleanup_queue)))
3981
task_queue *queue = create_queue();
3982
g_assert_nonnull(queue);
3984
task_context task = {
3985
.func=read_inotify_event,
3988
.quit_now=&quit_now,
3989
.password=&password,
3990
.filename=strdup("/nonexistent"),
3991
.cancelled_filenames = &(string_set){},
3993
.current_time=¤t_time,
3994
.mandos_client_exited=&mandos_client_exited,
3995
.password_is_read=&password_is_read,
3997
g_assert_nonnull(task.filename);
3998
run_task_with_stderr_to_dev_null(task, queue);
4000
g_assert_nonnull(find_matching_task(queue, task));
4001
g_assert_true(queue->next_run == 1000000 + current_time);
4003
g_assert_cmpint(close(pipefds[0]), ==, 0);
4004
g_assert_cmpint(close(pipefds[1]), ==, 0);
4007
static void test_read_inotify_event_nodata(__attribute__((unused))
4008
test_fixture *fixture,
4009
__attribute__((unused))
4010
gconstpointer user_data){
4011
__attribute__((cleanup(cleanup_close)))
4012
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4013
g_assert_cmpint(epoll_fd, >=, 0);
4014
const mono_microsecs current_time = 0;
4017
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4019
bool quit_now = false;
4020
buffer password = {};
4021
bool mandos_client_exited = false;
4022
bool password_is_read = false;
4023
__attribute__((cleanup(cleanup_queue)))
4024
task_queue *queue = create_queue();
4025
g_assert_nonnull(queue);
4027
task_context task = {
4028
.func=read_inotify_event,
4031
.quit_now=&quit_now,
4032
.password=&password,
4033
.filename=strdup("/nonexistent"),
4034
.cancelled_filenames = &(string_set){},
4036
.current_time=¤t_time,
4037
.mandos_client_exited=&mandos_client_exited,
4038
.password_is_read=&password_is_read,
4040
g_assert_nonnull(task.filename);
4041
task.func(task, queue);
4042
g_assert_false(quit_now);
4043
g_assert_true(queue->next_run == 0);
4044
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4046
g_assert_nonnull(find_matching_task(queue, (task_context){
4047
.func=read_inotify_event,
4050
.quit_now=&quit_now,
4051
.password=&password,
4052
.filename=task.filename,
4053
.cancelled_filenames=task.cancelled_filenames,
4054
.current_time=¤t_time,
4055
.mandos_client_exited=&mandos_client_exited,
4056
.password_is_read=&password_is_read,
4059
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4060
EPOLLIN | EPOLLRDHUP));
4062
g_assert_cmpint(close(pipefds[1]), ==, 0);
4065
static void test_read_inotify_event_eof(__attribute__((unused))
4066
test_fixture *fixture,
4067
__attribute__((unused))
4068
gconstpointer user_data){
4069
__attribute__((cleanup(cleanup_close)))
4070
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4071
g_assert_cmpint(epoll_fd, >=, 0);
4072
const mono_microsecs current_time = 0;
4075
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4076
g_assert_cmpint(close(pipefds[1]), ==, 0);
4078
bool quit_now = false;
4079
buffer password = {};
4080
__attribute__((cleanup(cleanup_queue)))
4081
task_queue *queue = create_queue();
4082
g_assert_nonnull(queue);
4084
task_context task = {
4085
.func=read_inotify_event,
4088
.quit_now=&quit_now,
4089
.password=&password,
4090
.filename=strdup("/nonexistent"),
4091
.cancelled_filenames = &(string_set){},
4093
.current_time=¤t_time,
4095
run_task_with_stderr_to_dev_null(task, queue);
4096
g_assert_true(quit_now);
4097
g_assert_true(queue->next_run == 0);
4098
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4100
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4102
g_assert_cmpint(close(pipefds[0]), ==, -1);
4106
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4107
test_fixture *fixture,
4108
__attribute__((unused))
4109
gconstpointer user_data){
4110
__attribute__((cleanup(cleanup_close)))
4111
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4112
g_assert_cmpint(epoll_fd, >=, 0);
4113
const mono_microsecs current_time = 0;
4116
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4118
/* "sufficient to read at least one event." - inotify(7) */
4119
const size_t ievent_max_size = (sizeof(struct inotify_event)
4121
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4123
struct inotify_event event;
4124
char name_buffer[NAME_MAX + 1];
4126
struct inotify_event *const ievent = &ievent_buffer.event;
4128
const char dummy_file_name[] = "ask.dummy_file_name";
4129
ievent->mask = IN_CLOSE_WRITE;
4130
ievent->len = sizeof(dummy_file_name);
4131
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4132
const size_t ievent_size = (sizeof(struct inotify_event)
4133
+ sizeof(dummy_file_name));
4134
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4136
g_assert_cmpint(close(pipefds[1]), ==, 0);
4138
bool quit_now = false;
4139
buffer password = {};
4140
bool mandos_client_exited = false;
4141
bool password_is_read = false;
4142
__attribute__((cleanup(cleanup_queue)))
4143
task_queue *queue = create_queue();
4144
g_assert_nonnull(queue);
4146
task_context task = {
4147
.func=read_inotify_event,
4150
.quit_now=&quit_now,
4151
.password=&password,
4152
.filename=strdup("/nonexistent"),
4153
.cancelled_filenames = &(string_set){},
4155
.current_time=¤t_time,
4156
.mandos_client_exited=&mandos_client_exited,
4157
.password_is_read=&password_is_read,
4159
task.func(task, queue);
4160
g_assert_false(quit_now);
4161
g_assert_true(queue->next_run != 0);
4162
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4164
g_assert_nonnull(find_matching_task(queue, (task_context){
4165
.func=read_inotify_event,
4168
.quit_now=&quit_now,
4169
.password=&password,
4170
.filename=task.filename,
4171
.cancelled_filenames=task.cancelled_filenames,
4172
.current_time=¤t_time,
4173
.mandos_client_exited=&mandos_client_exited,
4174
.password_is_read=&password_is_read,
4177
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4178
EPOLLIN | EPOLLRDHUP));
4180
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4182
__attribute__((cleanup(cleanup_string)))
4183
char *filename = NULL;
4184
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4185
dummy_file_name), >, 0);
4186
g_assert_nonnull(filename);
4187
g_assert_nonnull(find_matching_task(queue, (task_context){
4188
.func=open_and_parse_question,
4191
.question_filename=filename,
4192
.password=&password,
4193
.cancelled_filenames=task.cancelled_filenames,
4194
.current_time=¤t_time,
4195
.mandos_client_exited=&mandos_client_exited,
4196
.password_is_read=&password_is_read,
4201
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4202
test_fixture *fixture,
4203
__attribute__((unused))
4204
gconstpointer user_data){
4205
__attribute__((cleanup(cleanup_close)))
4206
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4207
g_assert_cmpint(epoll_fd, >=, 0);
4208
const mono_microsecs current_time = 0;
4211
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4213
/* "sufficient to read at least one event." - inotify(7) */
4214
const size_t ievent_max_size = (sizeof(struct inotify_event)
4216
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4218
struct inotify_event event;
4219
char name_buffer[NAME_MAX + 1];
4221
struct inotify_event *const ievent = &ievent_buffer.event;
4223
const char dummy_file_name[] = "ask.dummy_file_name";
4224
ievent->mask = IN_MOVED_TO;
4225
ievent->len = sizeof(dummy_file_name);
4226
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4227
const size_t ievent_size = (sizeof(struct inotify_event)
4228
+ sizeof(dummy_file_name));
4229
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4231
g_assert_cmpint(close(pipefds[1]), ==, 0);
4233
bool quit_now = false;
4234
buffer password = {};
4235
bool mandos_client_exited = false;
4236
bool password_is_read = false;
4237
__attribute__((cleanup(cleanup_queue)))
4238
task_queue *queue = create_queue();
4239
g_assert_nonnull(queue);
4241
task_context task = {
4242
.func=read_inotify_event,
4245
.quit_now=&quit_now,
4246
.password=&password,
4247
.filename=strdup("/nonexistent"),
4248
.cancelled_filenames = &(string_set){},
4250
.current_time=¤t_time,
4251
.mandos_client_exited=&mandos_client_exited,
4252
.password_is_read=&password_is_read,
4254
task.func(task, queue);
4255
g_assert_false(quit_now);
4256
g_assert_true(queue->next_run != 0);
4257
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4259
g_assert_nonnull(find_matching_task(queue, (task_context){
4260
.func=read_inotify_event,
4263
.quit_now=&quit_now,
4264
.password=&password,
4265
.filename=task.filename,
4266
.cancelled_filenames=task.cancelled_filenames,
4267
.current_time=¤t_time,
4268
.mandos_client_exited=&mandos_client_exited,
4269
.password_is_read=&password_is_read,
4272
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4273
EPOLLIN | EPOLLRDHUP));
4275
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4277
__attribute__((cleanup(cleanup_string)))
4278
char *filename = NULL;
4279
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4280
dummy_file_name), >, 0);
4281
g_assert_nonnull(filename);
4282
g_assert_nonnull(find_matching_task(queue, (task_context){
4283
.func=open_and_parse_question,
4286
.question_filename=filename,
4287
.password=&password,
4288
.cancelled_filenames=task.cancelled_filenames,
4289
.current_time=¤t_time,
4290
.mandos_client_exited=&mandos_client_exited,
4291
.password_is_read=&password_is_read,
4296
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4297
test_fixture *fixture,
4298
__attribute__((unused))
4299
gconstpointer user_data){
4300
__attribute__((cleanup(cleanup_close)))
4301
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4302
g_assert_cmpint(epoll_fd, >=, 0);
4303
__attribute__((cleanup(string_set_clear)))
4304
string_set cancelled_filenames = {};
4305
const mono_microsecs current_time = 0;
4308
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4310
/* "sufficient to read at least one event." - inotify(7) */
4311
const size_t ievent_max_size = (sizeof(struct inotify_event)
4313
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4315
struct inotify_event event;
4316
char name_buffer[NAME_MAX + 1];
4318
struct inotify_event *const ievent = &ievent_buffer.event;
4320
const char dummy_file_name[] = "ask.dummy_file_name";
4321
ievent->mask = IN_MOVED_FROM;
4322
ievent->len = sizeof(dummy_file_name);
4323
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4324
const size_t ievent_size = (sizeof(struct inotify_event)
4325
+ sizeof(dummy_file_name));
4326
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4328
g_assert_cmpint(close(pipefds[1]), ==, 0);
4330
bool quit_now = false;
4331
buffer password = {};
4332
bool mandos_client_exited = false;
4333
bool password_is_read = false;
4334
__attribute__((cleanup(cleanup_queue)))
4335
task_queue *queue = create_queue();
4336
g_assert_nonnull(queue);
4338
task_context task = {
4339
.func=read_inotify_event,
4342
.quit_now=&quit_now,
4343
.password=&password,
4344
.filename=strdup("/nonexistent"),
4345
.cancelled_filenames=&cancelled_filenames,
4346
.current_time=¤t_time,
4347
.mandos_client_exited=&mandos_client_exited,
4348
.password_is_read=&password_is_read,
4350
task.func(task, queue);
4351
g_assert_false(quit_now);
4352
g_assert_true(queue->next_run == 0);
4353
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4355
g_assert_nonnull(find_matching_task(queue, (task_context){
4356
.func=read_inotify_event,
4359
.quit_now=&quit_now,
4360
.password=&password,
4361
.filename=task.filename,
4362
.cancelled_filenames=&cancelled_filenames,
4363
.current_time=¤t_time,
4364
.mandos_client_exited=&mandos_client_exited,
4365
.password_is_read=&password_is_read,
4368
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4369
EPOLLIN | EPOLLRDHUP));
4371
__attribute__((cleanup(cleanup_string)))
4372
char *filename = NULL;
4373
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4374
dummy_file_name), >, 0);
4375
g_assert_nonnull(filename);
4376
g_assert_true(string_set_contains(*task.cancelled_filenames,
4380
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4381
test_fixture *fixture,
4382
__attribute__((unused))
4385
__attribute__((cleanup(cleanup_close)))
4386
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4387
g_assert_cmpint(epoll_fd, >=, 0);
4388
__attribute__((cleanup(string_set_clear)))
4389
string_set cancelled_filenames = {};
4390
const mono_microsecs current_time = 0;
4393
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4395
/* "sufficient to read at least one event." - inotify(7) */
4396
const size_t ievent_max_size = (sizeof(struct inotify_event)
4398
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4400
struct inotify_event event;
4401
char name_buffer[NAME_MAX + 1];
4403
struct inotify_event *const ievent = &ievent_buffer.event;
4405
const char dummy_file_name[] = "ask.dummy_file_name";
4406
ievent->mask = IN_DELETE;
4407
ievent->len = sizeof(dummy_file_name);
4408
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4409
const size_t ievent_size = (sizeof(struct inotify_event)
4410
+ sizeof(dummy_file_name));
4411
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4413
g_assert_cmpint(close(pipefds[1]), ==, 0);
4415
bool quit_now = false;
4416
buffer password = {};
4417
bool mandos_client_exited = false;
4418
bool password_is_read = false;
4419
__attribute__((cleanup(cleanup_queue)))
4420
task_queue *queue = create_queue();
4421
g_assert_nonnull(queue);
4423
task_context task = {
4424
.func=read_inotify_event,
4427
.quit_now=&quit_now,
4428
.password=&password,
4429
.filename=strdup("/nonexistent"),
4430
.cancelled_filenames=&cancelled_filenames,
4431
.current_time=¤t_time,
4432
.mandos_client_exited=&mandos_client_exited,
4433
.password_is_read=&password_is_read,
4435
task.func(task, queue);
4436
g_assert_false(quit_now);
4437
g_assert_true(queue->next_run == 0);
4438
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4440
g_assert_nonnull(find_matching_task(queue, (task_context){
4441
.func=read_inotify_event,
4444
.quit_now=&quit_now,
4445
.password=&password,
4446
.filename=task.filename,
4447
.cancelled_filenames=&cancelled_filenames,
4448
.current_time=¤t_time,
4449
.mandos_client_exited=&mandos_client_exited,
4450
.password_is_read=&password_is_read,
4453
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4454
EPOLLIN | EPOLLRDHUP));
4456
__attribute__((cleanup(cleanup_string)))
4457
char *filename = NULL;
4458
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4459
dummy_file_name), >, 0);
4460
g_assert_nonnull(filename);
4461
g_assert_true(string_set_contains(*task.cancelled_filenames,
4466
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4467
test_fixture *fixture,
4468
__attribute__((unused))
4471
__attribute__((cleanup(cleanup_close)))
4472
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4473
g_assert_cmpint(epoll_fd, >=, 0);
4474
const mono_microsecs current_time = 0;
4477
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4479
/* "sufficient to read at least one event." - inotify(7) */
4480
const size_t ievent_max_size = (sizeof(struct inotify_event)
4482
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4484
struct inotify_event event;
4485
char name_buffer[NAME_MAX + 1];
4487
struct inotify_event *const ievent = &ievent_buffer.event;
4489
const char dummy_file_name[] = "ignored.dummy_file_name";
4490
ievent->mask = IN_CLOSE_WRITE;
4491
ievent->len = sizeof(dummy_file_name);
4492
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4493
const size_t ievent_size = (sizeof(struct inotify_event)
4494
+ sizeof(dummy_file_name));
4495
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4497
g_assert_cmpint(close(pipefds[1]), ==, 0);
4499
bool quit_now = false;
4500
buffer password = {};
4501
bool mandos_client_exited = false;
4502
bool password_is_read = false;
4503
__attribute__((cleanup(cleanup_queue)))
4504
task_queue *queue = create_queue();
4505
g_assert_nonnull(queue);
4507
task_context task = {
4508
.func=read_inotify_event,
4511
.quit_now=&quit_now,
4512
.password=&password,
4513
.filename=strdup("/nonexistent"),
4514
.cancelled_filenames = &(string_set){},
4516
.current_time=¤t_time,
4517
.mandos_client_exited=&mandos_client_exited,
4518
.password_is_read=&password_is_read,
4520
task.func(task, queue);
4521
g_assert_false(quit_now);
4522
g_assert_true(queue->next_run == 0);
4523
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4525
g_assert_nonnull(find_matching_task(queue, (task_context){
4526
.func=read_inotify_event,
4529
.quit_now=&quit_now,
4530
.password=&password,
4531
.filename=task.filename,
4532
.cancelled_filenames=task.cancelled_filenames,
4533
.current_time=¤t_time,
4534
.mandos_client_exited=&mandos_client_exited,
4535
.password_is_read=&password_is_read,
4538
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4539
EPOLLIN | EPOLLRDHUP));
4543
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4544
test_fixture *fixture,
4545
__attribute__((unused))
4546
gconstpointer user_data){
4547
__attribute__((cleanup(cleanup_close)))
4548
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4549
g_assert_cmpint(epoll_fd, >=, 0);
4550
const mono_microsecs current_time = 0;
4553
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4555
/* "sufficient to read at least one event." - inotify(7) */
4556
const size_t ievent_max_size = (sizeof(struct inotify_event)
4558
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4560
struct inotify_event event;
4561
char name_buffer[NAME_MAX + 1];
4563
struct inotify_event *const ievent = &ievent_buffer.event;
4565
const char dummy_file_name[] = "ignored.dummy_file_name";
4566
ievent->mask = IN_MOVED_TO;
4567
ievent->len = sizeof(dummy_file_name);
4568
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4569
const size_t ievent_size = (sizeof(struct inotify_event)
4570
+ sizeof(dummy_file_name));
4571
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4573
g_assert_cmpint(close(pipefds[1]), ==, 0);
4575
bool quit_now = false;
4576
buffer password = {};
4577
bool mandos_client_exited = false;
4578
bool password_is_read = false;
4579
__attribute__((cleanup(cleanup_queue)))
4580
task_queue *queue = create_queue();
4581
g_assert_nonnull(queue);
4583
task_context task = {
4584
.func=read_inotify_event,
4587
.quit_now=&quit_now,
4588
.password=&password,
4589
.filename=strdup("/nonexistent"),
4590
.cancelled_filenames = &(string_set){},
4592
.current_time=¤t_time,
4593
.mandos_client_exited=&mandos_client_exited,
4594
.password_is_read=&password_is_read,
4596
task.func(task, queue);
4597
g_assert_false(quit_now);
4598
g_assert_true(queue->next_run == 0);
4599
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4601
g_assert_nonnull(find_matching_task(queue, (task_context){
4602
.func=read_inotify_event,
4605
.quit_now=&quit_now,
4606
.password=&password,
4607
.filename=task.filename,
4608
.cancelled_filenames=task.cancelled_filenames,
4609
.current_time=¤t_time,
4610
.mandos_client_exited=&mandos_client_exited,
4611
.password_is_read=&password_is_read,
4614
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4615
EPOLLIN | EPOLLRDHUP));
4619
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4620
test_fixture *fixture,
4621
__attribute__((unused))
4624
__attribute__((cleanup(cleanup_close)))
4625
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4626
g_assert_cmpint(epoll_fd, >=, 0);
4627
__attribute__((cleanup(string_set_clear)))
4628
string_set cancelled_filenames = {};
4629
const mono_microsecs current_time = 0;
4632
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4634
/* "sufficient to read at least one event." - inotify(7) */
4635
const size_t ievent_max_size = (sizeof(struct inotify_event)
4637
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4639
struct inotify_event event;
4640
char name_buffer[NAME_MAX + 1];
4642
struct inotify_event *const ievent = &ievent_buffer.event;
4644
const char dummy_file_name[] = "ignored.dummy_file_name";
4645
ievent->mask = IN_MOVED_FROM;
4646
ievent->len = sizeof(dummy_file_name);
4647
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4648
const size_t ievent_size = (sizeof(struct inotify_event)
4649
+ sizeof(dummy_file_name));
4650
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4652
g_assert_cmpint(close(pipefds[1]), ==, 0);
4654
bool quit_now = false;
4655
buffer password = {};
4656
bool mandos_client_exited = false;
4657
bool password_is_read = false;
4658
__attribute__((cleanup(cleanup_queue)))
4659
task_queue *queue = create_queue();
4660
g_assert_nonnull(queue);
4662
task_context task = {
4663
.func=read_inotify_event,
4666
.quit_now=&quit_now,
4667
.password=&password,
4668
.filename=strdup("/nonexistent"),
4669
.cancelled_filenames=&cancelled_filenames,
4670
.current_time=¤t_time,
4671
.mandos_client_exited=&mandos_client_exited,
4672
.password_is_read=&password_is_read,
4674
task.func(task, queue);
4675
g_assert_false(quit_now);
4676
g_assert_true(queue->next_run == 0);
4677
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4679
g_assert_nonnull(find_matching_task(queue, (task_context){
4680
.func=read_inotify_event,
4683
.quit_now=&quit_now,
4684
.password=&password,
4685
.filename=task.filename,
4686
.cancelled_filenames=&cancelled_filenames,
4687
.current_time=¤t_time,
4688
.mandos_client_exited=&mandos_client_exited,
4689
.password_is_read=&password_is_read,
4692
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4693
EPOLLIN | EPOLLRDHUP));
4695
__attribute__((cleanup(cleanup_string)))
4696
char *filename = NULL;
4697
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4698
dummy_file_name), >, 0);
4699
g_assert_nonnull(filename);
4700
g_assert_false(string_set_contains(cancelled_filenames, filename));
4704
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4705
test_fixture *fixture,
4706
__attribute__((unused))
4709
__attribute__((cleanup(cleanup_close)))
4710
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4711
g_assert_cmpint(epoll_fd, >=, 0);
4712
__attribute__((cleanup(string_set_clear)))
4713
string_set cancelled_filenames = {};
4714
const mono_microsecs current_time = 0;
4717
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4719
/* "sufficient to read at least one event." - inotify(7) */
4720
const size_t ievent_max_size = (sizeof(struct inotify_event)
4722
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4724
struct inotify_event event;
4725
char name_buffer[NAME_MAX + 1];
4727
struct inotify_event *const ievent = &ievent_buffer.event;
4729
const char dummy_file_name[] = "ignored.dummy_file_name";
4730
ievent->mask = IN_DELETE;
4731
ievent->len = sizeof(dummy_file_name);
4732
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4733
const size_t ievent_size = (sizeof(struct inotify_event)
4734
+ sizeof(dummy_file_name));
4735
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4737
g_assert_cmpint(close(pipefds[1]), ==, 0);
4739
bool quit_now = false;
4740
buffer password = {};
4741
bool mandos_client_exited = false;
4742
bool password_is_read = false;
4743
__attribute__((cleanup(cleanup_queue)))
4744
task_queue *queue = create_queue();
4745
g_assert_nonnull(queue);
4747
task_context task = {
4748
.func=read_inotify_event,
4751
.quit_now=&quit_now,
4752
.password=&password,
4753
.filename=strdup("/nonexistent"),
4754
.cancelled_filenames=&cancelled_filenames,
4755
.current_time=¤t_time,
4756
.mandos_client_exited=&mandos_client_exited,
4757
.password_is_read=&password_is_read,
4759
task.func(task, queue);
4760
g_assert_false(quit_now);
4761
g_assert_true(queue->next_run == 0);
4762
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4764
g_assert_nonnull(find_matching_task(queue, (task_context){
4765
.func=read_inotify_event,
4768
.quit_now=&quit_now,
4769
.password=&password,
4770
.filename=task.filename,
4771
.cancelled_filenames=&cancelled_filenames,
4772
.current_time=¤t_time,
4773
.mandos_client_exited=&mandos_client_exited,
4774
.password_is_read=&password_is_read,
4777
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4778
EPOLLIN | EPOLLRDHUP));
4780
__attribute__((cleanup(cleanup_string)))
4781
char *filename = NULL;
4782
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4783
dummy_file_name), >, 0);
4784
g_assert_nonnull(filename);
4785
g_assert_false(string_set_contains(cancelled_filenames, filename));
4789
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4790
test_fixture *fixture,
4791
__attribute__((unused))
4792
gconstpointer user_data){
4793
__attribute__((cleanup(cleanup_close)))
4794
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4795
g_assert_cmpint(epoll_fd, >=, 0);
4796
__attribute__((cleanup(string_set_clear)))
4797
string_set cancelled_filenames = {};
4798
bool mandos_client_exited = false;
4799
bool password_is_read = false;
4800
__attribute__((cleanup(cleanup_queue)))
4801
task_queue *queue = create_queue();
4802
g_assert_nonnull(queue);
4804
char *const filename = strdup("/nonexistent");
4805
g_assert_nonnull(filename);
4806
task_context task = {
4807
.func=open_and_parse_question,
4808
.question_filename=filename,
4810
.password=(buffer[]){{}},
4812
.cancelled_filenames=&cancelled_filenames,
4813
.current_time=(mono_microsecs[]){0},
4814
.mandos_client_exited=&mandos_client_exited,
4815
.password_is_read=&password_is_read,
4817
task.func(task, queue);
4818
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4821
static void test_open_and_parse_question_EIO(__attribute__((unused))
4822
test_fixture *fixture,
4823
__attribute__((unused))
4824
gconstpointer user_data){
4825
__attribute__((cleanup(cleanup_close)))
4826
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4827
g_assert_cmpint(epoll_fd, >=, 0);
4828
__attribute__((cleanup(string_set_clear)))
4829
string_set cancelled_filenames = {};
4830
buffer password = {};
4831
bool mandos_client_exited = false;
4832
bool password_is_read = false;
4833
__attribute__((cleanup(cleanup_queue)))
4834
task_queue *queue = create_queue();
4835
g_assert_nonnull(queue);
4836
const mono_microsecs current_time = 0;
4838
char *filename = strdup("/proc/self/mem");
4839
task_context task = {
4840
.func=open_and_parse_question,
4841
.question_filename=filename,
4843
.password=&password,
4845
.cancelled_filenames=&cancelled_filenames,
4846
.current_time=¤t_time,
4847
.mandos_client_exited=&mandos_client_exited,
4848
.password_is_read=&password_is_read,
4850
run_task_with_stderr_to_dev_null(task, queue);
4851
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4855
test_open_and_parse_question_parse_error(__attribute__((unused))
4856
test_fixture *fixture,
4857
__attribute__((unused))
4858
gconstpointer user_data){
4859
__attribute__((cleanup(cleanup_close)))
4860
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4861
g_assert_cmpint(epoll_fd, >=, 0);
4862
__attribute__((cleanup(string_set_clear)))
4863
string_set cancelled_filenames = {};
4864
__attribute__((cleanup(cleanup_queue)))
4865
task_queue *queue = create_queue();
4866
g_assert_nonnull(queue);
4868
__attribute__((cleanup(cleanup_string)))
4869
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4870
g_assert_nonnull(tempfilename);
4871
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4872
g_assert_cmpint(tempfile, >, 0);
4873
const char bad_data[] = "this is bad syntax\n";
4874
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4875
==, sizeof(bad_data));
4876
g_assert_cmpint(close(tempfile), ==, 0);
4878
char *const filename = strdup(tempfilename);
4879
g_assert_nonnull(filename);
4880
task_context task = {
4881
.func=open_and_parse_question,
4882
.question_filename=filename,
4884
.password=(buffer[]){{}},
4886
.cancelled_filenames=&cancelled_filenames,
4887
.current_time=(mono_microsecs[]){0},
4888
.mandos_client_exited=(bool[]){false},
4889
.password_is_read=(bool[]){false},
4891
run_task_with_stderr_to_dev_null(task, queue);
4893
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4895
g_assert_cmpint(unlink(tempfilename), ==, 0);
4899
void test_open_and_parse_question_nosocket(__attribute__((unused))
4900
test_fixture *fixture,
4901
__attribute__((unused))
4902
gconstpointer user_data){
4903
__attribute__((cleanup(cleanup_close)))
4904
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4905
g_assert_cmpint(epoll_fd, >=, 0);
4906
__attribute__((cleanup(string_set_clear)))
4907
string_set cancelled_filenames = {};
4908
__attribute__((cleanup(cleanup_queue)))
4909
task_queue *queue = create_queue();
4910
g_assert_nonnull(queue);
4912
__attribute__((cleanup(cleanup_string)))
4913
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4914
g_assert_nonnull(tempfilename);
4915
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4916
g_assert_cmpint(questionfile, >, 0);
4917
FILE *qf = fdopen(questionfile, "w");
4918
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4919
g_assert_cmpint(fclose(qf), ==, 0);
4921
char *const filename = strdup(tempfilename);
4922
g_assert_nonnull(filename);
4923
task_context task = {
4924
.func=open_and_parse_question,
4925
.question_filename=filename,
4927
.password=(buffer[]){{}},
4929
.cancelled_filenames=&cancelled_filenames,
4930
.current_time=(mono_microsecs[]){0},
4931
.mandos_client_exited=(bool[]){false},
4932
.password_is_read=(bool[]){false},
4934
run_task_with_stderr_to_dev_null(task, queue);
4935
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4937
g_assert_cmpint(unlink(tempfilename), ==, 0);
4941
void test_open_and_parse_question_badsocket(__attribute__((unused))
4942
test_fixture *fixture,
4943
__attribute__((unused))
4944
gconstpointer user_data){
4945
__attribute__((cleanup(cleanup_close)))
4946
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4947
g_assert_cmpint(epoll_fd, >=, 0);
4948
__attribute__((cleanup(string_set_clear)))
4949
string_set cancelled_filenames = {};
4950
__attribute__((cleanup(cleanup_queue)))
4951
task_queue *queue = create_queue();
4952
g_assert_nonnull(queue);
4954
__attribute__((cleanup(cleanup_string)))
4955
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4956
g_assert_nonnull(tempfilename);
4957
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4958
g_assert_cmpint(questionfile, >, 0);
4959
FILE *qf = fdopen(questionfile, "w");
4960
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4961
g_assert_cmpint(fclose(qf), ==, 0);
4963
char *const filename = strdup(tempfilename);
4964
g_assert_nonnull(filename);
4965
task_context task = {
4966
.func=open_and_parse_question,
4967
.question_filename=filename,
4969
.password=(buffer[]){{}},
4971
.cancelled_filenames=&cancelled_filenames,
4972
.current_time=(mono_microsecs[]){0},
4973
.mandos_client_exited=(bool[]){false},
4974
.password_is_read=(bool[]){false},
4976
run_task_with_stderr_to_dev_null(task, queue);
4977
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4979
g_assert_cmpint(unlink(tempfilename), ==, 0);
4983
void test_open_and_parse_question_nopid(__attribute__((unused))
4984
test_fixture *fixture,
4985
__attribute__((unused))
4986
gconstpointer user_data){
4987
__attribute__((cleanup(cleanup_close)))
4988
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4989
g_assert_cmpint(epoll_fd, >=, 0);
4990
__attribute__((cleanup(string_set_clear)))
4991
string_set cancelled_filenames = {};
4992
__attribute__((cleanup(cleanup_queue)))
4993
task_queue *queue = create_queue();
4994
g_assert_nonnull(queue);
4996
__attribute__((cleanup(cleanup_string)))
4997
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4998
g_assert_nonnull(tempfilename);
4999
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5000
g_assert_cmpint(questionfile, >, 0);
5001
FILE *qf = fdopen(questionfile, "w");
5002
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5003
g_assert_cmpint(fclose(qf), ==, 0);
5005
char *const filename = strdup(tempfilename);
5006
g_assert_nonnull(filename);
5007
task_context task = {
5008
.func=open_and_parse_question,
5009
.question_filename=filename,
5011
.password=(buffer[]){{}},
5013
.cancelled_filenames=&cancelled_filenames,
5014
.current_time=(mono_microsecs[]){0},
5015
.mandos_client_exited=(bool[]){false},
5016
.password_is_read=(bool[]){false},
5018
run_task_with_stderr_to_dev_null(task, queue);
5019
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5021
g_assert_cmpint(unlink(tempfilename), ==, 0);
5025
void test_open_and_parse_question_badpid(__attribute__((unused))
5026
test_fixture *fixture,
5027
__attribute__((unused))
5028
gconstpointer user_data){
5029
__attribute__((cleanup(cleanup_close)))
5030
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5031
g_assert_cmpint(epoll_fd, >=, 0);
5032
__attribute__((cleanup(string_set_clear)))
5033
string_set cancelled_filenames = {};
5034
__attribute__((cleanup(cleanup_queue)))
5035
task_queue *queue = create_queue();
5036
g_assert_nonnull(queue);
5038
__attribute__((cleanup(cleanup_string)))
5039
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5040
g_assert_nonnull(tempfilename);
5041
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5042
g_assert_cmpint(questionfile, >, 0);
5043
FILE *qf = fdopen(questionfile, "w");
5044
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5046
g_assert_cmpint(fclose(qf), ==, 0);
5048
char *const filename = strdup(tempfilename);
5049
g_assert_nonnull(filename);
5050
task_context task = {
5051
.func=open_and_parse_question,
5052
.question_filename=filename,
5054
.password=(buffer[]){{}},
5056
.cancelled_filenames=&cancelled_filenames,
5057
.current_time=(mono_microsecs[]){0},
5058
.mandos_client_exited=(bool[]){false},
5059
.password_is_read=(bool[]){false},
5061
run_task_with_stderr_to_dev_null(task, queue);
5062
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5064
g_assert_cmpint(unlink(tempfilename), ==, 0);
5068
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5069
test_fixture *fixture,
5070
__attribute__((unused))
5071
gconstpointer user_data){
5072
__attribute__((cleanup(cleanup_close)))
5073
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5074
g_assert_cmpint(epoll_fd, >=, 0);
5075
__attribute__((cleanup(string_set_clear)))
5076
string_set cancelled_filenames = {};
5077
buffer password = {};
5078
bool mandos_client_exited = false;
5079
bool password_is_read = false;
5080
__attribute__((cleanup(cleanup_queue)))
5081
task_queue *queue = create_queue();
5082
g_assert_nonnull(queue);
5083
const mono_microsecs current_time = 0;
5085
/* Find value of sysctl kernel.pid_max */
5086
uintmax_t pid_max = 0;
5087
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5088
g_assert_nonnull(sysctl_pid_max);
5089
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5091
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5093
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5094
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5096
__attribute__((cleanup(cleanup_string)))
5097
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5098
g_assert_nonnull(tempfilename);
5099
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5100
g_assert_cmpint(questionfile, >, 0);
5101
FILE *qf = fdopen(questionfile, "w");
5102
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5103
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5105
g_assert_cmpint(fclose(qf), ==, 0);
5107
char *const question_filename = strdup(tempfilename);
5108
g_assert_nonnull(question_filename);
5109
task_context task = {
5110
.func=open_and_parse_question,
5111
.question_filename=question_filename,
5113
.password=&password,
5114
.filename=question_filename,
5115
.cancelled_filenames=&cancelled_filenames,
5116
.current_time=¤t_time,
5117
.mandos_client_exited=&mandos_client_exited,
5118
.password_is_read=&password_is_read,
5120
run_task_with_stderr_to_dev_null(task, queue);
5121
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5123
g_assert_cmpint(unlink(tempfilename), ==, 0);
5127
test_open_and_parse_question_no_notafter(__attribute__((unused))
5128
test_fixture *fixture,
5129
__attribute__((unused))
5130
gconstpointer user_data){
5131
__attribute__((cleanup(cleanup_close)))
5132
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5133
g_assert_cmpint(epoll_fd, >=, 0);
5134
__attribute__((cleanup(string_set_clear)))
5135
string_set cancelled_filenames = {};
5136
buffer password = {};
5137
bool mandos_client_exited = false;
5138
bool password_is_read = false;
5139
__attribute__((cleanup(cleanup_queue)))
5140
task_queue *queue = create_queue();
5141
g_assert_nonnull(queue);
5142
const mono_microsecs current_time = 0;
5144
__attribute__((cleanup(cleanup_string)))
5145
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5146
g_assert_nonnull(tempfilename);
5147
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5148
g_assert_cmpint(questionfile, >, 0);
5149
FILE *qf = fdopen(questionfile, "w");
5150
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5151
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5152
g_assert_cmpint(fclose(qf), ==, 0);
5154
char *const filename = strdup(tempfilename);
5155
g_assert_nonnull(filename);
5156
task_context task = {
5157
.func=open_and_parse_question,
5158
.question_filename=filename,
5160
.password=&password,
5162
.cancelled_filenames=&cancelled_filenames,
5163
.current_time=¤t_time,
5164
.mandos_client_exited=&mandos_client_exited,
5165
.password_is_read=&password_is_read,
5167
task.func(task, queue);
5168
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5170
__attribute__((cleanup(cleanup_string)))
5171
char *socket_filename = strdup("/nonexistent");
5172
g_assert_nonnull(socket_filename);
5173
g_assert_nonnull(find_matching_task(queue, (task_context){
5174
.func=connect_question_socket,
5175
.question_filename=tempfilename,
5176
.filename=socket_filename,
5178
.password=&password,
5179
.current_time=¤t_time,
5180
.mandos_client_exited=&mandos_client_exited,
5181
.password_is_read=&password_is_read,
5184
g_assert_true(queue->next_run != 0);
5186
g_assert_cmpint(unlink(tempfilename), ==, 0);
5190
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5191
test_fixture *fixture,
5192
__attribute__((unused))
5193
gconstpointer user_data){
5194
__attribute__((cleanup(cleanup_close)))
5195
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5196
g_assert_cmpint(epoll_fd, >=, 0);
5197
__attribute__((cleanup(string_set_clear)))
5198
string_set cancelled_filenames = {};
5199
buffer password = {};
5200
bool mandos_client_exited = false;
5201
bool password_is_read = false;
5202
__attribute__((cleanup(cleanup_queue)))
5203
task_queue *queue = create_queue();
5204
g_assert_nonnull(queue);
5205
const mono_microsecs current_time = 0;
5207
__attribute__((cleanup(cleanup_string)))
5208
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5209
g_assert_nonnull(tempfilename);
5210
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5211
g_assert_cmpint(questionfile, >, 0);
5212
FILE *qf = fdopen(questionfile, "w");
5213
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5214
PRIuMAX "\nNotAfter=\n",
5215
(uintmax_t)getpid()), >, 0);
5216
g_assert_cmpint(fclose(qf), ==, 0);
5218
char *const filename = strdup(tempfilename);
5219
g_assert_nonnull(filename);
5220
task_context task = {
5221
.func=open_and_parse_question,
5222
.question_filename=filename,
5224
.password=&password,
5226
.cancelled_filenames=&cancelled_filenames,
5227
.current_time=¤t_time,
5228
.mandos_client_exited=&mandos_client_exited,
5229
.password_is_read=&password_is_read,
5231
run_task_with_stderr_to_dev_null(task, queue);
5232
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5234
__attribute__((cleanup(cleanup_string)))
5235
char *socket_filename = strdup("/nonexistent");
5236
g_assert_nonnull(find_matching_task(queue, (task_context){
5237
.func=connect_question_socket,
5238
.question_filename=tempfilename,
5239
.filename=socket_filename,
5241
.password=&password,
5242
.current_time=¤t_time,
5243
.mandos_client_exited=&mandos_client_exited,
5244
.password_is_read=&password_is_read,
5246
g_assert_true(queue->next_run != 0);
5248
g_assert_cmpint(unlink(tempfilename), ==, 0);
5252
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5254
const mono_microsecs
5256
const mono_microsecs
5258
__attribute__((cleanup(cleanup_close)))
5259
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5260
g_assert_cmpint(epoll_fd, >=, 0);
5261
__attribute__((cleanup(string_set_clear)))
5262
string_set cancelled_filenames = {};
5263
buffer password = {};
5264
bool mandos_client_exited = false;
5265
bool password_is_read = false;
5266
__attribute__((cleanup(cleanup_queue)))
5267
task_queue *queue = create_queue();
5268
g_assert_nonnull(queue);
5269
queue->next_run = next_queue_run;
5271
__attribute__((cleanup(cleanup_string)))
5272
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5273
g_assert_nonnull(tempfilename);
5274
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5275
g_assert_cmpint(questionfile, >, 0);
5276
FILE *qf = fdopen(questionfile, "w");
5277
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5278
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5279
(uintmax_t)getpid(), notafter), >, 0);
5280
g_assert_cmpint(fclose(qf), ==, 0);
5282
char *const filename = strdup(tempfilename);
5283
g_assert_nonnull(filename);
5284
task_context task = {
5285
.func=open_and_parse_question,
5286
.question_filename=filename,
5288
.password=&password,
5290
.cancelled_filenames=&cancelled_filenames,
5291
.current_time=¤t_time,
5292
.mandos_client_exited=&mandos_client_exited,
5293
.password_is_read=&password_is_read,
5295
task.func(task, queue);
5297
if(queue->length >= 1){
5298
__attribute__((cleanup(cleanup_string)))
5299
char *socket_filename = strdup("/nonexistent");
5300
g_assert_nonnull(find_matching_task(queue, (task_context){
5301
.func=connect_question_socket,
5302
.filename=socket_filename,
5304
.password=&password,
5305
.current_time=¤t_time,
5306
.cancelled_filenames=&cancelled_filenames,
5307
.mandos_client_exited=&mandos_client_exited,
5308
.password_is_read=&password_is_read,
5310
g_assert_true(queue->next_run != 0);
5314
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5315
} else if(current_time >= notafter) {
5316
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5318
g_assert_nonnull(find_matching_task(queue, (task_context){
5319
.func=cancel_old_question,
5320
.question_filename=tempfilename,
5321
.filename=tempfilename,
5323
.cancelled_filenames=&cancelled_filenames,
5324
.current_time=¤t_time,
5327
g_assert_true(queue->next_run == 1);
5329
g_assert_cmpint(unlink(tempfilename), ==, 0);
5333
test_open_and_parse_question_notafter_0(__attribute__((unused))
5334
test_fixture *fixture,
5335
__attribute__((unused))
5336
gconstpointer user_data){
5337
/* current_time, notafter, next_queue_run */
5338
assert_open_and_parse_question_with_notafter(0, 0, 0);
5342
test_open_and_parse_question_notafter_1(__attribute__((unused))
5343
test_fixture *fixture,
5344
__attribute__((unused))
5345
gconstpointer user_data){
5346
/* current_time, notafter, next_queue_run */
5347
assert_open_and_parse_question_with_notafter(0, 1, 0);
5351
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5352
test_fixture *fixture,
5353
__attribute__((unused))
5354
gconstpointer user_data){
5355
/* current_time, notafter, next_queue_run */
5356
assert_open_and_parse_question_with_notafter(0, 1, 1);
5360
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5361
test_fixture *fixture,
5362
__attribute__((unused))
5363
gconstpointer user_data){
5364
/* current_time, notafter, next_queue_run */
5365
assert_open_and_parse_question_with_notafter(0, 1, 2);
5369
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5370
test_fixture *fixture,
5371
__attribute__((unused))
5372
gconstpointer user_data){
5373
/* current_time, notafter, next_queue_run */
5374
assert_open_and_parse_question_with_notafter(1, 1, 0);
5378
test_open_and_parse_question_late_notafter(__attribute__((unused))
5379
test_fixture *fixture,
5380
__attribute__((unused))
5381
gconstpointer user_data){
5382
/* current_time, notafter, next_queue_run */
5383
assert_open_and_parse_question_with_notafter(2, 1, 0);
5386
static void assert_cancel_old_question_param(const mono_microsecs
5388
const mono_microsecs
5390
const mono_microsecs
5392
const mono_microsecs
5394
__attribute__((cleanup(string_set_clear)))
5395
string_set cancelled_filenames = {};
5396
__attribute__((cleanup(cleanup_queue)))
5397
task_queue *queue = create_queue();
5398
g_assert_nonnull(queue);
5399
queue->next_run = next_queue_run;
5401
char *const question_filename = strdup("/nonexistent");
5402
g_assert_nonnull(question_filename);
5403
task_context task = {
5404
.func=cancel_old_question,
5405
.question_filename=question_filename,
5406
.filename=question_filename,
5408
.cancelled_filenames=&cancelled_filenames,
5409
.current_time=¤t_time,
5411
task.func(task, queue);
5413
if(current_time >= notafter){
5414
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5415
g_assert_true(string_set_contains(cancelled_filenames,
5418
g_assert_nonnull(find_matching_task(queue, (task_context){
5419
.func=cancel_old_question,
5420
.question_filename=question_filename,
5421
.filename=question_filename,
5423
.cancelled_filenames=&cancelled_filenames,
5424
.current_time=¤t_time,
5427
g_assert_false(string_set_contains(cancelled_filenames,
5428
question_filename));
5430
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5431
(unsigned int)next_set_to);
5434
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5435
test_fixture *fixture,
5436
__attribute__((unused))
5437
gconstpointer user_data){
5438
/* next_queue_run unset,
5439
cancellation should happen because time has come,
5440
next_queue_run should be unchanged */
5441
/* next_queue_run, notafter, current_time, next_set_to */
5442
assert_cancel_old_question_param(0, 1, 2, 0);
5445
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5446
test_fixture *fixture,
5447
__attribute__((unused))
5448
gconstpointer user_data){
5449
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5450
and current_time is not yet notafter or greater,
5451
update value of next_queue_run to value of notafter */
5452
/* next_queue_run, notafter, current_time, next_set_to */
5453
assert_cancel_old_question_param(0, 2, 1, 2);
5456
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5457
test_fixture *fixture,
5458
__attribute__((unused))
5459
gconstpointer user_data){
5460
/* next_queue_run 1,
5461
cancellation should happen because time has come,
5462
next_queue_run should be unchanged */
5463
/* next_queue_run, notafter, current_time, next_set_to */
5464
assert_cancel_old_question_param(1, 2, 3, 1);
5467
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5468
test_fixture *fixture,
5469
__attribute__((unused))
5470
gconstpointer user_data){
5471
/* If next_queue_run is set,
5472
and current_time is not yet notafter or greater,
5473
and notafter is larger than next_queue_run
5474
next_queue_run should be unchanged */
5475
/* next_queue_run, notafter, current_time, next_set_to */
5476
assert_cancel_old_question_param(1, 3, 2, 1);
5479
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5480
test_fixture *fixture,
5481
__attribute__((unused))
5482
gconstpointer user_data){
5483
/* next_queue_run 2,
5484
cancellation should happen because time has come,
5485
next_queue_run should be unchanged */
5486
/* next_queue_run, notafter, current_time, next_set_to */
5487
assert_cancel_old_question_param(2, 1, 3, 2);
5490
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5491
test_fixture *fixture,
5492
__attribute__((unused))
5493
gconstpointer user_data){
5494
/* If next_queue_run is set,
5495
and current_time is not yet notafter or greater,
5496
and notafter is larger than next_queue_run
5497
next_queue_run should be unchanged */
5498
/* next_queue_run, notafter, current_time, next_set_to */
5499
assert_cancel_old_question_param(2, 3, 1, 2);
5502
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5503
test_fixture *fixture,
5504
__attribute__((unused))
5505
gconstpointer user_data){
5506
/* next_queue_run 3,
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(3, 1, 2, 3);
5513
static void test_cancel_old_question_3_2_1(__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 smaller than next_queue_run
5520
update value of next_queue_run to value of notafter */
5521
/* next_queue_run, notafter, current_time, next_set_to */
5522
assert_cancel_old_question_param(3, 2, 1, 2);
5526
test_connect_question_socket_name_too_long(__attribute__((unused))
5527
test_fixture *fixture,
5528
__attribute__((unused))
5529
gconstpointer user_data){
5530
__attribute__((cleanup(cleanup_close)))
5531
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5532
g_assert_cmpint(epoll_fd, >=, 0);
5533
const char question_filename[] = "/nonexistent/question";
5534
__attribute__((cleanup(string_set_clear)))
5535
string_set cancelled_filenames = {};
5536
__attribute__((cleanup(cleanup_queue)))
5537
task_queue *queue = create_queue();
5538
g_assert_nonnull(queue);
5539
__attribute__((cleanup(cleanup_string)))
5540
char *tempdir = make_temporary_directory();
5541
g_assert_nonnull(tempdir);
5542
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5543
char socket_name[sizeof(unix_socket.sun_path)];
5544
memset(socket_name, 'x', sizeof(socket_name));
5545
socket_name[sizeof(socket_name)-1] = '\0';
5546
char *filename = NULL;
5547
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5549
g_assert_nonnull(filename);
5551
task_context task = {
5552
.func=connect_question_socket,
5553
.question_filename=strdup(question_filename),
5555
.password=(buffer[]){{}},
5557
.cancelled_filenames=&cancelled_filenames,
5558
.mandos_client_exited=(bool[]){false},
5559
.password_is_read=(bool[]){false},
5560
.current_time=(mono_microsecs[]){0},
5562
g_assert_nonnull(task.question_filename);
5563
run_task_with_stderr_to_dev_null(task, queue);
5565
g_assert_true(string_set_contains(cancelled_filenames,
5566
question_filename));
5567
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5568
g_assert_true(queue->next_run == 0);
5570
g_assert_cmpint(rmdir(tempdir), ==, 0);
5574
void test_connect_question_socket_connect_fail(__attribute__((unused))
5575
test_fixture *fixture,
5576
__attribute__((unused))
5579
__attribute__((cleanup(cleanup_close)))
5580
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5581
g_assert_cmpint(epoll_fd, >=, 0);
5582
const char question_filename[] = "/nonexistent/question";
5583
__attribute__((cleanup(string_set_clear)))
5584
string_set cancelled_filenames = {};
5585
const mono_microsecs current_time = 3;
5586
__attribute__((cleanup(cleanup_queue)))
5587
task_queue *queue = create_queue();
5588
g_assert_nonnull(queue);
5589
__attribute__((cleanup(cleanup_string)))
5590
char *tempdir = make_temporary_directory();
5591
g_assert_nonnull(tempdir);
5592
char socket_name[] = "nonexistent";
5593
char *filename = NULL;
5594
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5596
g_assert_nonnull(filename);
5598
task_context task = {
5599
.func=connect_question_socket,
5600
.question_filename=strdup(question_filename),
5602
.password=(buffer[]){{}},
5604
.cancelled_filenames=&cancelled_filenames,
5605
.mandos_client_exited=(bool[]){false},
5606
.password_is_read=(bool[]){false},
5607
.current_time=¤t_time,
5609
g_assert_nonnull(task.question_filename);
5610
run_task_with_stderr_to_dev_null(task, queue);
5612
g_assert_nonnull(find_matching_task(queue, task));
5614
g_assert_false(string_set_contains(cancelled_filenames,
5615
question_filename));
5616
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5617
g_assert_true(queue->next_run == 1000000 + current_time);
5619
g_assert_cmpint(rmdir(tempdir), ==, 0);
5623
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5624
test_fixture *fixture,
5625
__attribute__((unused))
5626
gconstpointer user_data){
5627
__attribute__((cleanup(cleanup_close)))
5628
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5629
__attribute__((cleanup(cleanup_string)))
5630
char *const question_filename = strdup("/nonexistent/question");
5631
g_assert_nonnull(question_filename);
5632
__attribute__((cleanup(string_set_clear)))
5633
string_set cancelled_filenames = {};
5634
const mono_microsecs current_time = 5;
5635
__attribute__((cleanup(cleanup_queue)))
5636
task_queue *queue = create_queue();
5637
g_assert_nonnull(queue);
5638
__attribute__((cleanup(cleanup_string)))
5639
char *tempdir = make_temporary_directory();
5640
g_assert_nonnull(tempdir);
5641
__attribute__((cleanup(cleanup_close)))
5642
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5643
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5644
g_assert_cmpint(sock_fd, >=, 0);
5645
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5646
const char socket_name[] = "socket_name";
5647
__attribute__((cleanup(cleanup_string)))
5648
char *filename = NULL;
5649
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5651
g_assert_nonnull(filename);
5652
g_assert_cmpint((int)strlen(filename), <,
5653
(int)sizeof(sock_name.sun_path));
5654
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5655
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5656
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5657
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5658
task_context task = {
5659
.func=connect_question_socket,
5660
.question_filename=strdup(question_filename),
5662
.password=(buffer[]){{}},
5663
.filename=strdup(filename),
5664
.cancelled_filenames=&cancelled_filenames,
5665
.mandos_client_exited=(bool[]){false},
5666
.password_is_read=(bool[]){false},
5667
.current_time=¤t_time,
5669
g_assert_nonnull(task.question_filename);
5670
run_task_with_stderr_to_dev_null(task, queue);
5672
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5673
const task_context *const added_task
5674
= find_matching_task(queue, task);
5675
g_assert_nonnull(added_task);
5676
g_assert_true(queue->next_run == 1000000 + current_time);
5678
g_assert_cmpint(unlink(filename), ==, 0);
5679
g_assert_cmpint(rmdir(tempdir), ==, 0);
5683
void test_connect_question_socket_usable(__attribute__((unused))
5684
test_fixture *fixture,
5685
__attribute__((unused))
5686
gconstpointer user_data){
5687
__attribute__((cleanup(cleanup_close)))
5688
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5689
g_assert_cmpint(epoll_fd, >=, 0);
5690
__attribute__((cleanup(cleanup_string)))
5691
char *const question_filename = strdup("/nonexistent/question");
5692
g_assert_nonnull(question_filename);
5693
__attribute__((cleanup(string_set_clear)))
5694
string_set cancelled_filenames = {};
5695
buffer password = {};
5696
bool mandos_client_exited = false;
5697
bool password_is_read = false;
5698
const mono_microsecs current_time = 0;
5699
__attribute__((cleanup(cleanup_queue)))
5700
task_queue *queue = create_queue();
5701
g_assert_nonnull(queue);
5702
__attribute__((cleanup(cleanup_string)))
5703
char *tempdir = make_temporary_directory();
5704
g_assert_nonnull(tempdir);
5705
__attribute__((cleanup(cleanup_close)))
5706
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5707
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5708
g_assert_cmpint(sock_fd, >=, 0);
5709
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5710
const char socket_name[] = "socket_name";
5711
__attribute__((cleanup(cleanup_string)))
5712
char *filename = NULL;
5713
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5715
g_assert_nonnull(filename);
5716
g_assert_cmpint((int)strlen(filename), <,
5717
(int)sizeof(sock_name.sun_path));
5718
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5719
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5720
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5721
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5722
task_context task = {
5723
.func=connect_question_socket,
5724
.question_filename=strdup(question_filename),
5726
.password=&password,
5727
.filename=strdup(filename),
5728
.cancelled_filenames=&cancelled_filenames,
5729
.mandos_client_exited=&mandos_client_exited,
5730
.password_is_read=&password_is_read,
5731
.current_time=¤t_time,
5733
g_assert_nonnull(task.question_filename);
5734
task.func(task, queue);
5736
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5737
const task_context *const added_task
5738
= find_matching_task(queue, (task_context){
5739
.func=send_password_to_socket,
5740
.question_filename=question_filename,
5743
.password=&password,
5744
.cancelled_filenames=&cancelled_filenames,
5745
.mandos_client_exited=&mandos_client_exited,
5746
.password_is_read=&password_is_read,
5747
.current_time=¤t_time,
5749
g_assert_nonnull(added_task);
5750
g_assert_cmpint(added_task->fd, >, 0);
5752
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5755
const int fd = added_task->fd;
5756
g_assert_cmpint(fd, >, 0);
5757
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5760
char write_data[PIPE_BUF];
5762
/* Construct test password buffer */
5763
/* Start with + since that is what the real procotol uses */
5764
write_data[0] = '+';
5765
/* Set a special character at string end just to mark the end */
5766
write_data[sizeof(write_data)-2] = 'y';
5767
/* Set NUL at buffer end, as suggested by the protocol */
5768
write_data[sizeof(write_data)-1] = '\0';
5769
/* Fill rest of password with 'x' */
5770
memset(write_data+1, 'x', sizeof(write_data)-3);
5771
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5772
MSG_NOSIGNAL), ==, sizeof(write_data));
5775
/* read from sock_fd */
5776
char read_data[sizeof(write_data)];
5777
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5778
==, sizeof(read_data));
5780
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5783
/* writing to sock_fd should fail */
5784
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5785
MSG_NOSIGNAL), <, 0);
5787
/* reading from fd should fail */
5788
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5789
MSG_NOSIGNAL), <, 0);
5791
g_assert_cmpint(unlink(filename), ==, 0);
5792
g_assert_cmpint(rmdir(tempdir), ==, 0);
5796
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5797
test_fixture *fixture,
5798
__attribute__((unused))
5801
__attribute__((cleanup(cleanup_close)))
5802
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5803
g_assert_cmpint(epoll_fd, >=, 0);
5804
__attribute__((cleanup(cleanup_string)))
5805
char *const question_filename = strdup("/nonexistent/question");
5806
g_assert_nonnull(question_filename);
5807
__attribute__((cleanup(cleanup_string)))
5808
char *const filename = strdup("/nonexistent/socket");
5809
g_assert_nonnull(filename);
5810
__attribute__((cleanup(string_set_clear)))
5811
string_set cancelled_filenames = {};
5812
buffer password = {};
5813
bool password_is_read = true;
5814
__attribute__((cleanup(cleanup_queue)))
5815
task_queue *queue = create_queue();
5816
g_assert_nonnull(queue);
5818
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5819
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5821
__attribute__((cleanup(cleanup_close)))
5822
const int read_socket = socketfds[0];
5823
const int write_socket = socketfds[1];
5824
task_context task = {
5825
.func=send_password_to_socket,
5826
.question_filename=strdup(question_filename),
5827
.filename=strdup(filename),
5830
.password=&password,
5831
.cancelled_filenames=&cancelled_filenames,
5832
.mandos_client_exited=(bool[]){false},
5833
.password_is_read=&password_is_read,
5834
.current_time=(mono_microsecs[]){0},
5836
g_assert_nonnull(task.question_filename);
5838
task.func(task, queue);
5840
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5842
const task_context *const added_task
5843
= find_matching_task(queue, task);
5844
g_assert_nonnull(added_task);
5845
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5846
g_assert_true(password_is_read);
5848
g_assert_cmpint(added_task->fd, >, 0);
5849
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5854
test_send_password_to_socket_password_not_read(__attribute__((unused))
5855
test_fixture *fixture,
5856
__attribute__((unused))
5859
__attribute__((cleanup(cleanup_close)))
5860
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5861
g_assert_cmpint(epoll_fd, >=, 0);
5862
__attribute__((cleanup(cleanup_string)))
5863
char *const question_filename = strdup("/nonexistent/question");
5864
g_assert_nonnull(question_filename);
5865
__attribute__((cleanup(cleanup_string)))
5866
char *const filename = strdup("/nonexistent/socket");
5867
__attribute__((cleanup(string_set_clear)))
5868
string_set cancelled_filenames = {};
5869
buffer password = {};
5870
__attribute__((cleanup(cleanup_queue)))
5871
task_queue *queue = create_queue();
5872
g_assert_nonnull(queue);
5874
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5875
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5877
__attribute__((cleanup(cleanup_close)))
5878
const int read_socket = socketfds[0];
5879
const int write_socket = socketfds[1];
5880
task_context task = {
5881
.func=send_password_to_socket,
5882
.question_filename=strdup(question_filename),
5883
.filename=strdup(filename),
5886
.password=&password,
5887
.cancelled_filenames=&cancelled_filenames,
5888
.mandos_client_exited=(bool[]){false},
5889
.password_is_read=(bool[]){false},
5890
.current_time=(mono_microsecs[]){0},
5892
g_assert_nonnull(task.question_filename);
5894
task.func(task, queue);
5896
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5898
const task_context *const added_task = find_matching_task(queue,
5900
g_assert_nonnull(added_task);
5901
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5902
g_assert_true(queue->next_run == 0);
5904
g_assert_cmpint(added_task->fd, >, 0);
5905
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5910
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5911
test_fixture *fixture,
5912
__attribute__((unused))
5913
gconstpointer user_data){
5914
__attribute__((cleanup(cleanup_close)))
5915
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5916
g_assert_cmpint(epoll_fd, >=, 0);
5917
const char question_filename[] = "/nonexistent/question";
5918
char *const filename = strdup("/nonexistent/socket");
5919
__attribute__((cleanup(string_set_clear)))
5920
string_set cancelled_filenames = {};
5921
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5922
__attribute__((cleanup(cleanup_buffer)))
5924
.data=malloc(oversized),
5926
.allocated=oversized,
5928
g_assert_nonnull(password.data);
5929
if(mlock(password.data, password.allocated) != 0){
5930
g_assert_true(errno == EPERM or errno == ENOMEM);
5932
/* Construct test password buffer */
5933
/* Start with + since that is what the real procotol uses */
5934
password.data[0] = '+';
5935
/* Set a special character at string end just to mark the end */
5936
password.data[oversized-3] = 'y';
5937
/* Set NUL at buffer end, as suggested by the protocol */
5938
password.data[oversized-2] = '\0';
5939
/* Fill rest of password with 'x' */
5940
memset(password.data+1, 'x', oversized-3);
5942
__attribute__((cleanup(cleanup_queue)))
5943
task_queue *queue = create_queue();
5944
g_assert_nonnull(queue);
5946
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5947
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5949
__attribute__((cleanup(cleanup_close)))
5950
const int read_socket = socketfds[0];
5951
__attribute__((cleanup(cleanup_close)))
5952
const int write_socket = socketfds[1];
5953
task_context task = {
5954
.func=send_password_to_socket,
5955
.question_filename=strdup(question_filename),
5959
.password=&password,
5960
.cancelled_filenames=&cancelled_filenames,
5961
.mandos_client_exited=(bool[]){true},
5962
.password_is_read=(bool[]){true},
5963
.current_time=(mono_microsecs[]){0},
5965
g_assert_nonnull(task.question_filename);
5967
run_task_with_stderr_to_dev_null(task, queue);
5969
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5970
g_assert_true(string_set_contains(cancelled_filenames,
5971
question_filename));
5974
static void test_send_password_to_socket_retry(__attribute__((unused))
5975
test_fixture *fixture,
5976
__attribute__((unused))
5979
__attribute__((cleanup(cleanup_close)))
5980
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5981
g_assert_cmpint(epoll_fd, >=, 0);
5982
__attribute__((cleanup(cleanup_string)))
5983
char *const question_filename = strdup("/nonexistent/question");
5984
g_assert_nonnull(question_filename);
5985
__attribute__((cleanup(cleanup_string)))
5986
char *const filename = strdup("/nonexistent/socket");
5987
g_assert_nonnull(filename);
5988
__attribute__((cleanup(string_set_clear)))
5989
string_set cancelled_filenames = {};
5990
__attribute__((cleanup(cleanup_buffer)))
5991
buffer password = {};
5993
__attribute__((cleanup(cleanup_queue)))
5994
task_queue *queue = create_queue();
5995
g_assert_nonnull(queue);
5997
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5998
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6000
__attribute__((cleanup(cleanup_close)))
6001
const int read_socket = socketfds[0];
6002
const int write_socket = socketfds[1];
6003
/* Close the server side socket to force ECONNRESET on client */
6004
g_assert_cmpint(close(read_socket), ==, 0);
6005
task_context task = {
6006
.func=send_password_to_socket,
6007
.question_filename=strdup(question_filename),
6008
.filename=strdup(filename),
6011
.password=&password,
6012
.cancelled_filenames=&cancelled_filenames,
6013
.mandos_client_exited=(bool[]){true},
6014
.password_is_read=(bool[]){true},
6015
.current_time=(mono_microsecs[]){0},
6017
g_assert_nonnull(task.question_filename);
6019
task.func(task, queue);
6021
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6023
const task_context *const added_task = find_matching_task(queue,
6025
g_assert_nonnull(added_task);
6026
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6028
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6033
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6034
test_fixture *fixture,
6035
__attribute__((unused))
6036
gconstpointer user_data){
6037
__attribute__((cleanup(cleanup_close)))
6038
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6039
__attribute__((cleanup(cleanup_string)))
6040
char *const question_filename = strdup("/nonexistent/question");
6041
g_assert_nonnull(question_filename);
6042
__attribute__((cleanup(cleanup_string)))
6043
char *const filename = strdup("/nonexistent/socket");
6044
g_assert_nonnull(filename);
6045
__attribute__((cleanup(string_set_clear)))
6046
string_set cancelled_filenames = {};
6047
__attribute__((cleanup(cleanup_buffer)))
6048
buffer password = {};
6050
const mono_microsecs current_time = 11;
6051
__attribute__((cleanup(cleanup_queue)))
6052
task_queue *queue = create_queue();
6053
g_assert_nonnull(queue);
6055
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6056
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6058
__attribute__((cleanup(cleanup_close)))
6059
const int read_socket = socketfds[0];
6060
const int write_socket = socketfds[1];
6061
/* Close the server side socket to force ECONNRESET on client */
6062
g_assert_cmpint(close(read_socket), ==, 0);
6063
task_context task = {
6064
.func=send_password_to_socket,
6065
.question_filename=strdup(question_filename),
6066
.filename=strdup(filename),
6069
.password=&password,
6070
.cancelled_filenames=&cancelled_filenames,
6071
.mandos_client_exited=(bool[]){true},
6072
.password_is_read=(bool[]){true},
6073
.current_time=¤t_time,
6075
g_assert_nonnull(task.question_filename);
6077
run_task_with_stderr_to_dev_null(task, queue);
6079
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6081
const task_context *const added_task = find_matching_task(queue,
6083
g_assert_nonnull(added_task);
6084
g_assert_true(queue->next_run == current_time + 1000000);
6085
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6088
static void assert_send_password_to_socket_password(buffer password){
6089
__attribute__((cleanup(cleanup_close)))
6090
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6091
g_assert_cmpint(epoll_fd, >=, 0);
6092
char *const question_filename = strdup("/nonexistent/question");
6093
g_assert_nonnull(question_filename);
6094
char *const filename = strdup("/nonexistent/socket");
6095
g_assert_nonnull(filename);
6096
__attribute__((cleanup(string_set_clear)))
6097
string_set cancelled_filenames = {};
6099
__attribute__((cleanup(cleanup_queue)))
6100
task_queue *queue = create_queue();
6101
g_assert_nonnull(queue);
6103
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6104
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6106
__attribute__((cleanup(cleanup_close)))
6107
const int read_socket = socketfds[0];
6108
const int write_socket = socketfds[1];
6109
task_context task = {
6110
.func=send_password_to_socket,
6111
.question_filename=question_filename,
6115
.password=&password,
6116
.cancelled_filenames=&cancelled_filenames,
6117
.mandos_client_exited=(bool[]){true},
6118
.password_is_read=(bool[]){true},
6119
.current_time=(mono_microsecs[]){0},
6122
char *expected_written_data = malloc(password.length + 2);
6123
g_assert_nonnull(expected_written_data);
6124
expected_written_data[0] = '+';
6125
expected_written_data[password.length + 1] = '\0';
6126
if(password.length > 0){
6127
g_assert_nonnull(password.data);
6128
memcpy(expected_written_data + 1, password.data, password.length);
6131
task.func(task, queue);
6134
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6135
(int)(password.length + 2));
6136
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6138
g_assert_true(memcmp(expected_written_data, buf,
6139
password.length + 2) == 0);
6141
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6143
free(expected_written_data);
6147
test_send_password_to_socket_null_password(__attribute__((unused))
6148
test_fixture *fixture,
6149
__attribute__((unused))
6150
gconstpointer user_data){
6151
__attribute__((cleanup(cleanup_buffer)))
6152
buffer password = {};
6153
assert_send_password_to_socket_password(password);
6157
test_send_password_to_socket_empty_password(__attribute__((unused))
6158
test_fixture *fixture,
6159
__attribute__((unused))
6160
gconstpointer user_data){
6161
__attribute__((cleanup(cleanup_buffer)))
6163
.data=malloc(1), /* because malloc(0) may return NULL */
6165
.allocated=0, /* deliberate lie */
6167
g_assert_nonnull(password.data);
6168
assert_send_password_to_socket_password(password);
6172
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6173
test_fixture *fixture,
6174
__attribute__((unused))
6175
gconstpointer user_data){
6176
__attribute__((cleanup(cleanup_buffer)))
6182
if(mlock(password.data, password.allocated) != 0){
6183
g_assert_true(errno == EPERM or errno == ENOMEM);
6185
assert_send_password_to_socket_password(password);
6189
test_send_password_to_socket_text_password(__attribute__((unused))
6190
test_fixture *fixture,
6191
__attribute__((unused))
6192
gconstpointer user_data){
6193
const char dummy_test_password[] = "dummy test password";
6194
__attribute__((cleanup(cleanup_buffer)))
6196
.data = strdup(dummy_test_password),
6197
.length = strlen(dummy_test_password),
6198
.allocated = sizeof(dummy_test_password),
6200
if(mlock(password.data, password.allocated) != 0){
6201
g_assert_true(errno == EPERM or errno == ENOMEM);
6203
assert_send_password_to_socket_password(password);
6207
test_send_password_to_socket_binary_password(__attribute__((unused))
6208
test_fixture *fixture,
6209
__attribute__((unused))
6210
gconstpointer user_data){
6211
__attribute__((cleanup(cleanup_buffer)))
6217
g_assert_nonnull(password.data);
6218
if(mlock(password.data, password.allocated) != 0){
6219
g_assert_true(errno == EPERM or errno == ENOMEM);
6221
char c = 1; /* Start at 1, avoiding NUL */
6222
for(int i=0; i < 255; i++){
6223
password.data[i] = c++;
6225
assert_send_password_to_socket_password(password);
6229
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6230
test_fixture *fixture,
6231
__attribute__((unused))
6234
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6235
__attribute__((cleanup(cleanup_buffer)))
6237
.data=malloc(sizeof(test_password)),
6238
.length=sizeof(test_password),
6239
.allocated=sizeof(test_password),
6241
g_assert_nonnull(password.data);
6242
if(mlock(password.data, password.allocated) !=0){
6243
g_assert_true(errno == EPERM or errno == ENOMEM);
6245
memcpy(password.data, test_password, password.allocated);
6246
assert_send_password_to_socket_password(password);
6249
static bool assert_add_existing_questions_to_devnull(task_queue
6262
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6263
test_fixture *fixture,
6264
__attribute__((unused))
6267
__attribute__((cleanup(cleanup_queue)))
6268
task_queue *queue = create_queue();
6269
g_assert_nonnull(queue);
6270
__attribute__((cleanup(cleanup_close)))
6271
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6272
g_assert_cmpint(epoll_fd, >=, 0);
6273
__attribute__((cleanup(string_set_clear)))
6274
string_set cancelled_filenames = {};
6276
g_assert_false(assert_add_existing_questions_to_devnull
6279
(buffer[]){{}}, /* password */
6280
&cancelled_filenames,
6281
(mono_microsecs[]){0}, /* current_time */
6282
(bool[]){false}, /* mandos_client_exited */
6283
(bool[]){false}, /* password_is_read */
6284
"/nonexistent")); /* dirname */
6286
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6290
bool assert_add_existing_questions_to_devnull(task_queue
6297
*cancelled_filenames,
6298
const mono_microsecs
6299
*const current_time,
6301
mandos_client_exited,
6306
__attribute__((cleanup(cleanup_close)))
6307
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6308
g_assert_cmpint(devnull_fd, >=, 0);
6309
__attribute__((cleanup(cleanup_close)))
6310
const int real_stderr_fd = dup(STDERR_FILENO);
6311
g_assert_cmpint(real_stderr_fd, >=, 0);
6312
dup2(devnull_fd, STDERR_FILENO);
6313
const bool ret = add_existing_questions(queue, epoll_fd, password,
6314
cancelled_filenames,
6316
mandos_client_exited,
6317
password_is_read, dirname);
6318
dup2(real_stderr_fd, STDERR_FILENO);
6323
void test_add_existing_questions_no_questions(__attribute__((unused))
6324
test_fixture *fixture,
6325
__attribute__((unused))
6328
__attribute__((cleanup(cleanup_queue)))
6329
task_queue *queue = create_queue();
6330
g_assert_nonnull(queue);
6331
__attribute__((cleanup(cleanup_close)))
6332
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6333
g_assert_cmpint(epoll_fd, >=, 0);
6334
__attribute__((cleanup(string_set_clear)))
6335
string_set cancelled_filenames = {};
6336
__attribute__((cleanup(cleanup_string)))
6337
char *tempdir = make_temporary_directory();
6338
g_assert_nonnull(tempdir);
6340
g_assert_false(assert_add_existing_questions_to_devnull
6343
(buffer[]){{}}, /* password */
6344
&cancelled_filenames,
6345
(mono_microsecs[]){0}, /* current_time */
6346
(bool[]){false}, /* mandos_client_exited */
6347
(bool[]){false}, /* password_is_read */
6350
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6352
g_assert_cmpint(rmdir(tempdir), ==, 0);
6355
static char *make_question_file_in_directory(const char *const);
6358
void test_add_existing_questions_one_question(__attribute__((unused))
6359
test_fixture *fixture,
6360
__attribute__((unused))
6363
__attribute__((cleanup(cleanup_queue)))
6364
task_queue *queue = create_queue();
6365
g_assert_nonnull(queue);
6366
__attribute__((cleanup(cleanup_close)))
6367
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6368
g_assert_cmpint(epoll_fd, >=, 0);
6369
__attribute__((cleanup(cleanup_buffer)))
6370
buffer password = {};
6371
__attribute__((cleanup(string_set_clear)))
6372
string_set cancelled_filenames = {};
6373
const mono_microsecs current_time = 0;
6374
bool mandos_client_exited = false;
6375
bool password_is_read = false;
6376
__attribute__((cleanup(cleanup_string)))
6377
char *tempdir = make_temporary_directory();
6378
g_assert_nonnull(tempdir);
6379
__attribute__((cleanup(cleanup_string)))
6380
char *question_filename
6381
= make_question_file_in_directory(tempdir);
6382
g_assert_nonnull(question_filename);
6384
g_assert_true(assert_add_existing_questions_to_devnull
6388
&cancelled_filenames,
6390
&mandos_client_exited,
6394
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6396
g_assert_nonnull(find_matching_task(queue, (task_context){
6397
.func=open_and_parse_question,
6399
.filename=question_filename,
6400
.question_filename=question_filename,
6401
.password=&password,
6402
.cancelled_filenames=&cancelled_filenames,
6403
.current_time=¤t_time,
6404
.mandos_client_exited=&mandos_client_exited,
6405
.password_is_read=&password_is_read,
6408
g_assert_true(queue->next_run == 1);
6410
g_assert_cmpint(unlink(question_filename), ==, 0);
6411
g_assert_cmpint(rmdir(tempdir), ==, 0);
6414
static char *make_question_file_in_directory(const char
6416
return make_temporary_prefixed_file_in_directory("ask.", dir);
6420
void test_add_existing_questions_two_questions(__attribute__((unused))
6421
test_fixture *fixture,
6422
__attribute__((unused))
6425
__attribute__((cleanup(cleanup_queue)))
6426
task_queue *queue = create_queue();
6427
g_assert_nonnull(queue);
6428
__attribute__((cleanup(cleanup_close)))
6429
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6430
g_assert_cmpint(epoll_fd, >=, 0);
6431
__attribute__((cleanup(cleanup_buffer)))
6432
buffer password = {};
6433
__attribute__((cleanup(string_set_clear)))
6434
string_set cancelled_filenames = {};
6435
const mono_microsecs current_time = 0;
6436
bool mandos_client_exited = false;
6437
bool password_is_read = false;
6438
__attribute__((cleanup(cleanup_string)))
6439
char *tempdir = make_temporary_directory();
6440
g_assert_nonnull(tempdir);
6441
__attribute__((cleanup(cleanup_string)))
6442
char *question_filename1
6443
= make_question_file_in_directory(tempdir);
6444
g_assert_nonnull(question_filename1);
6445
__attribute__((cleanup(cleanup_string)))
6446
char *question_filename2
6447
= make_question_file_in_directory(tempdir);
6448
g_assert_nonnull(question_filename2);
6450
g_assert_true(assert_add_existing_questions_to_devnull
6454
&cancelled_filenames,
6456
&mandos_client_exited,
6460
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6462
g_assert_true(queue->next_run == 1);
6464
__attribute__((cleanup(string_set_clear)))
6465
string_set seen_questions = {};
6467
bool queue_contains_question_opener(char *const question_filename){
6468
return(find_matching_task(queue, (task_context){
6469
.func=open_and_parse_question,
6471
.question_filename=question_filename,
6472
.password=&password,
6473
.cancelled_filenames=&cancelled_filenames,
6474
.current_time=¤t_time,
6475
.mandos_client_exited=&mandos_client_exited,
6476
.password_is_read=&password_is_read,
6480
g_assert_true(queue_contains_question_opener(question_filename1));
6481
g_assert_true(queue_contains_question_opener(question_filename2));
6483
g_assert_true(queue->next_run == 1);
6485
g_assert_cmpint(unlink(question_filename1), ==, 0);
6486
g_assert_cmpint(unlink(question_filename2), ==, 0);
6487
g_assert_cmpint(rmdir(tempdir), ==, 0);
6491
test_add_existing_questions_non_questions(__attribute__((unused))
6492
test_fixture *fixture,
6493
__attribute__((unused))
6494
gconstpointer user_data){
6495
__attribute__((cleanup(cleanup_queue)))
6496
task_queue *queue = create_queue();
6497
g_assert_nonnull(queue);
6498
__attribute__((cleanup(cleanup_close)))
6499
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6500
g_assert_cmpint(epoll_fd, >=, 0);
6501
__attribute__((cleanup(string_set_clear)))
6502
string_set cancelled_filenames = {};
6503
__attribute__((cleanup(cleanup_string)))
6504
char *tempdir = make_temporary_directory();
6505
g_assert_nonnull(tempdir);
6506
__attribute__((cleanup(cleanup_string)))
6507
char *question_filename1
6508
= make_temporary_file_in_directory(tempdir);
6509
g_assert_nonnull(question_filename1);
6510
__attribute__((cleanup(cleanup_string)))
6511
char *question_filename2
6512
= make_temporary_file_in_directory(tempdir);
6513
g_assert_nonnull(question_filename2);
6515
g_assert_false(assert_add_existing_questions_to_devnull
6518
(buffer[]){{}}, /* password */
6519
&cancelled_filenames,
6520
(mono_microsecs[]){0}, /* current_time */
6521
(bool[]){false}, /* mandos_client_exited */
6522
(bool[]){false}, /* password_is_read */
6525
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6527
g_assert_cmpint(unlink(question_filename1), ==, 0);
6528
g_assert_cmpint(unlink(question_filename2), ==, 0);
6529
g_assert_cmpint(rmdir(tempdir), ==, 0);
6533
test_add_existing_questions_both_types(__attribute__((unused))
6534
test_fixture *fixture,
6535
__attribute__((unused))
6536
gconstpointer user_data){
6537
__attribute__((cleanup(cleanup_queue)))
6538
task_queue *queue = create_queue();
6539
g_assert_nonnull(queue);
6540
__attribute__((cleanup(cleanup_close)))
6541
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6542
g_assert_cmpint(epoll_fd, >=, 0);
6543
__attribute__((cleanup(cleanup_buffer)))
6544
buffer password = {};
6545
__attribute__((cleanup(string_set_clear)))
6546
string_set cancelled_filenames = {};
6547
const mono_microsecs current_time = 0;
6548
bool mandos_client_exited = false;
6549
bool password_is_read = false;
6550
__attribute__((cleanup(cleanup_string)))
6551
char *tempdir = make_temporary_directory();
6552
g_assert_nonnull(tempdir);
6553
__attribute__((cleanup(cleanup_string)))
6554
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6555
g_assert_nonnull(tempfilename1);
6556
__attribute__((cleanup(cleanup_string)))
6557
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6558
g_assert_nonnull(tempfilename2);
6559
__attribute__((cleanup(cleanup_string)))
6560
char *question_filename
6561
= make_question_file_in_directory(tempdir);
6562
g_assert_nonnull(question_filename);
6564
g_assert_true(assert_add_existing_questions_to_devnull
6568
&cancelled_filenames,
6570
&mandos_client_exited,
6574
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6576
g_assert_nonnull(find_matching_task(queue, (task_context){
6577
.func=open_and_parse_question,
6579
.filename=question_filename,
6580
.question_filename=question_filename,
6581
.password=&password,
6582
.cancelled_filenames=&cancelled_filenames,
6583
.current_time=¤t_time,
6584
.mandos_client_exited=&mandos_client_exited,
6585
.password_is_read=&password_is_read,
6588
g_assert_true(queue->next_run == 1);
6590
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6591
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6592
g_assert_cmpint(unlink(question_filename), ==, 0);
6593
g_assert_cmpint(rmdir(tempdir), ==, 0);
6596
static void test_wait_for_event_timeout(__attribute__((unused))
6597
test_fixture *fixture,
6598
__attribute__((unused))
6599
gconstpointer user_data){
6600
__attribute__((cleanup(cleanup_close)))
6601
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6602
g_assert_cmpint(epoll_fd, >=, 0);
6604
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6607
static void test_wait_for_event_event(__attribute__((unused))
6608
test_fixture *fixture,
6609
__attribute__((unused))
6610
gconstpointer user_data){
6611
__attribute__((cleanup(cleanup_close)))
6612
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6613
g_assert_cmpint(epoll_fd, >=, 0);
6615
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6616
__attribute__((cleanup(cleanup_close)))
6617
const int read_pipe = pipefds[0];
6618
__attribute__((cleanup(cleanup_close)))
6619
const int write_pipe = pipefds[1];
6620
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6621
&(struct epoll_event)
6622
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6623
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6625
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6628
static void test_wait_for_event_sigchld(test_fixture *fixture,
6629
__attribute__((unused))
6630
gconstpointer user_data){
6631
const pid_t pid = fork();
6632
if(pid == 0){ /* Child */
6633
if(not restore_signal_handler(&fixture->orig_sigaction)){
6634
_exit(EXIT_FAILURE);
6636
if(not restore_sigmask(&fixture->orig_sigmask)){
6637
_exit(EXIT_FAILURE);
6641
g_assert_true(pid != -1);
6642
__attribute__((cleanup(cleanup_close)))
6643
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6644
g_assert_cmpint(epoll_fd, >=, 0);
6646
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6649
g_assert_true(waitpid(pid, &status, 0) == pid);
6650
g_assert_true(WIFEXITED(status));
6651
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6654
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6655
test_fixture *fixture,
6656
__attribute__((unused))
6657
gconstpointer user_data){
6658
__attribute__((cleanup(cleanup_queue)))
6659
task_queue *queue = create_queue();
6660
g_assert_nonnull(queue);
6661
queue->next_run = 1;
6662
__attribute__((cleanup(cleanup_close)))
6663
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6664
__attribute__((cleanup(string_set_clear)))
6665
string_set cancelled_filenames = {};
6666
bool quit_now = false;
6668
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6669
g_assert_false(quit_now);
6670
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6674
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6675
test_fixture *fixture,
6676
__attribute__((unused))
6679
__attribute__((cleanup(cleanup_queue)))
6680
task_queue *queue = create_queue();
6681
g_assert_nonnull(queue);
6682
__attribute__((cleanup(string_set_clear)))
6683
string_set cancelled_filenames = {};
6684
bool quit_now = false;
6685
const char question_filename[] = "/nonexistent/question_filename";
6686
g_assert_true(string_set_add(&cancelled_filenames,
6687
question_filename));
6689
g_assert_true(add_to_queue(queue,
6690
(task_context){ .func=dummy_func }));
6692
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6693
g_assert_false(quit_now);
6694
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6695
g_assert_false(string_set_contains(cancelled_filenames,
6696
question_filename));
6700
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6701
test_fixture *fixture,
6702
__attribute__((unused))
6705
__attribute__((cleanup(cleanup_queue)))
6706
task_queue *queue = create_queue();
6707
g_assert_nonnull(queue);
6708
__attribute__((cleanup(string_set_clear)))
6709
string_set cancelled_filenames = {};
6710
bool quit_now = false;
6712
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6713
__attribute__((cleanup(cleanup_close)))
6714
const int read_pipe = pipefds[0];
6715
g_assert_cmpint(close(pipefds[1]), ==, 0);
6716
const char question_filename[] = "/nonexistent/question_filename";
6717
g_assert_true(string_set_add(&cancelled_filenames,
6718
question_filename));
6719
__attribute__((nonnull))
6720
void quit_func(const task_context task,
6721
__attribute__((unused)) task_queue *const q){
6722
g_assert_nonnull(task.quit_now);
6723
*task.quit_now = true;
6725
task_context task = {
6727
.question_filename=strdup(question_filename),
6728
.quit_now=&quit_now,
6731
g_assert_nonnull(task.question_filename);
6733
g_assert_true(add_to_queue(queue, task));
6735
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6736
g_assert_false(quit_now);
6738
/* read_pipe should be closed already */
6740
bool read_pipe_closed = (close(read_pipe) == -1);
6741
read_pipe_closed &= (errno == EBADF);
6742
g_assert_true(read_pipe_closed);
6745
static void test_run_queue_one_task(__attribute__((unused))
6746
test_fixture *fixture,
6747
__attribute__((unused))
6748
gconstpointer user_data){
6749
__attribute__((cleanup(cleanup_queue)))
6750
task_queue *queue = create_queue();
6751
g_assert_nonnull(queue);
6752
__attribute__((cleanup(string_set_clear)))
6753
string_set cancelled_filenames = {};
6754
bool quit_now = false;
6756
__attribute__((nonnull))
6757
void next_run_func(__attribute__((unused))
6758
const task_context task,
6759
task_queue *const q){
6763
task_context task = {
6764
.func=next_run_func,
6766
g_assert_true(add_to_queue(queue, task));
6768
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6769
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6770
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6773
static void test_run_queue_two_tasks(__attribute__((unused))
6774
test_fixture *fixture,
6775
__attribute__((unused))
6776
gconstpointer user_data){
6777
__attribute__((cleanup(cleanup_queue)))
6778
task_queue *queue = create_queue();
6779
g_assert_nonnull(queue);
6780
queue->next_run = 1;
6781
__attribute__((cleanup(string_set_clear)))
6782
string_set cancelled_filenames = {};
6783
bool quit_now = false;
6784
bool mandos_client_exited = false;
6786
__attribute__((nonnull))
6787
void next_run_func(__attribute__((unused))
6788
const task_context task,
6789
task_queue *const q){
6793
__attribute__((nonnull))
6794
void exited_func(const task_context task,
6795
__attribute__((unused)) task_queue *const q){
6796
*task.mandos_client_exited = true;
6799
task_context task1 = {
6800
.func=next_run_func,
6802
g_assert_true(add_to_queue(queue, task1));
6804
task_context task2 = {
6806
.mandos_client_exited=&mandos_client_exited,
6808
g_assert_true(add_to_queue(queue, task2));
6810
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6811
g_assert_false(quit_now);
6812
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6813
g_assert_true(mandos_client_exited);
6814
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6817
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6818
test_fixture *fixture,
6819
__attribute__((unused))
6820
gconstpointer user_data){
6821
__attribute__((cleanup(cleanup_queue)))
6822
task_queue *queue = create_queue();
6823
g_assert_nonnull(queue);
6824
__attribute__((cleanup(string_set_clear)))
6825
string_set cancelled_filenames = {};
6826
bool quit_now = false;
6827
bool mandos_client_exited = false;
6828
bool password_is_read = false;
6830
__attribute__((nonnull))
6831
void set_exited_func(const task_context task,
6832
__attribute__((unused)) task_queue *const q){
6833
*task.mandos_client_exited = true;
6834
*task.quit_now = true;
6836
task_context task1 = {
6837
.func=set_exited_func,
6838
.quit_now=&quit_now,
6839
.mandos_client_exited=&mandos_client_exited,
6841
g_assert_true(add_to_queue(queue, task1));
6843
__attribute__((nonnull))
6844
void set_read_func(const task_context task,
6845
__attribute__((unused)) task_queue *const q){
6846
*task.quit_now = true;
6847
*task.password_is_read = true;
6849
task_context task2 = {
6850
.func=set_read_func,
6851
.quit_now=&quit_now,
6852
.password_is_read=&password_is_read,
6854
g_assert_true(add_to_queue(queue, task2));
6856
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6857
g_assert_true(quit_now);
6858
g_assert_true(mandos_client_exited xor password_is_read);
6859
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6862
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6863
test_fixture *fixture,
6864
__attribute__((unused))
6865
gconstpointer user_data){
6866
__attribute__((cleanup(cleanup_queue)))
6867
task_queue *queue = create_queue();
6868
g_assert_nonnull(queue);
6869
__attribute__((cleanup(string_set_clear)))
6870
string_set cancelled_filenames = {};
6872
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6873
__attribute__((cleanup(cleanup_close)))
6874
const int read_pipe = pipefds[0];
6875
__attribute__((cleanup(cleanup_close)))
6876
const int write_pipe = pipefds[1];
6877
bool quit_now = false;
6879
__attribute__((nonnull))
6880
void read_func(const task_context task,
6881
__attribute__((unused)) task_queue *const q){
6882
*task.quit_now = true;
6884
task_context task1 = {
6886
.quit_now=&quit_now,
6889
g_assert_true(add_to_queue(queue, task1));
6891
__attribute__((nonnull))
6892
void write_func(const task_context task,
6893
__attribute__((unused)) task_queue *const q){
6894
*task.quit_now = true;
6896
task_context task2 = {
6898
.quit_now=&quit_now,
6901
g_assert_true(add_to_queue(queue, task2));
6903
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6904
g_assert_true(quit_now);
6906
/* Either read_pipe or write_pipe should be closed already */
6908
bool close_read_pipe = (close(read_pipe) == -1);
6909
close_read_pipe &= (errno == EBADF);
6911
bool close_write_pipe = (close(write_pipe) == -1);
6912
close_write_pipe &= (errno == EBADF);
6913
g_assert_true(close_read_pipe xor close_write_pipe);
6914
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6917
static void test_setup_signal_handler(__attribute__((unused))
6918
test_fixture *fixture,
6919
__attribute__((unused))
6920
gconstpointer user_data){
6921
/* Save current SIGCHLD action, whatever it is */
6922
struct sigaction expected_sigchld_action;
6923
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6926
/* Act; i.e. run the setup_signal_handler() function */
6927
struct sigaction actual_old_sigchld_action;
6928
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6930
/* Check that the function correctly set "actual_old_sigchld_action"
6931
to the same values as the previously saved
6932
"expected_sigchld_action" */
6933
/* Check member sa_handler */
6934
g_assert_true(actual_old_sigchld_action.sa_handler
6935
== expected_sigchld_action.sa_handler);
6936
/* Check member sa_mask */
6937
for(int signum = 1; signum < NSIG; signum++){
6938
const int expected_old_block_state
6939
= sigismember(&expected_sigchld_action.sa_mask, signum);
6940
g_assert_cmpint(expected_old_block_state, >=, 0);
6941
const int actual_old_block_state
6942
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6943
g_assert_cmpint(actual_old_block_state, >=, 0);
6944
g_assert_cmpint(actual_old_block_state,
6945
==, expected_old_block_state);
6947
/* Check member sa_flags */
6948
g_assert_true((actual_old_sigchld_action.sa_flags
6949
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6950
== (expected_sigchld_action.sa_flags
6951
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6953
/* Retrieve the current signal handler for SIGCHLD as set by
6954
setup_signal_handler() */
6955
struct sigaction actual_new_sigchld_action;
6956
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6957
&actual_new_sigchld_action), ==, 0);
6958
/* Check that the signal handler (member sa_handler) is correctly
6959
set to the "handle_sigchld" function */
6960
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6961
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6962
g_assert_true(actual_new_sigchld_action.sa_handler
6964
/* Check (in member sa_mask) that at least a handful of signals are
6965
actually blocked during the signal handler */
6966
for(int signum = 1; signum < NSIG; signum++){
6967
int actual_new_block_state;
6973
actual_new_block_state
6974
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6975
g_assert_cmpint(actual_new_block_state, ==, 1);
6977
case SIGKILL: /* non-blockable */
6978
case SIGSTOP: /* non-blockable */
6979
case SIGCHLD: /* always blocked */
6984
/* Check member sa_flags */
6985
g_assert_true((actual_new_sigchld_action.sa_flags
6986
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6987
== (SA_NOCLDSTOP | SA_RESTART));
6989
/* Restore signal handler */
6990
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6994
static void test_restore_signal_handler(__attribute__((unused))
6995
test_fixture *fixture,
6996
__attribute__((unused))
6997
gconstpointer user_data){
6998
/* Save current SIGCHLD action, whatever it is */
6999
struct sigaction expected_sigchld_action;
7000
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7002
/* Since we haven't established a signal handler yet, there should
7003
not be one established. But another test may have relied on
7004
restore_signal_handler() to restore the signal handler, and if
7005
restore_signal_handler() is buggy (which we should be prepared
7006
for in this test) the signal handler may not have been restored
7007
properly; check for this: */
7008
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7010
/* Establish a signal handler */
7011
struct sigaction sigchld_action = {
7012
.sa_handler=handle_sigchld,
7013
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7015
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7016
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7018
/* Act; i.e. run the restore_signal_handler() function */
7019
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7021
/* Retrieve the restored signal handler data */
7022
struct sigaction actual_restored_sigchld_action;
7023
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7024
&actual_restored_sigchld_action), ==, 0);
7026
/* Check that the function correctly restored the signal action, as
7027
saved in "actual_restored_sigchld_action", to the same values as
7028
the previously saved "expected_sigchld_action" */
7029
/* Check member sa_handler */
7030
g_assert_true(actual_restored_sigchld_action.sa_handler
7031
== expected_sigchld_action.sa_handler);
7032
/* Check member sa_mask */
7033
for(int signum = 1; signum < NSIG; signum++){
7034
const int expected_old_block_state
7035
= sigismember(&expected_sigchld_action.sa_mask, signum);
7036
g_assert_cmpint(expected_old_block_state, >=, 0);
7037
const int actual_restored_block_state
7038
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7039
g_assert_cmpint(actual_restored_block_state, >=, 0);
7040
g_assert_cmpint(actual_restored_block_state,
7041
==, expected_old_block_state);
7043
/* Check member sa_flags */
7044
g_assert_true((actual_restored_sigchld_action.sa_flags
7045
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7046
== (expected_sigchld_action.sa_flags
7047
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7050
static void test_block_sigchld(__attribute__((unused))
7051
test_fixture *fixture,
7052
__attribute__((unused))
7053
gconstpointer user_data){
7054
/* Save original signal mask */
7055
sigset_t expected_sigmask;
7056
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7059
/* Make sure SIGCHLD is unblocked for this test */
7060
sigset_t sigchld_sigmask;
7061
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7062
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7063
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7066
/* Act; i.e. run the block_sigchld() function */
7067
sigset_t actual_old_sigmask;
7068
g_assert_true(block_sigchld(&actual_old_sigmask));
7070
/* Check the actual_old_sigmask; it should be the same as the
7071
previously saved signal mask "expected_sigmask". */
7072
for(int signum = 1; signum < NSIG; signum++){
7073
const int expected_old_block_state
7074
= sigismember(&expected_sigmask, signum);
7075
g_assert_cmpint(expected_old_block_state, >=, 0);
7076
const int actual_old_block_state
7077
= sigismember(&actual_old_sigmask, signum);
7078
g_assert_cmpint(actual_old_block_state, >=, 0);
7079
g_assert_cmpint(actual_old_block_state,
7080
==, expected_old_block_state);
7083
/* Retrieve the newly set signal mask */
7084
sigset_t actual_sigmask;
7085
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7087
/* SIGCHLD should be blocked */
7088
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7090
/* Restore signal mask */
7091
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7095
static void test_restore_sigmask(__attribute__((unused))
7096
test_fixture *fixture,
7097
__attribute__((unused))
7098
gconstpointer user_data){
7099
/* Save original signal mask */
7100
sigset_t orig_sigmask;
7101
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7103
/* Make sure SIGCHLD is blocked for this test */
7104
sigset_t sigchld_sigmask;
7105
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7106
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7107
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7110
/* Act; i.e. run the restore_sigmask() function */
7111
g_assert_true(restore_sigmask(&orig_sigmask));
7113
/* Retrieve the newly restored signal mask */
7114
sigset_t restored_sigmask;
7115
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7118
/* Check the restored_sigmask; it should be the same as the
7119
previously saved signal mask "orig_sigmask". */
7120
for(int signum = 1; signum < NSIG; signum++){
7121
const int orig_block_state = sigismember(&orig_sigmask, signum);
7122
g_assert_cmpint(orig_block_state, >=, 0);
7123
const int restored_block_state = sigismember(&restored_sigmask,
7125
g_assert_cmpint(restored_block_state, >=, 0);
7126
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7129
/* Restore signal mask */
7130
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7134
static void test_parse_arguments_noargs(__attribute__((unused))
7135
test_fixture *fixture,
7136
__attribute__((unused))
7137
gconstpointer user_data){
7141
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7143
char *agent_directory = NULL;
7144
char *helper_directory = NULL;
7147
char *mandos_argz = NULL;
7148
size_t mandos_argz_length = 0;
7150
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7151
&helper_directory, &user, &group,
7152
&mandos_argz, &mandos_argz_length));
7153
g_assert_null(agent_directory);
7154
g_assert_null(helper_directory);
7155
g_assert_true(user == 0);
7156
g_assert_true(group == 0);
7157
g_assert_null(mandos_argz);
7158
g_assert_true(mandos_argz_length == 0);
7160
for(char **arg = argv; *arg != NULL; arg++){
7165
__attribute__((nonnull))
7166
static bool parse_arguments_devnull(int argc, char *argv[],
7167
const bool exit_failure,
7168
char **agent_directory,
7169
char **helper_directory,
7173
size_t *mandos_argz_length){
7175
FILE *real_stderr = stderr;
7176
FILE *devnull = fopen("/dev/null", "we");
7177
g_assert_nonnull(devnull);
7180
const bool ret = parse_arguments(argc, argv, exit_failure,
7182
helper_directory, user, group,
7183
mandos_argz, mandos_argz_length);
7184
const error_t saved_errno = errno;
7186
stderr = real_stderr;
7187
g_assert_cmpint(fclose(devnull), ==, 0);
7189
errno = saved_errno;
7194
static void test_parse_arguments_invalid(__attribute__((unused))
7195
test_fixture *fixture,
7196
__attribute__((unused))
7197
gconstpointer user_data){
7200
strdup("--invalid"),
7202
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7204
char *agent_directory = NULL;
7205
char *helper_directory = NULL;
7208
char *mandos_argz = NULL;
7209
size_t mandos_argz_length = 0;
7211
g_assert_false(parse_arguments_devnull(argc, argv, false,
7213
&helper_directory, &user,
7214
&group, &mandos_argz,
7215
&mandos_argz_length));
7217
g_assert_true(errno == EINVAL);
7218
g_assert_null(agent_directory);
7219
g_assert_null(helper_directory);
7220
g_assert_null(mandos_argz);
7221
g_assert_true(mandos_argz_length == 0);
7223
for(char **arg = argv; *arg != NULL; arg++){
7228
static void test_parse_arguments_long_dir(__attribute__((unused))
7229
test_fixture *fixture,
7230
__attribute__((unused))
7231
gconstpointer user_data){
7234
strdup("--agent-directory"),
7237
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7239
__attribute__((cleanup(cleanup_string)))
7240
char *agent_directory = NULL;
7241
char *helper_directory = NULL;
7244
__attribute__((cleanup(cleanup_string)))
7245
char *mandos_argz = NULL;
7246
size_t mandos_argz_length = 0;
7248
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7249
&helper_directory, &user, &group,
7250
&mandos_argz, &mandos_argz_length));
7252
g_assert_cmpstr(agent_directory, ==, "/tmp");
7253
g_assert_null(helper_directory);
7254
g_assert_true(user == 0);
7255
g_assert_true(group == 0);
7256
g_assert_null(mandos_argz);
7257
g_assert_true(mandos_argz_length == 0);
7259
for(char **arg = argv; *arg != NULL; arg++){
7264
static void test_parse_arguments_short_dir(__attribute__((unused))
7265
test_fixture *fixture,
7266
__attribute__((unused))
7267
gconstpointer user_data){
7273
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7275
__attribute__((cleanup(cleanup_string)))
7276
char *agent_directory = NULL;
7277
char *helper_directory = NULL;
7280
__attribute__((cleanup(cleanup_string)))
7281
char *mandos_argz = NULL;
7282
size_t mandos_argz_length = 0;
7284
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7285
&helper_directory, &user, &group,
7286
&mandos_argz, &mandos_argz_length));
7288
g_assert_cmpstr(agent_directory, ==, "/tmp");
7289
g_assert_null(helper_directory);
7290
g_assert_true(user == 0);
7291
g_assert_true(group == 0);
7292
g_assert_null(mandos_argz);
7293
g_assert_true(mandos_argz_length == 0);
7295
for(char **arg = argv; *arg != NULL; arg++){
7301
void test_parse_arguments_helper_directory(__attribute__((unused))
7302
test_fixture *fixture,
7303
__attribute__((unused))
7304
gconstpointer user_data){
7307
strdup("--helper-directory"),
7310
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7312
char *agent_directory = NULL;
7313
__attribute__((cleanup(cleanup_string)))
7314
char *helper_directory = NULL;
7317
__attribute__((cleanup(cleanup_string)))
7318
char *mandos_argz = NULL;
7319
size_t mandos_argz_length = 0;
7321
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7322
&helper_directory, &user, &group,
7323
&mandos_argz, &mandos_argz_length));
7325
g_assert_cmpstr(helper_directory, ==, "/tmp");
7326
g_assert_null(agent_directory);
7327
g_assert_true(user == 0);
7328
g_assert_true(group == 0);
7329
g_assert_null(mandos_argz);
7330
g_assert_true(mandos_argz_length == 0);
7332
for(char **arg = argv; *arg != NULL; arg++){
7338
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7339
test_fixture *fixture,
7340
__attribute__((unused))
7341
gconstpointer user_data){
7344
strdup("--plugin-helper-dir"),
7347
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7349
char *agent_directory = NULL;
7350
__attribute__((cleanup(cleanup_string)))
7351
char *helper_directory = NULL;
7354
__attribute__((cleanup(cleanup_string)))
7355
char *mandos_argz = NULL;
7356
size_t mandos_argz_length = 0;
7358
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7359
&helper_directory, &user, &group,
7360
&mandos_argz, &mandos_argz_length));
7362
g_assert_cmpstr(helper_directory, ==, "/tmp");
7363
g_assert_null(agent_directory);
7364
g_assert_true(user == 0);
7365
g_assert_true(group == 0);
7366
g_assert_null(mandos_argz);
7367
g_assert_true(mandos_argz_length == 0);
7369
for(char **arg = argv; *arg != NULL; arg++){
7374
static void test_parse_arguments_user(__attribute__((unused))
7375
test_fixture *fixture,
7376
__attribute__((unused))
7377
gconstpointer user_data){
7383
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7385
char *agent_directory = NULL;
7386
__attribute__((cleanup(cleanup_string)))
7387
char *helper_directory = NULL;
7390
__attribute__((cleanup(cleanup_string)))
7391
char *mandos_argz = NULL;
7392
size_t mandos_argz_length = 0;
7394
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7395
&helper_directory, &user, &group,
7396
&mandos_argz, &mandos_argz_length));
7398
g_assert_null(helper_directory);
7399
g_assert_null(agent_directory);
7400
g_assert_cmpuint((unsigned int)user, ==, 1000);
7401
g_assert_true(group == 0);
7402
g_assert_null(mandos_argz);
7403
g_assert_true(mandos_argz_length == 0);
7405
for(char **arg = argv; *arg != NULL; arg++){
7410
static void test_parse_arguments_user_invalid(__attribute__((unused))
7411
test_fixture *fixture,
7412
__attribute__((unused))
7420
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7422
char *agent_directory = NULL;
7423
__attribute__((cleanup(cleanup_string)))
7424
char *helper_directory = NULL;
7427
__attribute__((cleanup(cleanup_string)))
7428
char *mandos_argz = NULL;
7429
size_t mandos_argz_length = 0;
7431
g_assert_false(parse_arguments_devnull(argc, argv, false,
7433
&helper_directory, &user,
7434
&group, &mandos_argz,
7435
&mandos_argz_length));
7437
g_assert_null(helper_directory);
7438
g_assert_null(agent_directory);
7439
g_assert_cmpuint((unsigned int)user, ==, 0);
7440
g_assert_true(group == 0);
7441
g_assert_null(mandos_argz);
7442
g_assert_true(mandos_argz_length == 0);
7444
for(char **arg = argv; *arg != NULL; arg++){
7450
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7451
test_fixture *fixture,
7452
__attribute__((unused))
7453
gconstpointer user_data){
7459
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7461
char *agent_directory = NULL;
7462
__attribute__((cleanup(cleanup_string)))
7463
char *helper_directory = NULL;
7466
__attribute__((cleanup(cleanup_string)))
7467
char *mandos_argz = NULL;
7468
size_t mandos_argz_length = 0;
7470
g_assert_false(parse_arguments_devnull(argc, argv, false,
7472
&helper_directory, &user,
7473
&group, &mandos_argz,
7474
&mandos_argz_length));
7476
g_assert_null(helper_directory);
7477
g_assert_null(agent_directory);
7478
g_assert_cmpuint((unsigned int)user, ==, 0);
7479
g_assert_true(group == 0);
7480
g_assert_null(mandos_argz);
7481
g_assert_true(mandos_argz_length == 0);
7483
for(char **arg = argv; *arg != NULL; arg++){
7488
static void test_parse_arguments_group(__attribute__((unused))
7489
test_fixture *fixture,
7490
__attribute__((unused))
7491
gconstpointer user_data){
7497
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7499
char *agent_directory = NULL;
7500
__attribute__((cleanup(cleanup_string)))
7501
char *helper_directory = NULL;
7504
__attribute__((cleanup(cleanup_string)))
7505
char *mandos_argz = NULL;
7506
size_t mandos_argz_length = 0;
7508
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7509
&helper_directory, &user, &group,
7510
&mandos_argz, &mandos_argz_length));
7512
g_assert_null(helper_directory);
7513
g_assert_null(agent_directory);
7514
g_assert_true(user == 0);
7515
g_assert_cmpuint((unsigned int)group, ==, 1000);
7516
g_assert_null(mandos_argz);
7517
g_assert_true(mandos_argz_length == 0);
7519
for(char **arg = argv; *arg != NULL; arg++){
7524
static void test_parse_arguments_group_invalid(__attribute__((unused))
7525
test_fixture *fixture,
7526
__attribute__((unused))
7534
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7536
char *agent_directory = NULL;
7537
__attribute__((cleanup(cleanup_string)))
7538
char *helper_directory = NULL;
7541
__attribute__((cleanup(cleanup_string)))
7542
char *mandos_argz = NULL;
7543
size_t mandos_argz_length = 0;
7545
g_assert_false(parse_arguments_devnull(argc, argv, false,
7547
&helper_directory, &user,
7548
&group, &mandos_argz,
7549
&mandos_argz_length));
7551
g_assert_null(helper_directory);
7552
g_assert_null(agent_directory);
7553
g_assert_true(user == 0);
7554
g_assert_true(group == 0);
7555
g_assert_null(mandos_argz);
7556
g_assert_true(mandos_argz_length == 0);
7558
for(char **arg = argv; *arg != NULL; arg++){
7564
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7565
test_fixture *fixture,
7566
__attribute__((unused))
7567
gconstpointer user_data){
7573
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7575
char *agent_directory = NULL;
7576
__attribute__((cleanup(cleanup_string)))
7577
char *helper_directory = NULL;
7580
__attribute__((cleanup(cleanup_string)))
7581
char *mandos_argz = NULL;
7582
size_t mandos_argz_length = 0;
7584
g_assert_false(parse_arguments_devnull(argc, argv, false,
7586
&helper_directory, &user,
7587
&group, &mandos_argz,
7588
&mandos_argz_length));
7590
g_assert_null(helper_directory);
7591
g_assert_null(agent_directory);
7592
g_assert_cmpuint((unsigned int)group, ==, 0);
7593
g_assert_true(group == 0);
7594
g_assert_null(mandos_argz);
7595
g_assert_true(mandos_argz_length == 0);
7597
for(char **arg = argv; *arg != NULL; arg++){
7602
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7603
test_fixture *fixture,
7604
__attribute__((unused))
7609
strdup("mandos-client"),
7611
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7613
__attribute__((cleanup(cleanup_string)))
7614
char *agent_directory = NULL;
7615
__attribute__((cleanup(cleanup_string)))
7616
char *helper_directory = NULL;
7619
__attribute__((cleanup(cleanup_string)))
7620
char *mandos_argz = NULL;
7621
size_t mandos_argz_length = 0;
7623
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7624
&helper_directory, &user, &group,
7625
&mandos_argz, &mandos_argz_length));
7627
g_assert_null(agent_directory);
7628
g_assert_null(helper_directory);
7629
g_assert_true(user == 0);
7630
g_assert_true(group == 0);
7631
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7632
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7633
mandos_argz_length),
7636
for(char **arg = argv; *arg != NULL; arg++){
7641
static void test_parse_arguments_mandos_args(__attribute__((unused))
7642
test_fixture *fixture,
7643
__attribute__((unused))
7644
gconstpointer user_data){
7647
strdup("mandos-client"),
7652
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7654
__attribute__((cleanup(cleanup_string)))
7655
char *agent_directory = NULL;
7656
__attribute__((cleanup(cleanup_string)))
7657
char *helper_directory = NULL;
7660
__attribute__((cleanup(cleanup_string)))
7661
char *mandos_argz = NULL;
7662
size_t mandos_argz_length = 0;
7664
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7665
&helper_directory, &user, &group,
7666
&mandos_argz, &mandos_argz_length));
7668
g_assert_null(agent_directory);
7669
g_assert_null(helper_directory);
7670
g_assert_true(user == 0);
7671
g_assert_true(group == 0);
7672
char *marg = mandos_argz;
7673
g_assert_cmpstr(marg, ==, "mandos-client");
7674
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7675
g_assert_cmpstr(marg, ==, "one");
7676
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7677
g_assert_cmpstr(marg, ==, "two");
7678
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7679
g_assert_cmpstr(marg, ==, "three");
7680
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7681
mandos_argz_length),
7684
for(char **arg = argv; *arg != NULL; arg++){
7689
static void test_parse_arguments_all_args(__attribute__((unused))
7690
test_fixture *fixture,
7691
__attribute__((unused))
7692
gconstpointer user_data){
7695
strdup("--agent-directory"),
7697
strdup("--helper-directory"),
7703
strdup("mandos-client"),
7708
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7710
__attribute__((cleanup(cleanup_string)))
7711
char *agent_directory = NULL;
7712
__attribute__((cleanup(cleanup_string)))
7713
char *helper_directory = NULL;
7716
__attribute__((cleanup(cleanup_string)))
7717
char *mandos_argz = NULL;
7718
size_t mandos_argz_length = 0;
7720
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7721
&helper_directory, &user, &group,
7722
&mandos_argz, &mandos_argz_length));
7724
g_assert_cmpstr(agent_directory, ==, "/tmp");
7725
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7726
g_assert_true(user == 1);
7727
g_assert_true(group == 2);
7728
char *marg = mandos_argz;
7729
g_assert_cmpstr(marg, ==, "mandos-client");
7730
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7731
g_assert_cmpstr(marg, ==, "one");
7732
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7733
g_assert_cmpstr(marg, ==, "two");
7734
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7735
g_assert_cmpstr(marg, ==, "three");
7736
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7737
mandos_argz_length),
7740
for(char **arg = argv; *arg != NULL; arg++){
7745
static void test_parse_arguments_mixed(__attribute__((unused))
7746
test_fixture *fixture,
7747
__attribute__((unused))
7748
gconstpointer user_data){
7751
strdup("mandos-client"),
7755
strdup("--agent-directory"),
7759
strdup("--helper-directory=/var/tmp"),
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 == 0);
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
/* End of tests section */
7800
/* Test boilerplate section; New tests should be added to the test
7801
suite definition here, in the "run_tests" function.
7803
Finally, this section also contains the should_only_run_tests()
7804
function used by main() for deciding if tests should be run or to
7807
__attribute__((cold))
7808
static bool run_tests(int argc, char *argv[]){
7809
g_test_init(&argc, &argv, NULL);
7811
/* A macro to add a test with no setup or teardown functions */
7812
#define test_add(testpath, testfunc) \
7814
g_test_add((testpath), test_fixture, NULL, NULL, \
7815
(testfunc), NULL); \
7818
/* Test the signal-related functions first, since some other tests
7819
depend on these functions in their setups and teardowns */
7820
test_add("/signal-handling/setup", test_setup_signal_handler);
7821
test_add("/signal-handling/restore", test_restore_signal_handler);
7822
test_add("/signal-handling/block", test_block_sigchld);
7823
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7825
/* Regular non-signal-related tests; these use no setups or
7827
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7828
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7829
test_add("/parse_arguments/long-dir",
7830
test_parse_arguments_long_dir);
7831
test_add("/parse_arguments/short-dir",
7832
test_parse_arguments_short_dir);
7833
test_add("/parse_arguments/helper-directory",
7834
test_parse_arguments_helper_directory);
7835
test_add("/parse_arguments/plugin-helper-dir",
7836
test_parse_arguments_plugin_helper_dir);
7837
test_add("/parse_arguments/user", test_parse_arguments_user);
7838
test_add("/parse_arguments/user-invalid",
7839
test_parse_arguments_user_invalid);
7840
test_add("/parse_arguments/user-zero-invalid",
7841
test_parse_arguments_user_zero_invalid);
7842
test_add("/parse_arguments/group", test_parse_arguments_group);
7843
test_add("/parse_arguments/group-invalid",
7844
test_parse_arguments_group_invalid);
7845
test_add("/parse_arguments/group-zero-invalid",
7846
test_parse_arguments_group_zero_invalid);
7847
test_add("/parse_arguments/mandos-noargs",
7848
test_parse_arguments_mandos_noargs);
7849
test_add("/parse_arguments/mandos-args",
7850
test_parse_arguments_mandos_args);
7851
test_add("/parse_arguments/all-args",
7852
test_parse_arguments_all_args);
7853
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7854
test_add("/queue/create", test_create_queue);
7855
test_add("/queue/add", test_add_to_queue);
7856
test_add("/queue/has_question/empty",
7857
test_queue_has_question_empty);
7858
test_add("/queue/has_question/false",
7859
test_queue_has_question_false);
7860
test_add("/queue/has_question/true", test_queue_has_question_true);
7861
test_add("/queue/has_question/false2",
7862
test_queue_has_question_false2);
7863
test_add("/queue/has_question/true2",
7864
test_queue_has_question_true2);
7865
test_add("/buffer/cleanup", test_cleanup_buffer);
7866
test_add("/string_set/net-set-contains-nothing",
7867
test_string_set_new_set_contains_nothing);
7868
test_add("/string_set/with-added-string-contains-it",
7869
test_string_set_with_added_string_contains_it);
7870
test_add("/string_set/cleared-does-not-contain-string",
7871
test_string_set_cleared_does_not_contain_str);
7872
test_add("/string_set/swap/one-with-empty",
7873
test_string_set_swap_one_with_empty);
7874
test_add("/string_set/swap/empty-with-one",
7875
test_string_set_swap_empty_with_one);
7876
test_add("/string_set/swap/one-with-one",
7877
test_string_set_swap_one_with_one);
7879
/* A macro to add a test using the setup and teardown functions */
7880
#define test_add_st(path, func) \
7882
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7886
/* Signal-related tests; these use setups and teardowns which
7887
establish, during each test run, a signal handler for, and a
7888
signal mask blocking, the SIGCHLD signal, just like main() */
7889
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7890
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7891
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7892
test_add_st("/run_queue/zeroes-next-run",
7893
test_run_queue_zeroes_next_run);
7894
test_add_st("/run_queue/clears-cancelled_filenames",
7895
test_run_queue_clears_cancelled_filenames);
7896
test_add_st("/run_queue/skips-cancelled-filenames",
7897
test_run_queue_skips_cancelled_filenames);
7898
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7899
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7900
test_add_st("/run_queue/two-tasks/quit",
7901
test_run_queue_two_tasks_quit);
7902
test_add_st("/run_queue/two-tasks-cleanup",
7903
test_run_queue_two_tasks_cleanup);
7904
test_add_st("/task-creators/start_mandos_client",
7905
test_start_mandos_client);
7906
test_add_st("/task-creators/start_mandos_client/execv",
7907
test_start_mandos_client_execv);
7908
test_add_st("/task-creators/start_mandos_client/suid/euid",
7909
test_start_mandos_client_suid_euid);
7910
test_add_st("/task-creators/start_mandos_client/suid/egid",
7911
test_start_mandos_client_suid_egid);
7912
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7913
test_start_mandos_client_suid_ruid);
7914
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7915
test_start_mandos_client_suid_rgid);
7916
test_add_st("/task-creators/start_mandos_client/read",
7917
test_start_mandos_client_read);
7918
test_add_st("/task-creators/start_mandos_client/helper-directory",
7919
test_start_mandos_client_helper_directory);
7920
test_add_st("/task-creators/start_mandos_client/sigmask",
7921
test_start_mandos_client_sigmask);
7922
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7923
test_wait_for_mandos_client_exit_badpid);
7924
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7925
test_wait_for_mandos_client_exit_noexit);
7926
test_add_st("/task/wait_for_mandos_client_exit/success",
7927
test_wait_for_mandos_client_exit_success);
7928
test_add_st("/task/wait_for_mandos_client_exit/failure",
7929
test_wait_for_mandos_client_exit_failure);
7930
test_add_st("/task/wait_for_mandos_client_exit/killed",
7931
test_wait_for_mandos_client_exit_killed);
7932
test_add_st("/task/read_mandos_client_output/readerror",
7933
test_read_mandos_client_output_readerror);
7934
test_add_st("/task/read_mandos_client_output/nodata",
7935
test_read_mandos_client_output_nodata);
7936
test_add_st("/task/read_mandos_client_output/eof",
7937
test_read_mandos_client_output_eof);
7938
test_add_st("/task/read_mandos_client_output/once",
7939
test_read_mandos_client_output_once);
7940
test_add_st("/task/read_mandos_client_output/malloc",
7941
test_read_mandos_client_output_malloc);
7942
test_add_st("/task/read_mandos_client_output/append",
7943
test_read_mandos_client_output_append);
7944
test_add_st("/task-creators/add_inotify_dir_watch",
7945
test_add_inotify_dir_watch);
7946
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7947
test_add_inotify_dir_watch_fail);
7948
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7949
test_add_inotify_dir_watch_nondir);
7950
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7951
test_add_inotify_dir_watch_EAGAIN);
7952
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7953
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7954
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7955
test_add_inotify_dir_watch_IN_MOVED_TO);
7956
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7957
test_add_inotify_dir_watch_IN_MOVED_FROM);
7958
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7959
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7960
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7961
test_add_inotify_dir_watch_IN_DELETE);
7962
test_add_st("/task/read_inotify_event/readerror",
7963
test_read_inotify_event_readerror);
7964
test_add_st("/task/read_inotify_event/bad-epoll",
7965
test_read_inotify_event_bad_epoll);
7966
test_add_st("/task/read_inotify_event/nodata",
7967
test_read_inotify_event_nodata);
7968
test_add_st("/task/read_inotify_event/eof",
7969
test_read_inotify_event_eof);
7970
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7971
test_read_inotify_event_IN_CLOSE_WRITE);
7972
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7973
test_read_inotify_event_IN_MOVED_TO);
7974
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7975
test_read_inotify_event_IN_MOVED_FROM);
7976
test_add_st("/task/read_inotify_event/IN_DELETE",
7977
test_read_inotify_event_IN_DELETE);
7978
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7979
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7980
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7981
test_read_inotify_event_IN_MOVED_TO_badname);
7982
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7983
test_read_inotify_event_IN_MOVED_FROM_badname);
7984
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7985
test_read_inotify_event_IN_DELETE_badname);
7986
test_add_st("/task/open_and_parse_question/ENOENT",
7987
test_open_and_parse_question_ENOENT);
7988
test_add_st("/task/open_and_parse_question/EIO",
7989
test_open_and_parse_question_EIO);
7990
test_add_st("/task/open_and_parse_question/parse-error",
7991
test_open_and_parse_question_parse_error);
7992
test_add_st("/task/open_and_parse_question/nosocket",
7993
test_open_and_parse_question_nosocket);
7994
test_add_st("/task/open_and_parse_question/badsocket",
7995
test_open_and_parse_question_badsocket);
7996
test_add_st("/task/open_and_parse_question/nopid",
7997
test_open_and_parse_question_nopid);
7998
test_add_st("/task/open_and_parse_question/badpid",
7999
test_open_and_parse_question_badpid);
8000
test_add_st("/task/open_and_parse_question/noexist_pid",
8001
test_open_and_parse_question_noexist_pid);
8002
test_add_st("/task/open_and_parse_question/no-notafter",
8003
test_open_and_parse_question_no_notafter);
8004
test_add_st("/task/open_and_parse_question/bad-notafter",
8005
test_open_and_parse_question_bad_notafter);
8006
test_add_st("/task/open_and_parse_question/notafter-0",
8007
test_open_and_parse_question_notafter_0);
8008
test_add_st("/task/open_and_parse_question/notafter-1",
8009
test_open_and_parse_question_notafter_1);
8010
test_add_st("/task/open_and_parse_question/notafter-1-1",
8011
test_open_and_parse_question_notafter_1_1);
8012
test_add_st("/task/open_and_parse_question/notafter-1-2",
8013
test_open_and_parse_question_notafter_1_2);
8014
test_add_st("/task/open_and_parse_question/equal-notafter",
8015
test_open_and_parse_question_equal_notafter);
8016
test_add_st("/task/open_and_parse_question/late-notafter",
8017
test_open_and_parse_question_late_notafter);
8018
test_add_st("/task/cancel_old_question/0-1-2",
8019
test_cancel_old_question_0_1_2);
8020
test_add_st("/task/cancel_old_question/0-2-1",
8021
test_cancel_old_question_0_2_1);
8022
test_add_st("/task/cancel_old_question/1-2-3",
8023
test_cancel_old_question_1_2_3);
8024
test_add_st("/task/cancel_old_question/1-3-2",
8025
test_cancel_old_question_1_3_2);
8026
test_add_st("/task/cancel_old_question/2-1-3",
8027
test_cancel_old_question_2_1_3);
8028
test_add_st("/task/cancel_old_question/2-3-1",
8029
test_cancel_old_question_2_3_1);
8030
test_add_st("/task/cancel_old_question/3-1-2",
8031
test_cancel_old_question_3_1_2);
8032
test_add_st("/task/cancel_old_question/3-2-1",
8033
test_cancel_old_question_3_2_1);
8034
test_add_st("/task/connect_question_socket/name-too-long",
8035
test_connect_question_socket_name_too_long);
8036
test_add_st("/task/connect_question_socket/connect-fail",
8037
test_connect_question_socket_connect_fail);
8038
test_add_st("/task/connect_question_socket/bad-epoll",
8039
test_connect_question_socket_bad_epoll);
8040
test_add_st("/task/connect_question_socket/usable",
8041
test_connect_question_socket_usable);
8042
test_add_st("/task/send_password_to_socket/client-not-exited",
8043
test_send_password_to_socket_client_not_exited);
8044
test_add_st("/task/send_password_to_socket/password-not-read",
8045
test_send_password_to_socket_password_not_read);
8046
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8047
test_send_password_to_socket_EMSGSIZE);
8048
test_add_st("/task/send_password_to_socket/retry",
8049
test_send_password_to_socket_retry);
8050
test_add_st("/task/send_password_to_socket/bad-epoll",
8051
test_send_password_to_socket_bad_epoll);
8052
test_add_st("/task/send_password_to_socket/null-password",
8053
test_send_password_to_socket_null_password);
8054
test_add_st("/task/send_password_to_socket/empty-password",
8055
test_send_password_to_socket_empty_password);
8056
test_add_st("/task/send_password_to_socket/empty-str-password",
8057
test_send_password_to_socket_empty_str_pass);
8058
test_add_st("/task/send_password_to_socket/text-password",
8059
test_send_password_to_socket_text_password);
8060
test_add_st("/task/send_password_to_socket/binary-password",
8061
test_send_password_to_socket_binary_password);
8062
test_add_st("/task/send_password_to_socket/nuls-in-password",
8063
test_send_password_to_socket_nuls_in_password);
8064
test_add_st("/task-creators/add_existing_questions/ENOENT",
8065
test_add_existing_questions_ENOENT);
8066
test_add_st("/task-creators/add_existing_questions/no-questions",
8067
test_add_existing_questions_no_questions);
8068
test_add_st("/task-creators/add_existing_questions/one-question",
8069
test_add_existing_questions_one_question);
8070
test_add_st("/task-creators/add_existing_questions/two-questions",
8071
test_add_existing_questions_two_questions);
8072
test_add_st("/task-creators/add_existing_questions/non-questions",
8073
test_add_existing_questions_non_questions);
8074
test_add_st("/task-creators/add_existing_questions/both-types",
8075
test_add_existing_questions_both_types);
8077
return g_test_run() == 0;
8080
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8081
GOptionContext *context = g_option_context_new("");
8083
g_option_context_set_help_enabled(context, FALSE);
8084
g_option_context_set_ignore_unknown_options(context, TRUE);
8086
gboolean run_tests = FALSE;
8087
GOptionEntry entries[] = {
8088
{ "test", 0, 0, G_OPTION_ARG_NONE,
8089
&run_tests, "Run tests", NULL },
8092
g_option_context_add_main_entries(context, entries, NULL);
8094
GError *error = NULL;
8096
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8097
g_option_context_free(context);
8098
g_error("Failed to parse options: %s", error->message);
8101
g_option_context_free(context);
8102
return run_tests != FALSE;