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, ENOTDIR,
52
EEXIST, ECHILD, EPERM, ENOMEM,
53
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <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_NOCTTY,
87
O_RDONLY, O_NOFOLLOW */
88
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
90
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
91
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
92
IN_CLOEXEC, inotify_add_watch(),
93
IN_CLOSE_WRITE, IN_MOVED_TO,
94
IN_MOVED_FROM, IN_DELETE,
95
IN_EXCL_UNLINK, IN_ONLYDIR,
96
struct inotify_event */
97
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
98
#include <stdio.h> /* asprintf(), FILE, fopen(),
99
getline(), sscanf(), feof(),
100
ferror(), fclose(), stderr,
101
rename(), fdopen(), fprintf(),
103
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
GError, g_key_file_load_from_file(),
105
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
106
g_key_file_get_string(), guint64,
107
g_key_file_get_uint64(),
108
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
109
g_assert_true(), g_assert_nonnull(),
110
g_assert_null(), g_assert_false(),
111
g_assert_cmpint(), g_assert_cmpuint(),
112
g_test_skip(), g_assert_cmpstr(),
113
g_test_init(), g_test_add(), g_test_run(),
114
GOptionContext, g_option_context_new(),
115
g_option_context_set_help_enabled(), FALSE,
116
g_option_context_set_ignore_unknown_options(),
117
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
118
g_option_context_add_main_entries(),
119
g_option_context_parse(),
120
g_option_context_free(), g_error() */
121
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
122
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
123
SOCK_DGRAM, SOCK_NONBLOCK,
124
SOCK_CLOEXEC, connect(),
125
struct sockaddr, socklen_t,
126
shutdown(), SHUT_RD, send(),
127
MSG_NOSIGNAL, bind(), recv(),
129
#include <glob.h> /* globfree(), glob_t, glob(),
130
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
131
GLOB_ABORTED, GLOB_NOMATCH,
134
/* End of includes */
136
/* Start of declarations of private types and functions */
138
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
139
typedef uintmax_t mono_microsecs;
141
/* "task_queue" - A queue of tasks to be run */
143
struct task_struct *tasks; /* Tasks in this queue */
144
size_t length; /* Number of tasks */
145
/* Memory allocated for "tasks", in bytes */
147
/* Time when this queue should be run, at the latest */
148
mono_microsecs next_run;
149
} __attribute__((designated_init)) task_queue;
151
/* "func_type" - A function type for task functions
153
I.e. functions for the code which runs when a task is run, all have
155
typedef void (task_func) (const struct task_struct,
157
__attribute__((nonnull));
159
/* "buffer" - A data buffer for a growing array of bytes
161
Used for the "password" variable */
166
} __attribute__((designated_init)) buffer;
168
/* "string_set" - A set type which can contain strings
170
Used by the "cancelled_filenames" variable */
172
char *argz; /* Do not access these except in */
173
size_t argz_len; /* the string_set_* functions */
174
} __attribute__((designated_init)) string_set;
176
/* "task_context" - local variables for tasks
178
This data structure distinguishes between different tasks which are
179
using the same function. This data structure is passed to every
180
task function when each task is run.
182
Note that not every task uses every struct member. */
183
typedef struct task_struct {
184
task_func *const func; /* The function run by this task */
185
char *const question_filename; /* The question file */
186
const pid_t pid; /* Mandos client process ID */
187
const int epoll_fd; /* The epoll set file descriptor */
188
bool *const quit_now; /* Set to true on fatal errors */
189
const int fd; /* General purpose file descriptor */
190
bool *const mandos_client_exited; /* Set true when client exits */
191
buffer *const password; /* As read from client process */
192
bool *const password_is_read; /* "password" is done growing */
193
char *filename; /* General purpose file name */
194
/* A set of strings of all the file names of questions which have
195
been cancelled for any reason; tasks pertaining to these question
196
files should not be run */
197
string_set *const cancelled_filenames;
198
const mono_microsecs notafter; /* "NotAfter" from question file */
199
/* Updated before each queue run; is compared with queue.next_run */
200
const mono_microsecs *const current_time;
201
} __attribute__((designated_init)) task_context;
203
/* Declare all our functions here so we can define them in any order
204
below. Note: test functions are *not* declared here, they are
205
declared in the test section. */
206
__attribute__((warn_unused_result))
207
static bool should_only_run_tests(int *, char **[]);
208
__attribute__((warn_unused_result, cold))
209
static bool run_tests(int, char *[]);
210
static void handle_sigchld(__attribute__((unused)) int sig){}
211
__attribute__((warn_unused_result, malloc))
212
task_queue *create_queue(void);
213
__attribute__((nonnull, warn_unused_result))
214
bool add_to_queue(task_queue *const, const task_context);
215
__attribute__((nonnull))
216
void cleanup_task(const task_context *const);
217
__attribute__((nonnull))
218
void cleanup_queue(task_queue *const *const);
219
__attribute__((pure, nonnull, warn_unused_result))
220
bool queue_has_question(const task_queue *const);
221
__attribute__((nonnull))
222
void cleanup_close(const int *const);
223
__attribute__((nonnull))
224
void cleanup_string(char *const *const);
225
__attribute__((nonnull))
226
void cleanup_buffer(buffer *const);
227
__attribute__((pure, nonnull, warn_unused_result))
228
bool string_set_contains(const string_set, const char *const);
229
__attribute__((nonnull, warn_unused_result))
230
bool string_set_add(string_set *const, const char *const);
231
__attribute__((nonnull))
232
void string_set_clear(string_set *);
233
void string_set_swap(string_set *const, string_set *const);
234
__attribute__((nonnull, warn_unused_result))
235
bool start_mandos_client(task_queue *const, const int, bool *const,
236
bool *const, buffer *const, bool *const,
237
const struct sigaction *const,
238
const sigset_t, const char *const,
239
const uid_t, const gid_t,
240
const char *const *const);
241
__attribute__((nonnull))
242
task_func wait_for_mandos_client_exit;
243
__attribute__((nonnull))
244
task_func read_mandos_client_output;
245
__attribute__((warn_unused_result))
246
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
247
buffer *const, const char *const,
248
string_set *, const mono_microsecs *const,
249
bool *const, bool *const);
250
__attribute__((nonnull))
251
task_func read_inotify_event;
252
__attribute__((nonnull))
253
task_func open_and_parse_question;
254
__attribute__((nonnull))
255
task_func cancel_old_question;
256
__attribute__((nonnull))
257
task_func connect_question_socket;
258
__attribute__((nonnull))
259
task_func send_password_to_socket;
260
__attribute__((warn_unused_result))
261
bool add_existing_questions(task_queue *const, const int,
262
buffer *const, string_set *,
263
const mono_microsecs *const,
264
bool *const, bool *const,
266
__attribute__((nonnull, warn_unused_result))
267
bool wait_for_event(const int, const mono_microsecs,
268
const mono_microsecs);
269
bool run_queue(task_queue **const, string_set *const, bool *const);
270
bool clear_all_fds_from_epoll_set(const int);
271
mono_microsecs get_current_time(void);
272
__attribute__((nonnull, warn_unused_result))
273
bool setup_signal_handler(struct sigaction *const);
274
__attribute__((nonnull))
275
bool restore_signal_handler(const struct sigaction *const);
276
__attribute__((nonnull, warn_unused_result))
277
bool block_sigchld(sigset_t *const);
278
__attribute__((nonnull))
279
bool restore_sigmask(const sigset_t *const);
280
__attribute__((nonnull))
281
bool parse_arguments(int, char *[], const bool, char **, char **,
282
uid_t *const , gid_t *const, char **, size_t *);
284
/* End of declarations of private types and functions */
286
/* Start of "main" section; this section LACKS TESTS!
288
Code here should be as simple as possible. */
290
/* These are required to be global by Argp */
291
const char *argp_program_version = "password-agent " VERSION;
292
const char *argp_program_bug_address = "<mandos@recompile.se>";
294
int main(int argc, char *argv[]){
296
/* If the --test option is passed, skip all normal operations and
297
instead only run the run_tests() function, which also does all
298
its own option parsing, so we don't have to do anything here. */
299
if(should_only_run_tests(&argc, &argv)){
300
if(run_tests(argc, argv)){
301
return EXIT_SUCCESS; /* All tests successful */
303
return EXIT_FAILURE; /* Some test(s) failed */
306
__attribute__((cleanup(cleanup_string)))
307
char *agent_directory = NULL;
309
__attribute__((cleanup(cleanup_string)))
310
char *helper_directory = NULL;
315
__attribute__((cleanup(cleanup_string)))
316
char *mandos_argz = NULL;
317
size_t mandos_argz_length = 0;
319
if(not parse_arguments(argc, argv, true, &agent_directory,
320
&helper_directory, &user, &group,
321
&mandos_argz, &mandos_argz_length)){
322
/* This should never happen, since "true" is passed as the third
323
argument to parse_arguments() above, which should make
324
argp_parse() call exit() if any parsing error occurs. */
325
error(EX_USAGE, errno, "Failed to parse arguments");
328
const char default_agent_directory[] = "/run/systemd/ask-password";
329
const char default_helper_directory[]
330
= "/lib/mandos/plugin-helpers";
331
const char *const default_argv[]
332
= {"/lib/mandos/plugins.d/mandos-client", NULL };
334
/* Set variables to default values if unset */
335
if(agent_directory == NULL){
336
agent_directory = strdup(default_agent_directory);
337
if(agent_directory == NULL){
338
error(EX_OSERR, errno, "Failed strdup()");
341
if(helper_directory == NULL){
342
helper_directory = strdup(default_helper_directory);
343
if(helper_directory == NULL){
344
error(EX_OSERR, errno, "Failed strdup()");
348
user = 65534; /* nobody */
351
group = 65534; /* nogroup */
353
/* If parse_opt did not create an argz vector, create one with
355
if(mandos_argz == NULL){
357
#pragma GCC diagnostic push
358
/* argz_create() takes a non-const argv for some unknown reason -
359
argz_create() isn't modifying the strings, just copying them.
360
Therefore, this cast to non-const should be safe. */
361
#pragma GCC diagnostic ignored "-Wcast-qual"
363
errno = argz_create((char *const *)default_argv, &mandos_argz,
364
&mandos_argz_length);
366
#pragma GCC diagnostic pop
369
error(EX_OSERR, errno, "Failed argz_create()");
372
/* Use argz vector to create a normal argv, usable by execv() */
374
char **mandos_argv = malloc((argz_count(mandos_argz,
376
+ 1) * sizeof(char *));
377
if(mandos_argv == NULL){
378
error_t saved_errno = errno;
380
error(EX_OSERR, saved_errno, "Failed malloc()");
382
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
384
sigset_t orig_sigmask;
385
if(not block_sigchld(&orig_sigmask)){
389
struct sigaction old_sigchld_action;
390
if(not setup_signal_handler(&old_sigchld_action)){
394
mono_microsecs current_time = 0;
396
bool mandos_client_exited = false;
397
bool quit_now = false;
398
__attribute__((cleanup(cleanup_close)))
399
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
401
error(EX_OSERR, errno, "Failed to create epoll set fd");
403
__attribute__((cleanup(cleanup_queue)))
404
task_queue *queue = create_queue();
406
error(EX_OSERR, errno, "Failed to create task queue");
409
__attribute__((cleanup(cleanup_buffer)))
410
buffer password = {};
411
bool password_is_read = false;
413
__attribute__((cleanup(string_set_clear)))
414
string_set cancelled_filenames = {};
416
/* Add tasks to queue */
417
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
418
&quit_now, &password, &password_is_read,
419
&old_sigchld_action, orig_sigmask,
420
helper_directory, user, group,
421
(const char *const *)mandos_argv)){
422
return EX_OSERR; /* Error has already been printed */
424
/* These variables were only for start_mandos_client() and are not
429
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
430
agent_directory, &cancelled_filenames,
431
¤t_time, &mandos_client_exited,
433
switch(errno){ /* Error has already been printed */
443
if(not add_existing_questions(queue, epoll_fd, &password,
444
&cancelled_filenames, ¤t_time,
445
&mandos_client_exited,
446
&password_is_read, agent_directory)){
447
return EXIT_FAILURE; /* Error has already been printed */
452
current_time = get_current_time();
453
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
454
const error_t saved_errno = errno;
455
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
459
current_time = get_current_time();
460
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
461
const error_t saved_errno = errno;
462
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
465
/* When no tasks about questions are left in the queue, break out
466
of the loop (and implicitly exit the program) */
467
} while(queue_has_question(queue));
469
restore_signal_handler(&old_sigchld_action);
470
restore_sigmask(&orig_sigmask);
475
__attribute__((warn_unused_result))
476
mono_microsecs get_current_time(void){
477
struct timespec currtime;
478
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
479
error(0, errno, "Failed to get current time");
482
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
483
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
486
/* End of "main" section */
488
/* Start of regular code section; ALL this code has tests */
490
__attribute__((nonnull))
491
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
492
char **agent_directory, char **helper_directory,
493
uid_t *const user, gid_t *const group,
494
char **mandos_argz, size_t *mandos_argz_length){
496
const struct argp_option options[] = {
497
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
498
.doc="Systemd password agent directory" },
499
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
500
.doc="Mandos Client password helper directory" },
501
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
502
.flags=OPTION_HIDDEN | OPTION_ALIAS },
503
{ .name="user", .key='u', .arg="USERID",
504
.doc="User ID the Mandos Client will use as its unprivileged"
506
{ .name="userid", .key=130, /* From plugin--runner */
507
.flags=OPTION_HIDDEN | OPTION_ALIAS },
508
{ .name="group", .key='g', .arg="GROUPID",
509
.doc="Group ID the Mandos Client will use as its unprivileged"
511
{ .name="groupid", .key=131, /* From plugin--runner */
512
.flags=OPTION_HIDDEN | OPTION_ALIAS },
513
{ .name="test", .key=255, /* See should_only_run_tests() */
514
.doc="Skip normal operation, and only run self-tests. See"
515
" --test --help.", .group=10, },
519
__attribute__((nonnull(3)))
520
error_t parse_opt(int key, char *arg, struct argp_state *state){
523
case 'd': /* --agent-directory */
524
*agent_directory = strdup(arg);
526
case 128: /* --helper-directory */
527
case 129: /* --plugin-helper-dir */
528
*helper_directory = strdup(arg);
530
case 'u': /* --user */
531
case 130: /* --userid */
534
uintmax_t tmp_id = 0;
536
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
537
if(errno != 0 or tmp == arg or *tmp != '\0'
538
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
539
return ARGP_ERR_UNKNOWN;
541
*user = (uid_t)tmp_id;
545
case 'g': /* --group */
546
case 131: /* --groupid */
549
uintmax_t tmp_id = 0;
551
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
552
if(errno != 0 or tmp == arg or *tmp != '\0'
553
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
554
return ARGP_ERR_UNKNOWN;
556
*group = (gid_t)tmp_id;
561
/* Copy arguments into argz vector */
562
return argz_create(state->argv + state->next, mandos_argz,
565
return ARGP_ERR_UNKNOWN;
570
const struct argp argp = {
573
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
574
.doc = "Mandos password agent -- runs Mandos client as a"
575
" systemd password agent",
578
errno = argp_parse(&argp, argc, argv,
579
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
584
__attribute__((nonnull, warn_unused_result))
585
bool block_sigchld(sigset_t *const orig_sigmask){
586
sigset_t sigchld_sigmask;
587
if(sigemptyset(&sigchld_sigmask) < 0){
588
error(0, errno, "Failed to empty signal set");
591
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
592
error(0, errno, "Failed to add SIGCHLD to signal set");
595
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
596
error(0, errno, "Failed to block SIGCHLD signal");
602
__attribute__((nonnull, warn_unused_result, const))
603
bool restore_sigmask(const sigset_t *const orig_sigmask){
604
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
605
error(0, errno, "Failed to restore blocked signals");
611
__attribute__((nonnull, warn_unused_result))
612
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
613
struct sigaction sigchld_action = {
614
.sa_handler=handle_sigchld,
615
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
617
/* Set all signals in "sa_mask" struct member; this makes all
618
signals automatically blocked during signal handler */
619
if(sigfillset(&sigchld_action.sa_mask) != 0){
620
error(0, errno, "Failed to do sigfillset()");
623
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
624
error(0, errno, "Failed to set SIGCHLD signal handler");
630
__attribute__((nonnull, warn_unused_result))
631
bool restore_signal_handler(const struct sigaction *const
633
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
634
error(0, errno, "Failed to restore signal handler");
640
__attribute__((warn_unused_result, malloc))
641
task_queue *create_queue(void){
642
task_queue *queue = malloc(sizeof(task_queue));
646
queue->allocated = 0;
652
__attribute__((nonnull, warn_unused_result))
653
bool add_to_queue(task_queue *const queue, const task_context task){
654
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
655
if(needed_size > (queue->allocated)){
656
task_context *const new_tasks = realloc(queue->tasks,
658
if(new_tasks == NULL){
659
error(0, errno, "Failed to allocate %" PRIuMAX
660
" bytes for queue->tasks", (uintmax_t)needed_size);
663
queue->tasks = new_tasks;
664
queue->allocated = needed_size;
666
/* Using memcpy here is necessary because doing */
667
/* queue->tasks[queue->length++] = task; */
668
/* would violate const-ness of task members */
669
memcpy(&(queue->tasks[queue->length++]), &task,
670
sizeof(task_context));
674
__attribute__((nonnull))
675
void cleanup_task(const task_context *const task){
676
const error_t saved_errno = errno;
677
/* free and close all task data */
678
free(task->question_filename);
679
if(task->filename != task->question_filename){
680
free(task->filename);
683
kill(task->pid, SIGTERM);
691
__attribute__((nonnull))
692
void free_queue(task_queue *const queue){
697
__attribute__((nonnull))
698
void cleanup_queue(task_queue *const *const queue){
702
for(size_t i = 0; i < (*queue)->length; i++){
703
const task_context *const task = ((*queue)->tasks)+i;
709
__attribute__((pure, nonnull, warn_unused_result))
710
bool queue_has_question(const task_queue *const queue){
711
for(size_t i=0; i < queue->length; i++){
712
if(queue->tasks[i].question_filename != NULL){
719
__attribute__((nonnull))
720
void cleanup_close(const int *const fd){
721
const error_t saved_errno = errno;
726
__attribute__((nonnull))
727
void cleanup_string(char *const *const ptr){
731
__attribute__((nonnull))
732
void cleanup_buffer(buffer *buf){
733
if(buf->allocated > 0){
734
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
735
explicit_bzero(buf->data, buf->allocated);
737
memset(buf->data, '\0', buf->allocated);
740
if(buf->data != NULL){
741
if(munlock(buf->data, buf->allocated) != 0){
742
error(0, errno, "Failed to unlock memory of old buffer");
751
__attribute__((pure, nonnull, warn_unused_result))
752
bool string_set_contains(const string_set set, const char *const str){
753
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
754
s = argz_next(set.argz, set.argz_len, s)){
755
if(strcmp(s, str) == 0){
762
__attribute__((nonnull, warn_unused_result))
763
bool string_set_add(string_set *const set, const char *const str){
764
if(string_set_contains(*set, str)){
767
error_t error = argz_add(&set->argz, &set->argz_len, str);
775
__attribute__((nonnull))
776
void string_set_clear(string_set *set){
782
__attribute__((nonnull))
783
void string_set_swap(string_set *const set1, string_set *const set2){
784
/* Swap contents of two string sets */
786
char *const tmp_argz = set1->argz;
787
set1->argz = set2->argz;
788
set2->argz = tmp_argz;
791
const size_t tmp_argz_len = set1->argz_len;
792
set1->argz_len = set2->argz_len;
793
set2->argz_len = tmp_argz_len;
797
__attribute__((nonnull, warn_unused_result))
798
bool start_mandos_client(task_queue *const queue,
800
bool *const mandos_client_exited,
801
bool *const quit_now, buffer *const password,
802
bool *const password_is_read,
803
const struct sigaction *const
804
old_sigchld_action, const sigset_t sigmask,
805
const char *const helper_directory,
806
const uid_t user, const gid_t group,
807
const char *const *const argv){
809
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
810
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
814
const pid_t pid = fork();
816
if(not restore_signal_handler(old_sigchld_action)){
819
if(not restore_sigmask(&sigmask)){
822
if(close(pipefds[0]) != 0){
823
error(0, errno, "Failed to close() parent pipe fd");
826
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
827
error(0, errno, "Failed to dup2() pipe fd to stdout");
830
if(close(pipefds[1]) != 0){
831
error(0, errno, "Failed to close() old child pipe fd");
834
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
835
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
836
" \"%s\", 1)", helper_directory);
839
if(group != 0 and setresgid(group, 0, 0) == -1){
840
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
841
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
844
if(user != 0 and setresuid(user, 0, 0) == -1){
845
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
846
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
850
#pragma GCC diagnostic push
851
/* For historical reasons, the "argv" argument to execv() is not
852
const, but it is safe to override this. */
853
#pragma GCC diagnostic ignored "-Wcast-qual"
855
execv(argv[0], (char **)argv);
857
#pragma GCC diagnostic pop
859
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
864
if(not add_to_queue(queue, (task_context){
865
.func=wait_for_mandos_client_exit,
867
.mandos_client_exited=mandos_client_exited,
870
error(0, errno, "Failed to add wait_for_mandos_client to queue");
875
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
876
&(struct epoll_event)
877
{ .events=EPOLLIN | EPOLLRDHUP });
878
if(ret != 0 and errno != EEXIST){
879
error(0, errno, "Failed to add file descriptor to epoll set");
884
return add_to_queue(queue, (task_context){
885
.func=read_mandos_client_output,
890
.password_is_read=password_is_read,
894
__attribute__((nonnull))
895
void wait_for_mandos_client_exit(const task_context task,
896
task_queue *const queue){
897
const pid_t pid = task.pid;
898
bool *const mandos_client_exited = task.mandos_client_exited;
899
bool *const quit_now = task.quit_now;
902
switch(waitpid(pid, &status, WNOHANG)){
903
case 0: /* Not exited yet */
904
if(not add_to_queue(queue, task)){
905
error(0, errno, "Failed to add myself to queue");
910
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
916
default: /* Has exited */
917
*mandos_client_exited = true;
918
if((not WIFEXITED(status))
919
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
920
error(0, 0, "Mandos client failed or was killed");
926
__attribute__((nonnull))
927
void read_mandos_client_output(const task_context task,
928
task_queue *const queue){
929
buffer *const password = task.password;
930
bool *const quit_now = task.quit_now;
931
bool *const password_is_read = task.password_is_read;
932
const int fd = task.fd;
933
const int epoll_fd = task.epoll_fd;
935
const size_t new_potential_size = (password->length + PIPE_BUF);
936
if(password->allocated < new_potential_size){
937
char *const new_buffer = calloc(new_potential_size, 1);
938
if(new_buffer == NULL){
939
error(0, errno, "Failed to allocate %" PRIuMAX
940
" bytes for password", (uintmax_t)new_potential_size);
945
if(mlock(new_buffer, new_potential_size) != 0){
946
/* Warn but do not treat as fatal error */
947
if(errno != EPERM and errno != ENOMEM){
948
error(0, errno, "Failed to lock memory for password");
951
if(password->length > 0){
952
memcpy(new_buffer, password->data, password->length);
953
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
954
explicit_bzero(password->data, password->allocated);
956
memset(password->data, '\0', password->allocated);
959
if(password->data != NULL){
960
if(munlock(password->data, password->allocated) != 0){
961
error(0, errno, "Failed to unlock memory of old buffer");
963
free(password->data);
965
password->data = new_buffer;
966
password->allocated = new_potential_size;
969
const ssize_t read_length = read(fd, password->data
970
+ password->length, PIPE_BUF);
972
if(read_length == 0){ /* EOF */
973
*password_is_read = true;
977
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
978
error(0, errno, "Failed to read password from Mandos client");
983
if(read_length > 0){ /* Data has been read */
984
password->length += (size_t)read_length;
987
/* Either data was read, or EAGAIN was indicated, meaning no data
990
/* Re-add the fd to the epoll set */
991
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
992
&(struct epoll_event)
993
{ .events=EPOLLIN | EPOLLRDHUP });
994
if(ret != 0 and errno != EEXIST){
995
error(0, errno, "Failed to re-add file descriptor to epoll set");
1001
/* Re-add myself to the queue */
1002
if(not add_to_queue(queue, task)){
1003
error(0, errno, "Failed to add myself to queue");
1009
__attribute__((nonnull, warn_unused_result))
1010
bool add_inotify_dir_watch(task_queue *const queue,
1011
const int epoll_fd, bool *const quit_now,
1012
buffer *const password,
1013
const char *const dir,
1014
string_set *cancelled_filenames,
1015
const mono_microsecs *const current_time,
1016
bool *const mandos_client_exited,
1017
bool *const password_is_read){
1018
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1020
error(0, errno, "Failed to create inotify instance");
1024
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1025
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1028
error(0, errno, "Failed to create inotify watch on %s", dir);
1032
/* Add the inotify fd to the epoll set */
1033
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1034
&(struct epoll_event)
1035
{ .events=EPOLLIN | EPOLLRDHUP });
1036
if(ret != 0 and errno != EEXIST){
1037
error(0, errno, "Failed to add file descriptor to epoll set");
1042
const task_context read_inotify_event_task = {
1043
.func=read_inotify_event,
1048
.filename=strdup(dir),
1049
.cancelled_filenames=cancelled_filenames,
1050
.current_time=current_time,
1051
.mandos_client_exited=mandos_client_exited,
1052
.password_is_read=password_is_read,
1054
if(read_inotify_event_task.filename == NULL){
1055
error(0, errno, "Failed to strdup(\"%s\")", dir);
1060
return add_to_queue(queue, read_inotify_event_task);
1063
__attribute__((nonnull))
1064
void read_inotify_event(const task_context task,
1065
task_queue *const queue){
1066
const int fd = task.fd;
1067
const int epoll_fd = task.epoll_fd;
1068
char *const filename = task.filename;
1069
bool *quit_now = task.quit_now;
1070
buffer *const password = task.password;
1071
string_set *const cancelled_filenames = task.cancelled_filenames;
1072
const mono_microsecs *const current_time = task.current_time;
1073
bool *const mandos_client_exited = task.mandos_client_exited;
1074
bool *const password_is_read = task.password_is_read;
1076
/* "sufficient to read at least one event." - inotify(7) */
1077
const size_t ievent_size = (sizeof(struct inotify_event)
1080
struct inotify_event event;
1081
char name_buffer[NAME_MAX + 1];
1083
struct inotify_event *const ievent = &ievent_buffer.event;
1085
const ssize_t read_length = read(fd, ievent, ievent_size);
1086
if(read_length == 0){ /* EOF */
1087
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1089
cleanup_task(&task);
1092
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1093
error(0, errno, "Failed to read from inotify fd for directory %s",
1096
cleanup_task(&task);
1099
if(read_length > 0 /* Data has been read */
1100
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1101
char *question_filename = NULL;
1102
const ssize_t question_filename_length
1103
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1104
if(question_filename_length < 0){
1105
error(0, errno, "Failed to create file name from directory name"
1106
" %s and file name %s", filename, ievent->name);
1108
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1109
if(not add_to_queue(queue, (task_context){
1110
.func=open_and_parse_question,
1112
.question_filename=question_filename,
1113
.filename=question_filename,
1115
.cancelled_filenames=cancelled_filenames,
1116
.current_time=current_time,
1117
.mandos_client_exited=mandos_client_exited,
1118
.password_is_read=password_is_read,
1120
error(0, errno, "Failed to add open_and_parse_question task"
1121
" for file name %s to queue", filename);
1123
/* Force the added task (open_and_parse_question) to run
1125
queue->next_run = 1;
1127
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1128
if(not string_set_add(cancelled_filenames,
1129
question_filename)){
1130
error(0, errno, "Could not add question %s to"
1131
" cancelled_questions", question_filename);
1133
free(question_filename);
1134
cleanup_task(&task);
1137
free(question_filename);
1142
/* Either data was read, or EAGAIN was indicated, meaning no data
1145
/* Re-add myself to the queue */
1146
if(not add_to_queue(queue, task)){
1147
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1148
" queue", filename);
1150
cleanup_task(&task);
1154
/* Re-add the fd to the epoll set */
1155
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1156
&(struct epoll_event)
1157
{ .events=EPOLLIN | EPOLLRDHUP });
1158
if(ret != 0 and errno != EEXIST){
1159
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1160
" directory %s to epoll set", fd, filename);
1161
/* Force the added task (read_inotify_event) to run again, at most
1162
one second from now */
1163
if((queue->next_run == 0)
1164
or (queue->next_run > (*current_time + 1000000))){
1165
queue->next_run = *current_time + 1000000;
1170
__attribute__((nonnull))
1171
void open_and_parse_question(const task_context task,
1172
task_queue *const queue){
1173
__attribute__((cleanup(cleanup_string)))
1174
char *question_filename = task.question_filename;
1175
const int epoll_fd = task.epoll_fd;
1176
buffer *const password = task.password;
1177
string_set *const cancelled_filenames = task.cancelled_filenames;
1178
const mono_microsecs *const current_time = task.current_time;
1179
bool *const mandos_client_exited = task.mandos_client_exited;
1180
bool *const password_is_read = task.password_is_read;
1182
/* We use the GLib "Key-value file parser" functions to parse the
1183
question file. See <https://www.freedesktop.org/wiki/Software
1184
/systemd/PasswordAgents/> for specification of contents */
1185
__attribute__((nonnull))
1186
void cleanup_g_key_file(GKeyFile **key_file){
1187
if(*key_file != NULL){
1188
g_key_file_free(*key_file);
1192
__attribute__((cleanup(cleanup_g_key_file)))
1193
GKeyFile *key_file = g_key_file_new();
1194
if(key_file == NULL){
1195
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1199
GError *glib_error = NULL;
1200
if(g_key_file_load_from_file(key_file, question_filename,
1201
G_KEY_FILE_NONE, &glib_error) != TRUE){
1202
/* If a file was removed, we should ignore it, so */
1203
/* only show error message if file actually existed */
1204
if(glib_error->code != G_FILE_ERROR_NOENT){
1205
error(0, 0, "Failed to load question data from file \"%s\": %s",
1206
question_filename, glib_error->message);
1211
__attribute__((cleanup(cleanup_string)))
1212
char *socket_name = g_key_file_get_string(key_file, "Ask",
1215
if(socket_name == NULL){
1216
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1217
question_filename, glib_error->message);
1221
if(strlen(socket_name) == 0){
1222
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1227
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1229
if(glib_error != NULL){
1230
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1231
question_filename, glib_error->message);
1235
if((pid != (guint64)((pid_t)pid))
1236
or (kill((pid_t)pid, 0) != 0)){
1237
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1238
" does not exist", (uintmax_t)pid, question_filename);
1242
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1243
"NotAfter", &glib_error);
1244
if(glib_error != NULL){
1245
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1246
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1247
" %s", question_filename, glib_error->message);
1252
if(queue->next_run == 0 or (queue->next_run > notafter)){
1253
queue->next_run = notafter;
1255
if(*current_time >= notafter){
1260
const task_context connect_question_socket_task = {
1261
.func=connect_question_socket,
1262
.question_filename=strdup(question_filename),
1265
.filename=strdup(socket_name),
1266
.cancelled_filenames=task.cancelled_filenames,
1267
.mandos_client_exited=mandos_client_exited,
1268
.password_is_read=password_is_read,
1269
.current_time=current_time,
1271
if(connect_question_socket_task.question_filename == NULL
1272
or connect_question_socket_task.filename == NULL
1273
or not add_to_queue(queue, connect_question_socket_task)){
1274
error(0, errno, "Failed to add connect_question_socket for socket"
1275
" %s (from \"%s\") to queue", socket_name,
1277
cleanup_task(&connect_question_socket_task);
1280
/* Force the added task (connect_question_socket) to run
1282
queue->next_run = 1;
1285
char *const dup_filename = strdup(question_filename);
1286
const task_context cancel_old_question_task = {
1287
.func=cancel_old_question,
1288
.question_filename=dup_filename,
1290
.filename=dup_filename,
1291
.cancelled_filenames=cancelled_filenames,
1292
.current_time=current_time,
1294
if(cancel_old_question_task.question_filename == NULL
1295
or not add_to_queue(queue, cancel_old_question_task)){
1296
error(0, errno, "Failed to add cancel_old_question for file "
1297
"\"%s\" to queue", question_filename);
1298
cleanup_task(&cancel_old_question_task);
1304
__attribute__((nonnull))
1305
void cancel_old_question(const task_context task,
1306
task_queue *const queue){
1307
char *const question_filename = task.question_filename;
1308
string_set *const cancelled_filenames = task.cancelled_filenames;
1309
const mono_microsecs notafter = task.notafter;
1310
const mono_microsecs *const current_time = task.current_time;
1312
if(*current_time >= notafter){
1313
if(not string_set_add(cancelled_filenames, question_filename)){
1314
error(0, errno, "Failed to cancel question for file %s",
1317
cleanup_task(&task);
1321
if(not add_to_queue(queue, task)){
1322
error(0, errno, "Failed to add cancel_old_question for file "
1323
"%s to queue", question_filename);
1324
cleanup_task(&task);
1328
if((queue->next_run == 0) or (queue->next_run > notafter)){
1329
queue->next_run = notafter;
1333
__attribute__((nonnull))
1334
void connect_question_socket(const task_context task,
1335
task_queue *const queue){
1336
char *const question_filename = task.question_filename;
1337
char *const filename = task.filename;
1338
const int epoll_fd = task.epoll_fd;
1339
buffer *const password = task.password;
1340
string_set *const cancelled_filenames = task.cancelled_filenames;
1341
bool *const mandos_client_exited = task.mandos_client_exited;
1342
bool *const password_is_read = task.password_is_read;
1343
const mono_microsecs *const current_time = task.current_time;
1345
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1347
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1348
error(0, 0, "Socket filename is larger than"
1349
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1350
(uintmax_t)sizeof(sock_name.sun_path), filename);
1351
if(not string_set_add(cancelled_filenames, question_filename)){
1352
error(0, errno, "Failed to cancel question for file %s",
1355
cleanup_task(&task);
1359
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1360
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1363
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1364
if(not add_to_queue(queue, task)){
1365
error(0, errno, "Failed to add connect_question_socket for file"
1366
" \"%s\" and socket \"%s\" to queue", question_filename,
1368
cleanup_task(&task);
1370
/* Force the added task (connect_question_socket) to run
1372
queue->next_run = 1;
1377
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1378
if(connect(fd, (struct sockaddr *)&sock_name,
1379
(socklen_t)SUN_LEN(&sock_name)) != 0){
1380
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1381
if(not add_to_queue(queue, task)){
1382
error(0, errno, "Failed to add connect_question_socket for file"
1383
" \"%s\" and socket \"%s\" to queue", question_filename,
1385
cleanup_task(&task);
1387
/* Force the added task (connect_question_socket) to run again,
1388
at most one second from now */
1389
if((queue->next_run == 0)
1390
or (queue->next_run > (*current_time + 1000000))){
1391
queue->next_run = *current_time + 1000000;
1397
/* Not necessary, but we can try, and merely warn on failure */
1398
if(shutdown(fd, SHUT_RD) != 0){
1399
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1403
/* Add the fd to the epoll set */
1404
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1405
&(struct epoll_event){ .events=EPOLLOUT })
1407
error(0, errno, "Failed to add inotify file descriptor %d for"
1408
" socket %s to epoll set", fd, filename);
1409
if(not add_to_queue(queue, task)){
1410
error(0, errno, "Failed to add connect_question_socket for file"
1411
" \"%s\" and socket \"%s\" to queue", question_filename,
1413
cleanup_task(&task);
1415
/* Force the added task (connect_question_socket) to run again,
1416
at most one second from now */
1417
if((queue->next_run == 0)
1418
or (queue->next_run > (*current_time + 1000000))){
1419
queue->next_run = *current_time + 1000000;
1425
/* add task send_password_to_socket to queue */
1426
const task_context send_password_to_socket_task = {
1427
.func=send_password_to_socket,
1428
.question_filename=question_filename,
1433
.cancelled_filenames=cancelled_filenames,
1434
.mandos_client_exited=mandos_client_exited,
1435
.password_is_read=password_is_read,
1436
.current_time=current_time,
1439
if(not add_to_queue(queue, send_password_to_socket_task)){
1440
error(0, errno, "Failed to add send_password_to_socket for"
1441
" file \"%s\" and socket \"%s\" to queue",
1442
question_filename, filename);
1443
cleanup_task(&send_password_to_socket_task);
1447
__attribute__((nonnull))
1448
void send_password_to_socket(const task_context task,
1449
task_queue *const queue){
1450
char *const question_filename=task.question_filename;
1451
char *const filename=task.filename;
1452
const int epoll_fd=task.epoll_fd;
1453
const int fd=task.fd;
1454
buffer *const password=task.password;
1455
string_set *const cancelled_filenames=task.cancelled_filenames;
1456
bool *const mandos_client_exited = task.mandos_client_exited;
1457
bool *const password_is_read = task.password_is_read;
1458
const mono_microsecs *const current_time = task.current_time;
1460
if(*mandos_client_exited and *password_is_read){
1462
const size_t send_buffer_length = password->length + 2;
1463
char *send_buffer = malloc(send_buffer_length);
1464
if(send_buffer == NULL){
1465
error(0, errno, "Failed to allocate send_buffer");
1467
if(mlock(send_buffer, send_buffer_length) != 0){
1468
/* Warn but do not treat as fatal error */
1469
if(errno != EPERM and errno != ENOMEM){
1470
error(0, errno, "Failed to lock memory for password"
1474
/* “[…] send a single datagram to the socket consisting of the
1475
password string either prefixed with "+" or with "-"
1476
depending on whether the password entry was successful or
1477
not. You may but don't have to include a final NUL byte in
1480
— <https://www.freedesktop.org/wiki/Software/systemd/
1481
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1483
send_buffer[0] = '+'; /* Prefix with "+" */
1484
/* Always add an extra NUL */
1485
send_buffer[password->length + 1] = '\0';
1486
if(password->length > 0){
1487
memcpy(send_buffer + 1, password->data, password->length);
1490
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1492
const error_t saved_errno = errno;
1493
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1494
explicit_bzero(send_buffer, send_buffer_length);
1496
memset(send_buffer, '\0', send_buffer_length);
1498
if(munlock(send_buffer, send_buffer_length) != 0){
1499
error(0, errno, "Failed to unlock memory of send buffer");
1502
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1503
switch(saved_errno){
1516
error(0, 0, "Password of size %" PRIuMAX " is too big",
1517
(uintmax_t)password->length);
1521
__attribute__((fallthrough));
1524
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1525
error(0, 0, "Password only partially sent to socket");
1530
__attribute__((fallthrough));
1533
error(0, saved_errno, "Failed to send() to socket %s",
1535
if(not string_set_add(cancelled_filenames,
1536
question_filename)){
1537
error(0, errno, "Failed to cancel question for file %s",
1540
cleanup_task(&task);
1545
cleanup_task(&task);
1551
/* We failed or are not ready yet; retry later */
1553
if(not add_to_queue(queue, task)){
1554
error(0, errno, "Failed to add send_password_to_socket for"
1555
" file %s and socket %s to queue", question_filename,
1557
cleanup_task(&task);
1560
/* Add the fd to the epoll set */
1561
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1562
&(struct epoll_event){ .events=EPOLLOUT })
1564
error(0, errno, "Failed to add socket file descriptor %d for"
1565
" socket %s to epoll set", fd, filename);
1566
/* Force the added task (send_password_to_socket) to run again, at
1567
most one second from now */
1568
if((queue->next_run == 0)
1569
or (queue->next_run > (*current_time + 1000000))){
1570
queue->next_run = *current_time + 1000000;
1575
__attribute__((warn_unused_result))
1576
bool add_existing_questions(task_queue *const queue,
1578
buffer *const password,
1579
string_set *cancelled_filenames,
1580
const mono_microsecs *const current_time,
1581
bool *const mandos_client_exited,
1582
bool *const password_is_read,
1583
const char *const dirname){
1584
__attribute__((cleanup(cleanup_string)))
1585
char *dir_pattern = NULL;
1586
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1587
if(ret < 0 or dir_pattern == NULL){
1588
error(0, errno, "Could not create glob pattern for directory %s",
1592
__attribute__((cleanup(globfree)))
1593
glob_t question_filenames = {};
1594
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1595
NULL, &question_filenames)){
1598
error(0, errno, "Failed to open directory %s", dirname);
1601
error(0, errno, "There are no question files in %s", dirname);
1604
error(0, errno, "Could not allocate memory for question file"
1605
" names in %s", dirname);
1609
__attribute__((fallthrough));
1612
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1613
char *const question_filename = strdup(question_filenames
1615
const task_context task = {
1616
.func=open_and_parse_question,
1618
.question_filename=question_filename,
1619
.filename=question_filename,
1621
.cancelled_filenames=cancelled_filenames,
1622
.current_time=current_time,
1623
.mandos_client_exited=mandos_client_exited,
1624
.password_is_read=password_is_read,
1627
if(question_filename == NULL
1628
or not add_to_queue(queue, task)){
1629
error(0, errno, "Failed to add open_and_parse_question for"
1630
" file %s to queue",
1631
question_filenames.gl_pathv[i]);
1632
free(question_filename);
1634
queue->next_run = 1;
1641
__attribute__((nonnull, warn_unused_result))
1642
bool wait_for_event(const int epoll_fd,
1643
const mono_microsecs queue_next_run,
1644
const mono_microsecs current_time){
1645
__attribute__((const))
1646
int milliseconds_to_wait(const mono_microsecs currtime,
1647
const mono_microsecs nextrun){
1648
if(currtime >= nextrun){
1651
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1652
if(wait_time_ms > (uintmax_t)INT_MAX){
1655
return (int)wait_time_ms;
1658
const int wait_time_ms = milliseconds_to_wait(current_time,
1661
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1662
sigset_t temporary_unblocked_sigmask;
1663
/* Get current signal mask */
1664
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1667
/* Remove SIGCHLD from the signal mask */
1668
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1671
struct epoll_event events[8]; /* Ignored */
1672
int ret = epoll_pwait(epoll_fd, events,
1673
sizeof(events) / sizeof(struct epoll_event),
1674
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1675
&temporary_unblocked_sigmask);
1676
if(ret < 0 and errno != EINTR){
1677
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1679
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1682
return clear_all_fds_from_epoll_set(epoll_fd);
1685
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1686
/* Create a new empty epoll set */
1687
__attribute__((cleanup(cleanup_close)))
1688
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1689
if(new_epoll_fd < 0){
1692
/* dup3() the new epoll set fd over the old one, replacing it */
1693
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1699
__attribute__((nonnull, warn_unused_result))
1700
bool run_queue(task_queue **const queue,
1701
string_set *const cancelled_filenames,
1702
bool *const quit_now){
1704
task_queue *new_queue = create_queue();
1705
if(new_queue == NULL){
1709
__attribute__((cleanup(string_set_clear)))
1710
string_set old_cancelled_filenames = {};
1711
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1713
/* Declare i outside the for loop, since we might need i after the
1714
loop in case we aborted in the middle */
1716
for(i=0; i < (*queue)->length and not *quit_now; i++){
1717
task_context *const task = &((*queue)->tasks[i]);
1718
const char *const question_filename = task->question_filename;
1719
/* Skip any task referencing a cancelled question filename */
1720
if(question_filename != NULL
1721
and string_set_contains(old_cancelled_filenames,
1722
question_filename)){
1726
task->func(*task, new_queue);
1730
/* we might be in the middle of the queue, so clean up any
1731
remaining tasks in the current queue */
1732
for(; i < (*queue)->length; i++){
1733
cleanup_task(&((*queue)->tasks[i]));
1747
/* End of regular code section */
1749
/* Start of tests section; here are the tests for the above code */
1751
/* This "fixture" data structure is used by the test setup and
1752
teardown functions */
1754
struct sigaction orig_sigaction;
1755
sigset_t orig_sigmask;
1758
static void test_setup(test_fixture *fixture,
1759
__attribute__((unused))
1760
gconstpointer user_data){
1761
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1762
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1765
static void test_teardown(test_fixture *fixture,
1766
__attribute__((unused))
1767
gconstpointer user_data){
1768
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1769
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1772
/* Utility function used by tests to search queue for matching task */
1773
__attribute__((pure, nonnull, warn_unused_result))
1774
static task_context *find_matching_task(const task_queue *const queue,
1775
const task_context task){
1776
/* The argument "task" structure is a pattern to match; 0 in any
1777
member means any value matches, otherwise the value must match.
1778
The filename strings are compared by strcmp(), not by pointer. */
1779
for(size_t i = 0; i < queue->length; i++){
1780
task_context *const current_task = queue->tasks+i;
1781
/* Check all members of task_context, if set to a non-zero value.
1782
If a member does not match, continue to next task in queue */
1784
/* task_func *const func */
1785
if(task.func != NULL and current_task->func != task.func){
1788
/* char *const question_filename; */
1789
if(task.question_filename != NULL
1790
and (current_task->question_filename == NULL
1791
or strcmp(current_task->question_filename,
1792
task.question_filename) != 0)){
1795
/* const pid_t pid; */
1796
if(task.pid != 0 and current_task->pid != task.pid){
1799
/* const int epoll_fd; */
1800
if(task.epoll_fd != 0
1801
and current_task->epoll_fd != task.epoll_fd){
1804
/* bool *const quit_now; */
1805
if(task.quit_now != NULL
1806
and current_task->quit_now != task.quit_now){
1810
if(task.fd != 0 and current_task->fd != task.fd){
1813
/* bool *const mandos_client_exited; */
1814
if(task.mandos_client_exited != NULL
1815
and current_task->mandos_client_exited
1816
!= task.mandos_client_exited){
1819
/* buffer *const password; */
1820
if(task.password != NULL
1821
and current_task->password != task.password){
1824
/* bool *const password_is_read; */
1825
if(task.password_is_read != NULL
1826
and current_task->password_is_read != task.password_is_read){
1829
/* char *filename; */
1830
if(task.filename != NULL
1831
and (current_task->filename == NULL
1832
or strcmp(current_task->filename, task.filename) != 0)){
1835
/* string_set *const cancelled_filenames; */
1836
if(task.cancelled_filenames != NULL
1837
and current_task->cancelled_filenames
1838
!= task.cancelled_filenames){
1841
/* const mono_microsecs notafter; */
1842
if(task.notafter != 0
1843
and current_task->notafter != task.notafter){
1846
/* const mono_microsecs *const current_time; */
1847
if(task.current_time != NULL
1848
and current_task->current_time != task.current_time){
1851
/* Current task matches all members; return it */
1852
return current_task;
1854
/* No task in queue matches passed pattern task */
1858
static void test_create_queue(__attribute__((unused))
1859
test_fixture *fixture,
1860
__attribute__((unused))
1861
gconstpointer user_data){
1862
__attribute__((cleanup(cleanup_queue)))
1863
task_queue *const queue = create_queue();
1864
g_assert_nonnull(queue);
1865
g_assert_null(queue->tasks);
1866
g_assert_true(queue->length == 0);
1867
g_assert_true(queue->next_run == 0);
1870
static task_func dummy_func;
1872
static void test_add_to_queue(__attribute__((unused))
1873
test_fixture *fixture,
1874
__attribute__((unused))
1875
gconstpointer user_data){
1876
__attribute__((cleanup(cleanup_queue)))
1877
task_queue *queue = create_queue();
1878
g_assert_nonnull(queue);
1880
g_assert_true(add_to_queue(queue,
1881
(task_context){ .func=dummy_func }));
1882
g_assert_true(queue->length == 1);
1883
g_assert_nonnull(queue->tasks);
1884
g_assert_true(queue->tasks[0].func == dummy_func);
1887
static void dummy_func(__attribute__((unused))
1888
const task_context task,
1889
__attribute__((unused))
1890
task_queue *const queue){
1893
static void test_queue_has_question_empty(__attribute__((unused))
1894
test_fixture *fixture,
1895
__attribute__((unused))
1896
gconstpointer user_data){
1897
__attribute__((cleanup(cleanup_queue)))
1898
task_queue *queue = create_queue();
1899
g_assert_nonnull(queue);
1900
g_assert_false(queue_has_question(queue));
1903
static void test_queue_has_question_false(__attribute__((unused))
1904
test_fixture *fixture,
1905
__attribute__((unused))
1906
gconstpointer user_data){
1907
__attribute__((cleanup(cleanup_queue)))
1908
task_queue *queue = create_queue();
1909
g_assert_nonnull(queue);
1910
g_assert_true(add_to_queue(queue,
1911
(task_context){ .func=dummy_func }));
1912
g_assert_false(queue_has_question(queue));
1915
static void test_queue_has_question_true(__attribute__((unused))
1916
test_fixture *fixture,
1917
__attribute__((unused))
1918
gconstpointer user_data){
1919
__attribute__((cleanup(cleanup_queue)))
1920
task_queue *queue = create_queue();
1921
g_assert_nonnull(queue);
1922
char *const question_filename
1923
= strdup("/nonexistent/question_filename");
1924
g_assert_nonnull(question_filename);
1925
task_context task = {
1927
.question_filename=question_filename,
1929
g_assert_true(add_to_queue(queue, task));
1930
g_assert_true(queue_has_question(queue));
1933
static void test_queue_has_question_false2(__attribute__((unused))
1934
test_fixture *fixture,
1935
__attribute__((unused))
1936
gconstpointer user_data){
1937
__attribute__((cleanup(cleanup_queue)))
1938
task_queue *queue = create_queue();
1939
g_assert_nonnull(queue);
1940
task_context task = { .func=dummy_func };
1941
g_assert_true(add_to_queue(queue, task));
1942
g_assert_true(add_to_queue(queue, task));
1943
g_assert_cmpint((int)queue->length, ==, 2);
1944
g_assert_false(queue_has_question(queue));
1947
static void test_queue_has_question_true2(__attribute__((unused))
1948
test_fixture *fixture,
1949
__attribute__((unused))
1950
gconstpointer user_data){
1951
__attribute__((cleanup(cleanup_queue)))
1952
task_queue *queue = create_queue();
1953
g_assert_nonnull(queue);
1954
task_context task1 = { .func=dummy_func };
1955
g_assert_true(add_to_queue(queue, task1));
1956
char *const question_filename
1957
= strdup("/nonexistent/question_filename");
1958
g_assert_nonnull(question_filename);
1959
task_context task2 = {
1961
.question_filename=question_filename,
1963
g_assert_true(add_to_queue(queue, task2));
1964
g_assert_cmpint((int)queue->length, ==, 2);
1965
g_assert_true(queue_has_question(queue));
1968
static void test_cleanup_buffer(__attribute__((unused))
1969
test_fixture *fixture,
1970
__attribute__((unused))
1971
gconstpointer user_data){
1974
const size_t buffersize = 10;
1976
buf.data = malloc(buffersize);
1977
g_assert_nonnull(buf.data);
1978
if(mlock(buf.data, buffersize) != 0){
1979
g_assert_true(errno == EPERM or errno == ENOMEM);
1982
cleanup_buffer(&buf);
1983
g_assert_null(buf.data);
1987
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1988
test_fixture *fixture,
1989
__attribute__((unused))
1992
__attribute__((cleanup(string_set_clear)))
1993
string_set set = {};
1994
g_assert_false(string_set_contains(set, "")); /* Empty string */
1995
g_assert_false(string_set_contains(set, "test_string"));
1999
test_string_set_with_added_string_contains_it(__attribute__((unused))
2000
test_fixture *fixture,
2001
__attribute__((unused))
2004
__attribute__((cleanup(string_set_clear)))
2005
string_set set = {};
2006
g_assert_true(string_set_add(&set, "test_string"));
2007
g_assert_true(string_set_contains(set, "test_string"));
2011
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2012
test_fixture *fixture,
2013
__attribute__((unused))
2014
gconstpointer user_data){
2015
__attribute__((cleanup(string_set_clear)))
2016
string_set set = {};
2017
g_assert_true(string_set_add(&set, "test_string"));
2018
string_set_clear(&set);
2019
g_assert_false(string_set_contains(set, "test_string"));
2023
void test_string_set_swap_one_with_empty(__attribute__((unused))
2024
test_fixture *fixture,
2025
__attribute__((unused))
2026
gconstpointer user_data){
2027
__attribute__((cleanup(string_set_clear)))
2028
string_set set1 = {};
2029
__attribute__((cleanup(string_set_clear)))
2030
string_set set2 = {};
2031
g_assert_true(string_set_add(&set1, "test_string1"));
2032
string_set_swap(&set1, &set2);
2033
g_assert_false(string_set_contains(set1, "test_string1"));
2034
g_assert_true(string_set_contains(set2, "test_string1"));
2038
void test_string_set_swap_empty_with_one(__attribute__((unused))
2039
test_fixture *fixture,
2040
__attribute__((unused))
2041
gconstpointer user_data){
2042
__attribute__((cleanup(string_set_clear)))
2043
string_set set1 = {};
2044
__attribute__((cleanup(string_set_clear)))
2045
string_set set2 = {};
2046
g_assert_true(string_set_add(&set2, "test_string2"));
2047
string_set_swap(&set1, &set2);
2048
g_assert_true(string_set_contains(set1, "test_string2"));
2049
g_assert_false(string_set_contains(set2, "test_string2"));
2052
static void test_string_set_swap_one_with_one(__attribute__((unused))
2053
test_fixture *fixture,
2054
__attribute__((unused))
2057
__attribute__((cleanup(string_set_clear)))
2058
string_set set1 = {};
2059
__attribute__((cleanup(string_set_clear)))
2060
string_set set2 = {};
2061
g_assert_true(string_set_add(&set1, "test_string1"));
2062
g_assert_true(string_set_add(&set2, "test_string2"));
2063
string_set_swap(&set1, &set2);
2064
g_assert_false(string_set_contains(set1, "test_string1"));
2065
g_assert_true(string_set_contains(set1, "test_string2"));
2066
g_assert_false(string_set_contains(set2, "test_string2"));
2067
g_assert_true(string_set_contains(set2, "test_string1"));
2070
static bool fd_has_cloexec_and_nonblock(const int);
2072
static bool epoll_set_contains(int, int, uint32_t);
2074
static void test_start_mandos_client(test_fixture *fixture,
2075
__attribute__((unused))
2076
gconstpointer user_data){
2078
bool mandos_client_exited = false;
2079
bool quit_now = false;
2080
__attribute__((cleanup(cleanup_close)))
2081
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2082
g_assert_cmpint(epoll_fd, >=, 0);
2083
__attribute__((cleanup(cleanup_queue)))
2084
task_queue *queue = create_queue();
2085
g_assert_nonnull(queue);
2086
buffer password = {};
2087
bool password_is_read = false;
2088
const char helper_directory[] = "/nonexistent";
2089
const char *const argv[] = { "/bin/true", NULL };
2091
g_assert_true(start_mandos_client(queue, epoll_fd,
2092
&mandos_client_exited, &quit_now,
2093
&password, &password_is_read,
2094
&fixture->orig_sigaction,
2095
fixture->orig_sigmask,
2096
helper_directory, 0, 0, argv));
2098
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2100
const task_context *const added_wait_task
2101
= find_matching_task(queue, (task_context){
2102
.func=wait_for_mandos_client_exit,
2103
.mandos_client_exited=&mandos_client_exited,
2104
.quit_now=&quit_now,
2106
g_assert_nonnull(added_wait_task);
2107
g_assert_cmpint(added_wait_task->pid, >, 0);
2108
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2109
waitpid(added_wait_task->pid, NULL, 0);
2111
const task_context *const added_read_task
2112
= find_matching_task(queue, (task_context){
2113
.func=read_mandos_client_output,
2115
.password=&password,
2116
.password_is_read=&password_is_read,
2117
.quit_now=&quit_now,
2119
g_assert_nonnull(added_read_task);
2120
g_assert_cmpint(added_read_task->fd, >, 2);
2121
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2122
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2123
EPOLLIN | EPOLLRDHUP));
2126
static bool fd_has_cloexec_and_nonblock(const int fd){
2127
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2128
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2129
return ((socket_fd_flags >= 0)
2130
and (socket_fd_flags & FD_CLOEXEC)
2131
and (socket_file_flags >= 0)
2132
and (socket_file_flags & O_NONBLOCK));
2135
__attribute__((const))
2136
bool is_privileged(void){
2137
uid_t user = getuid() + 1;
2138
if(user == 0){ /* Overflow check */
2141
gid_t group = getuid() + 1;
2142
if(group == 0){ /* Overflow check */
2145
const pid_t pid = fork();
2146
if(pid == 0){ /* Child */
2147
if(setresgid((uid_t)-1, group, group) == -1){
2149
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2150
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2154
if(setresuid((uid_t)-1, user, user) == -1){
2156
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2157
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2164
waitpid(pid, &status, 0);
2165
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2171
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2172
/* Only scan for events in this eventmask */
2173
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2174
__attribute__((cleanup(cleanup_string)))
2175
char *fdinfo_name = NULL;
2176
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2177
g_assert_cmpint(ret, >, 0);
2178
g_assert_nonnull(fdinfo_name);
2180
FILE *fdinfo = fopen(fdinfo_name, "r");
2181
g_assert_nonnull(fdinfo);
2182
uint32_t reported_events;
2187
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2190
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2191
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2192
&found_fd, &reported_events) == 2){
2197
} while(not feof(fdinfo) and not ferror(fdinfo));
2198
g_assert_cmpint(fclose(fdinfo), ==, 0);
2205
/* Don't check events if none are given */
2208
return (reported_events & eventmask) == (events & eventmask);
2211
static void test_start_mandos_client_execv(test_fixture *fixture,
2212
__attribute__((unused))
2213
gconstpointer user_data){
2214
bool mandos_client_exited = false;
2215
bool quit_now = false;
2216
__attribute__((cleanup(cleanup_close)))
2217
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2218
g_assert_cmpint(epoll_fd, >=, 0);
2219
__attribute__((cleanup(cleanup_queue)))
2220
task_queue *queue = create_queue();
2221
g_assert_nonnull(queue);
2222
__attribute__((cleanup(cleanup_buffer)))
2223
buffer password = {};
2224
const char helper_directory[] = "/nonexistent";
2225
/* Can't execv("/", ...), so this should fail */
2226
const char *const argv[] = { "/", NULL };
2229
__attribute__((cleanup(cleanup_close)))
2230
const int devnull_fd = open("/dev/null",
2231
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2232
g_assert_cmpint(devnull_fd, >=, 0);
2233
__attribute__((cleanup(cleanup_close)))
2234
const int real_stderr_fd = dup(STDERR_FILENO);
2235
g_assert_cmpint(real_stderr_fd, >=, 0);
2236
dup2(devnull_fd, STDERR_FILENO);
2238
const bool success = start_mandos_client(queue, epoll_fd,
2239
&mandos_client_exited,
2243
&fixture->orig_sigaction,
2244
fixture->orig_sigmask,
2245
helper_directory, 0, 0,
2247
dup2(real_stderr_fd, STDERR_FILENO);
2248
g_assert_true(success);
2250
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2252
struct timespec starttime, currtime;
2253
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2255
queue->next_run = 0;
2256
string_set cancelled_filenames = {};
2259
__attribute__((cleanup(cleanup_close)))
2260
const int devnull_fd = open("/dev/null",
2261
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2262
g_assert_cmpint(devnull_fd, >=, 0);
2263
__attribute__((cleanup(cleanup_close)))
2264
const int real_stderr_fd = dup(STDERR_FILENO);
2265
g_assert_cmpint(real_stderr_fd, >=, 0);
2266
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2267
dup2(devnull_fd, STDERR_FILENO);
2268
const bool success = run_queue(&queue, &cancelled_filenames,
2270
dup2(real_stderr_fd, STDERR_FILENO);
2275
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2276
} while(((queue->length) > 0)
2278
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2280
g_assert_true(quit_now);
2281
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2282
g_assert_true(mandos_client_exited);
2285
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2286
__attribute__((unused))
2289
if(not is_privileged()){
2290
g_test_skip("Not privileged");
2294
bool mandos_client_exited = false;
2295
bool quit_now = false;
2296
__attribute__((cleanup(cleanup_close)))
2297
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2298
g_assert_cmpint(epoll_fd, >=, 0);
2299
__attribute__((cleanup(cleanup_queue)))
2300
task_queue *queue = create_queue();
2301
g_assert_nonnull(queue);
2302
__attribute__((cleanup(cleanup_buffer)))
2303
buffer password = {};
2304
bool password_is_read = false;
2305
const char helper_directory[] = "/nonexistent";
2306
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2310
const bool success = start_mandos_client(queue, epoll_fd,
2311
&mandos_client_exited,
2312
&quit_now, &password,
2314
&fixture->orig_sigaction,
2315
fixture->orig_sigmask,
2316
helper_directory, user,
2318
g_assert_true(success);
2319
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2321
struct timespec starttime, currtime;
2322
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2324
queue->next_run = 0;
2325
string_set cancelled_filenames = {};
2326
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2327
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2328
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2329
} while(((queue->length) > 0)
2331
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2333
g_assert_false(quit_now);
2334
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2335
g_assert_true(mandos_client_exited);
2337
g_assert_true(password_is_read);
2338
g_assert_nonnull(password.data);
2341
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2343
g_assert_true((uid_t)id == id);
2345
g_assert_cmpuint((unsigned int)id, ==, 0);
2348
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2349
__attribute__((unused))
2352
if(not is_privileged()){
2353
g_test_skip("Not privileged");
2357
bool mandos_client_exited = false;
2358
bool quit_now = false;
2359
__attribute__((cleanup(cleanup_close)))
2360
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2361
g_assert_cmpint(epoll_fd, >=, 0);
2362
__attribute__((cleanup(cleanup_queue)))
2363
task_queue *queue = create_queue();
2364
g_assert_nonnull(queue);
2365
__attribute__((cleanup(cleanup_buffer)))
2366
buffer password = {};
2367
bool password_is_read = false;
2368
const char helper_directory[] = "/nonexistent";
2369
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2373
const bool success = start_mandos_client(queue, epoll_fd,
2374
&mandos_client_exited,
2375
&quit_now, &password,
2377
&fixture->orig_sigaction,
2378
fixture->orig_sigmask,
2379
helper_directory, user,
2381
g_assert_true(success);
2382
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2384
struct timespec starttime, currtime;
2385
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2387
queue->next_run = 0;
2388
string_set cancelled_filenames = {};
2389
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2390
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2391
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2392
} while(((queue->length) > 0)
2394
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2396
g_assert_false(quit_now);
2397
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2398
g_assert_true(mandos_client_exited);
2400
g_assert_true(password_is_read);
2401
g_assert_nonnull(password.data);
2404
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2406
g_assert_true((gid_t)id == id);
2408
g_assert_cmpuint((unsigned int)id, ==, 0);
2411
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2412
__attribute__((unused))
2415
if(not is_privileged()){
2416
g_test_skip("Not privileged");
2420
bool mandos_client_exited = false;
2421
bool quit_now = false;
2422
__attribute__((cleanup(cleanup_close)))
2423
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2424
g_assert_cmpint(epoll_fd, >=, 0);
2425
__attribute__((cleanup(cleanup_queue)))
2426
task_queue *queue = create_queue();
2427
g_assert_nonnull(queue);
2428
__attribute__((cleanup(cleanup_buffer)))
2429
buffer password = {};
2430
bool password_is_read = false;
2431
const char helper_directory[] = "/nonexistent";
2432
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2437
const bool success = start_mandos_client(queue, epoll_fd,
2438
&mandos_client_exited,
2439
&quit_now, &password,
2441
&fixture->orig_sigaction,
2442
fixture->orig_sigmask,
2443
helper_directory, user,
2445
g_assert_true(success);
2446
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2448
struct timespec starttime, currtime;
2449
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2451
queue->next_run = 0;
2452
string_set cancelled_filenames = {};
2453
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2454
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2455
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2456
} while(((queue->length) > 0)
2458
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2460
g_assert_false(quit_now);
2461
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2462
g_assert_true(mandos_client_exited);
2464
g_assert_true(password_is_read);
2465
g_assert_nonnull(password.data);
2468
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2470
g_assert_true((uid_t)id == id);
2472
g_assert_cmpuint((unsigned int)id, ==, user);
2475
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2476
__attribute__((unused))
2479
if(not is_privileged()){
2480
g_test_skip("Not privileged");
2484
bool mandos_client_exited = false;
2485
bool quit_now = false;
2486
__attribute__((cleanup(cleanup_close)))
2487
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2488
g_assert_cmpint(epoll_fd, >=, 0);
2489
__attribute__((cleanup(cleanup_queue)))
2490
task_queue *queue = create_queue();
2491
g_assert_nonnull(queue);
2492
__attribute__((cleanup(cleanup_buffer)))
2493
buffer password = {};
2494
bool password_is_read = false;
2495
const char helper_directory[] = "/nonexistent";
2496
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2501
const bool success = start_mandos_client(queue, epoll_fd,
2502
&mandos_client_exited,
2503
&quit_now, &password,
2505
&fixture->orig_sigaction,
2506
fixture->orig_sigmask,
2507
helper_directory, user,
2509
g_assert_true(success);
2510
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2512
struct timespec starttime, currtime;
2513
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2515
queue->next_run = 0;
2516
string_set cancelled_filenames = {};
2517
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2518
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2519
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2520
} while(((queue->length) > 0)
2522
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2524
g_assert_false(quit_now);
2525
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2526
g_assert_true(mandos_client_exited);
2528
g_assert_true(password_is_read);
2529
g_assert_nonnull(password.data);
2532
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2534
g_assert_true((gid_t)id == id);
2536
g_assert_cmpuint((unsigned int)id, ==, group);
2539
static void test_start_mandos_client_read(test_fixture *fixture,
2540
__attribute__((unused))
2541
gconstpointer user_data){
2542
bool mandos_client_exited = false;
2543
bool quit_now = false;
2544
__attribute__((cleanup(cleanup_close)))
2545
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2546
g_assert_cmpint(epoll_fd, >=, 0);
2547
__attribute__((cleanup(cleanup_queue)))
2548
task_queue *queue = create_queue();
2549
g_assert_nonnull(queue);
2550
__attribute__((cleanup(cleanup_buffer)))
2551
buffer password = {};
2552
bool password_is_read = false;
2553
const char dummy_test_password[] = "dummy test password";
2554
const char helper_directory[] = "/nonexistent";
2555
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2558
const bool success = start_mandos_client(queue, epoll_fd,
2559
&mandos_client_exited,
2560
&quit_now, &password,
2562
&fixture->orig_sigaction,
2563
fixture->orig_sigmask,
2564
helper_directory, 0, 0,
2566
g_assert_true(success);
2567
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2569
struct timespec starttime, currtime;
2570
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2572
queue->next_run = 0;
2573
string_set cancelled_filenames = {};
2574
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2575
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2576
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2577
} while(((queue->length) > 0)
2579
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2581
g_assert_false(quit_now);
2582
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2583
g_assert_true(mandos_client_exited);
2585
g_assert_true(password_is_read);
2586
g_assert_cmpint((int)password.length, ==,
2587
sizeof(dummy_test_password)-1);
2588
g_assert_nonnull(password.data);
2589
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2590
sizeof(dummy_test_password)-1), ==, 0);
2594
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2595
__attribute__((unused))
2598
bool mandos_client_exited = false;
2599
bool quit_now = false;
2600
__attribute__((cleanup(cleanup_close)))
2601
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2602
g_assert_cmpint(epoll_fd, >=, 0);
2603
__attribute__((cleanup(cleanup_queue)))
2604
task_queue *queue = create_queue();
2605
g_assert_nonnull(queue);
2606
__attribute__((cleanup(cleanup_buffer)))
2607
buffer password = {};
2608
bool password_is_read = false;
2609
const char helper_directory[] = "/nonexistent";
2610
const char *const argv[] = { "/bin/sh", "-c",
2611
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2613
const bool success = start_mandos_client(queue, epoll_fd,
2614
&mandos_client_exited,
2615
&quit_now, &password,
2617
&fixture->orig_sigaction,
2618
fixture->orig_sigmask,
2619
helper_directory, 0, 0,
2621
g_assert_true(success);
2622
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2624
struct timespec starttime, currtime;
2625
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2627
queue->next_run = 0;
2628
string_set cancelled_filenames = {};
2629
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2630
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2631
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2632
} while(((queue->length) > 0)
2634
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2636
g_assert_false(quit_now);
2637
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2638
g_assert_true(mandos_client_exited);
2640
g_assert_true(password_is_read);
2641
g_assert_cmpint((int)password.length, ==,
2642
sizeof(helper_directory)-1);
2643
g_assert_nonnull(password.data);
2644
g_assert_cmpint(memcmp(helper_directory, password.data,
2645
sizeof(helper_directory)-1), ==, 0);
2648
__attribute__((nonnull, warn_unused_result))
2649
static bool proc_status_sigblk_to_sigset(const char *const,
2652
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2653
__attribute__((unused))
2654
gconstpointer user_data){
2655
bool mandos_client_exited = false;
2656
bool quit_now = false;
2657
__attribute__((cleanup(cleanup_close)))
2658
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2659
g_assert_cmpint(epoll_fd, >=, 0);
2660
__attribute__((cleanup(cleanup_queue)))
2661
task_queue *queue = create_queue();
2662
g_assert_nonnull(queue);
2663
__attribute__((cleanup(cleanup_buffer)))
2664
buffer password = {};
2665
bool password_is_read = false;
2666
const char helper_directory[] = "/nonexistent";
2667
/* see proc(5) for format of /proc/self/status */
2668
const char *const argv[] = { "/usr/bin/awk",
2669
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2671
g_assert_true(start_mandos_client(queue, epoll_fd,
2672
&mandos_client_exited, &quit_now,
2673
&password, &password_is_read,
2674
&fixture->orig_sigaction,
2675
fixture->orig_sigmask,
2676
helper_directory, 0, 0, argv));
2678
struct timespec starttime, currtime;
2679
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2681
queue->next_run = 0;
2682
string_set cancelled_filenames = {};
2683
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2684
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2685
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2686
} while((not (mandos_client_exited and password_is_read))
2688
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2689
g_assert_true(mandos_client_exited);
2690
g_assert_true(password_is_read);
2692
sigset_t parsed_sigmask;
2693
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2696
for(int signum = 1; signum < NSIG; signum++){
2697
const bool has_signal = sigismember(&parsed_sigmask, signum);
2698
if(sigismember(&fixture->orig_sigmask, signum)){
2699
g_assert_true(has_signal);
2701
g_assert_false(has_signal);
2706
__attribute__((nonnull, warn_unused_result))
2707
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2708
sigset_t *const sigmask){
2709
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2710
uintmax_t scanned_sigmask;
2711
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2714
if(sigemptyset(sigmask) != 0){
2717
for(int signum = 1; signum < NSIG; signum++){
2718
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2719
if(sigaddset(sigmask, signum) != 0){
2727
static void run_task_with_stderr_to_dev_null(const task_context task,
2728
task_queue *const queue);
2731
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2732
test_fixture *fixture,
2733
__attribute__((unused))
2734
gconstpointer user_data){
2736
bool mandos_client_exited = false;
2737
bool quit_now = false;
2739
__attribute__((cleanup(cleanup_queue)))
2740
task_queue *queue = create_queue();
2741
g_assert_nonnull(queue);
2742
const task_context task = {
2743
.func=wait_for_mandos_client_exit,
2745
.mandos_client_exited=&mandos_client_exited,
2746
.quit_now=&quit_now,
2748
run_task_with_stderr_to_dev_null(task, queue);
2750
g_assert_false(mandos_client_exited);
2751
g_assert_true(quit_now);
2752
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2755
static void run_task_with_stderr_to_dev_null(const task_context task,
2756
task_queue *const queue){
2757
FILE *real_stderr = stderr;
2758
FILE *devnull = fopen("/dev/null", "we");
2759
g_assert_nonnull(devnull);
2762
task.func(task, queue);
2763
stderr = real_stderr;
2765
g_assert_cmpint(fclose(devnull), ==, 0);
2769
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2770
__attribute__((unused))
2771
gconstpointer user_data){
2772
bool mandos_client_exited = false;
2773
bool quit_now = false;
2775
pid_t create_eternal_process(void){
2776
const pid_t pid = fork();
2777
if(pid == 0){ /* Child */
2778
if(not restore_signal_handler(&fixture->orig_sigaction)){
2779
_exit(EXIT_FAILURE);
2781
if(not restore_sigmask(&fixture->orig_sigmask)){
2782
_exit(EXIT_FAILURE);
2790
pid_t pid = create_eternal_process();
2791
g_assert_true(pid != -1);
2793
__attribute__((cleanup(cleanup_queue)))
2794
task_queue *queue = create_queue();
2795
g_assert_nonnull(queue);
2796
const task_context task = {
2797
.func=wait_for_mandos_client_exit,
2799
.mandos_client_exited=&mandos_client_exited,
2800
.quit_now=&quit_now,
2802
task.func(task, queue);
2804
g_assert_false(mandos_client_exited);
2805
g_assert_false(quit_now);
2806
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2808
g_assert_nonnull(find_matching_task(queue, (task_context){
2809
.func=wait_for_mandos_client_exit,
2811
.mandos_client_exited=&mandos_client_exited,
2812
.quit_now=&quit_now,
2817
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2818
__attribute__((unused))
2821
bool mandos_client_exited = false;
2822
bool quit_now = false;
2824
pid_t create_successful_process(void){
2825
const pid_t pid = fork();
2826
if(pid == 0){ /* Child */
2827
if(not restore_signal_handler(&fixture->orig_sigaction)){
2828
_exit(EXIT_FAILURE);
2830
if(not restore_sigmask(&fixture->orig_sigmask)){
2831
_exit(EXIT_FAILURE);
2837
const pid_t pid = create_successful_process();
2838
g_assert_true(pid != -1);
2840
__attribute__((cleanup(cleanup_queue)))
2841
task_queue *queue = create_queue();
2842
g_assert_nonnull(queue);
2843
const task_context initial_task = {
2844
.func=wait_for_mandos_client_exit,
2846
.mandos_client_exited=&mandos_client_exited,
2847
.quit_now=&quit_now,
2849
g_assert_true(add_to_queue(queue, initial_task));
2851
struct timespec starttime, currtime;
2852
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2853
__attribute__((cleanup(cleanup_close)))
2854
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2856
queue->next_run = 0;
2857
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2858
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2859
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2860
} while((not mandos_client_exited)
2862
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2864
g_assert_true(mandos_client_exited);
2865
g_assert_false(quit_now);
2866
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2870
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2871
__attribute__((unused))
2874
bool mandos_client_exited = false;
2875
bool quit_now = false;
2877
pid_t create_failing_process(void){
2878
const pid_t pid = fork();
2879
if(pid == 0){ /* Child */
2880
if(not restore_signal_handler(&fixture->orig_sigaction)){
2881
_exit(EXIT_FAILURE);
2883
if(not restore_sigmask(&fixture->orig_sigmask)){
2884
_exit(EXIT_FAILURE);
2890
const pid_t pid = create_failing_process();
2891
g_assert_true(pid != -1);
2893
__attribute__((cleanup(string_set_clear)))
2894
string_set cancelled_filenames = {};
2895
__attribute__((cleanup(cleanup_close)))
2896
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2897
g_assert_cmpint(epoll_fd, >=, 0);
2898
__attribute__((cleanup(cleanup_queue)))
2899
task_queue *queue = create_queue();
2900
g_assert_nonnull(queue);
2901
g_assert_true(add_to_queue(queue, (task_context){
2902
.func=wait_for_mandos_client_exit,
2904
.mandos_client_exited=&mandos_client_exited,
2905
.quit_now=&quit_now,
2908
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2910
__attribute__((cleanup(cleanup_close)))
2911
const int devnull_fd = open("/dev/null",
2912
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2913
g_assert_cmpint(devnull_fd, >=, 0);
2914
__attribute__((cleanup(cleanup_close)))
2915
const int real_stderr_fd = dup(STDERR_FILENO);
2916
g_assert_cmpint(real_stderr_fd, >=, 0);
2918
struct timespec starttime, currtime;
2919
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2921
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2922
dup2(devnull_fd, STDERR_FILENO);
2923
const bool success = run_queue(&queue, &cancelled_filenames,
2925
dup2(real_stderr_fd, STDERR_FILENO);
2930
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2931
} while((not mandos_client_exited)
2933
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2935
g_assert_true(quit_now);
2936
g_assert_true(mandos_client_exited);
2937
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2941
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2942
__attribute__((unused))
2943
gconstpointer user_data){
2944
bool mandos_client_exited = false;
2945
bool quit_now = false;
2947
pid_t create_killed_process(void){
2948
const pid_t pid = fork();
2949
if(pid == 0){ /* Child */
2950
if(not restore_signal_handler(&fixture->orig_sigaction)){
2951
_exit(EXIT_FAILURE);
2953
if(not restore_sigmask(&fixture->orig_sigmask)){
2954
_exit(EXIT_FAILURE);
2963
const pid_t pid = create_killed_process();
2964
g_assert_true(pid != -1);
2966
__attribute__((cleanup(string_set_clear)))
2967
string_set cancelled_filenames = {};
2968
__attribute__((cleanup(cleanup_close)))
2969
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2970
g_assert_cmpint(epoll_fd, >=, 0);
2971
__attribute__((cleanup(cleanup_queue)))
2972
task_queue *queue = create_queue();
2973
g_assert_nonnull(queue);
2974
g_assert_true(add_to_queue(queue, (task_context){
2975
.func=wait_for_mandos_client_exit,
2977
.mandos_client_exited=&mandos_client_exited,
2978
.quit_now=&quit_now,
2981
__attribute__((cleanup(cleanup_close)))
2982
const int devnull_fd = open("/dev/null",
2983
O_WRONLY | O_CLOEXEC, O_NOCTTY);
2984
g_assert_cmpint(devnull_fd, >=, 0);
2985
__attribute__((cleanup(cleanup_close)))
2986
const int real_stderr_fd = dup(STDERR_FILENO);
2987
g_assert_cmpint(real_stderr_fd, >=, 0);
2989
struct timespec starttime, currtime;
2990
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2992
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2993
dup2(devnull_fd, STDERR_FILENO);
2994
const bool success = run_queue(&queue, &cancelled_filenames,
2996
dup2(real_stderr_fd, STDERR_FILENO);
3001
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3002
} while((not mandos_client_exited)
3004
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3006
g_assert_true(mandos_client_exited);
3007
g_assert_true(quit_now);
3008
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3011
static bool epoll_set_does_not_contain(int, int);
3014
void test_read_mandos_client_output_readerror(__attribute__((unused))
3015
test_fixture *fixture,
3016
__attribute__((unused))
3019
__attribute__((cleanup(cleanup_close)))
3020
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3021
g_assert_cmpint(epoll_fd, >=, 0);
3023
__attribute__((cleanup(cleanup_buffer)))
3024
buffer password = {};
3026
/* Reading /proc/self/mem from offset 0 will always give EIO */
3027
const int fd = open("/proc/self/mem",
3028
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3030
bool password_is_read = false;
3031
bool quit_now = false;
3032
__attribute__((cleanup(cleanup_queue)))
3033
task_queue *queue = create_queue();
3034
g_assert_nonnull(queue);
3036
task_context task = {
3037
.func=read_mandos_client_output,
3040
.password=&password,
3041
.password_is_read=&password_is_read,
3042
.quit_now=&quit_now,
3044
run_task_with_stderr_to_dev_null(task, queue);
3045
g_assert_false(password_is_read);
3046
g_assert_cmpint((int)password.length, ==, 0);
3047
g_assert_true(quit_now);
3048
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3050
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3052
g_assert_cmpint(close(fd), ==, -1);
3055
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3056
return not epoll_set_contains(epoll_fd, fd, 0);
3060
void test_read_mandos_client_output_nodata(__attribute__((unused))
3061
test_fixture *fixture,
3062
__attribute__((unused))
3063
gconstpointer user_data){
3064
__attribute__((cleanup(cleanup_close)))
3065
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3066
g_assert_cmpint(epoll_fd, >=, 0);
3069
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3071
__attribute__((cleanup(cleanup_buffer)))
3072
buffer password = {};
3074
bool password_is_read = false;
3075
bool quit_now = false;
3076
__attribute__((cleanup(cleanup_queue)))
3077
task_queue *queue = create_queue();
3078
g_assert_nonnull(queue);
3080
task_context task = {
3081
.func=read_mandos_client_output,
3084
.password=&password,
3085
.password_is_read=&password_is_read,
3086
.quit_now=&quit_now,
3088
task.func(task, queue);
3089
g_assert_false(password_is_read);
3090
g_assert_cmpint((int)password.length, ==, 0);
3091
g_assert_false(quit_now);
3092
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3094
g_assert_nonnull(find_matching_task(queue, (task_context){
3095
.func=read_mandos_client_output,
3098
.password=&password,
3099
.password_is_read=&password_is_read,
3100
.quit_now=&quit_now,
3103
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3104
EPOLLIN | EPOLLRDHUP));
3106
g_assert_cmpint(close(pipefds[1]), ==, 0);
3109
static void test_read_mandos_client_output_eof(__attribute__((unused))
3110
test_fixture *fixture,
3111
__attribute__((unused))
3114
__attribute__((cleanup(cleanup_close)))
3115
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3116
g_assert_cmpint(epoll_fd, >=, 0);
3119
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3120
g_assert_cmpint(close(pipefds[1]), ==, 0);
3122
__attribute__((cleanup(cleanup_buffer)))
3123
buffer password = {};
3125
bool password_is_read = false;
3126
bool quit_now = false;
3127
__attribute__((cleanup(cleanup_queue)))
3128
task_queue *queue = create_queue();
3129
g_assert_nonnull(queue);
3131
task_context task = {
3132
.func=read_mandos_client_output,
3135
.password=&password,
3136
.password_is_read=&password_is_read,
3137
.quit_now=&quit_now,
3139
task.func(task, queue);
3140
g_assert_true(password_is_read);
3141
g_assert_cmpint((int)password.length, ==, 0);
3142
g_assert_false(quit_now);
3143
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3145
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3147
g_assert_cmpint(close(pipefds[0]), ==, -1);
3151
void test_read_mandos_client_output_once(__attribute__((unused))
3152
test_fixture *fixture,
3153
__attribute__((unused))
3154
gconstpointer user_data){
3155
__attribute__((cleanup(cleanup_close)))
3156
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3157
g_assert_cmpint(epoll_fd, >=, 0);
3160
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3162
const char dummy_test_password[] = "dummy test password";
3163
/* Start with a pre-allocated buffer */
3164
__attribute__((cleanup(cleanup_buffer)))
3166
.data=malloc(sizeof(dummy_test_password)),
3168
.allocated=sizeof(dummy_test_password),
3170
g_assert_nonnull(password.data);
3171
if(mlock(password.data, password.allocated) != 0){
3172
g_assert_true(errno == EPERM or errno == ENOMEM);
3175
bool password_is_read = false;
3176
bool quit_now = false;
3177
__attribute__((cleanup(cleanup_queue)))
3178
task_queue *queue = create_queue();
3179
g_assert_nonnull(queue);
3181
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3182
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3183
sizeof(dummy_test_password)),
3184
==, (int)sizeof(dummy_test_password));
3186
task_context task = {
3187
.func=read_mandos_client_output,
3190
.password=&password,
3191
.password_is_read=&password_is_read,
3192
.quit_now=&quit_now,
3194
task.func(task, queue);
3196
g_assert_false(password_is_read);
3197
g_assert_cmpint((int)password.length, ==,
3198
(int)sizeof(dummy_test_password));
3199
g_assert_nonnull(password.data);
3200
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3201
sizeof(dummy_test_password)), ==, 0);
3203
g_assert_false(quit_now);
3204
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3206
g_assert_nonnull(find_matching_task(queue, (task_context){
3207
.func=read_mandos_client_output,
3210
.password=&password,
3211
.password_is_read=&password_is_read,
3212
.quit_now=&quit_now,
3215
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3216
EPOLLIN | EPOLLRDHUP));
3218
g_assert_cmpint(close(pipefds[1]), ==, 0);
3222
void test_read_mandos_client_output_malloc(__attribute__((unused))
3223
test_fixture *fixture,
3224
__attribute__((unused))
3225
gconstpointer user_data){
3226
__attribute__((cleanup(cleanup_close)))
3227
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3228
g_assert_cmpint(epoll_fd, >=, 0);
3231
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3233
const char dummy_test_password[] = "dummy test password";
3234
/* Start with an empty buffer */
3235
__attribute__((cleanup(cleanup_buffer)))
3236
buffer password = {};
3238
bool password_is_read = false;
3239
bool quit_now = false;
3240
__attribute__((cleanup(cleanup_queue)))
3241
task_queue *queue = create_queue();
3242
g_assert_nonnull(queue);
3244
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3245
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3246
sizeof(dummy_test_password)),
3247
==, (int)sizeof(dummy_test_password));
3249
task_context task = {
3250
.func=read_mandos_client_output,
3253
.password=&password,
3254
.password_is_read=&password_is_read,
3255
.quit_now=&quit_now,
3257
task.func(task, queue);
3259
g_assert_false(password_is_read);
3260
g_assert_cmpint((int)password.length, ==,
3261
(int)sizeof(dummy_test_password));
3262
g_assert_nonnull(password.data);
3263
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3264
sizeof(dummy_test_password)), ==, 0);
3266
g_assert_false(quit_now);
3267
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3269
g_assert_nonnull(find_matching_task(queue, (task_context){
3270
.func=read_mandos_client_output,
3273
.password=&password,
3274
.password_is_read=&password_is_read,
3275
.quit_now=&quit_now,
3278
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3279
EPOLLIN | EPOLLRDHUP));
3281
g_assert_cmpint(close(pipefds[1]), ==, 0);
3285
void test_read_mandos_client_output_append(__attribute__((unused))
3286
test_fixture *fixture,
3287
__attribute__((unused))
3288
gconstpointer user_data){
3289
__attribute__((cleanup(cleanup_close)))
3290
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3291
g_assert_cmpint(epoll_fd, >=, 0);
3294
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3296
const char dummy_test_password[] = "dummy test password";
3297
__attribute__((cleanup(cleanup_buffer)))
3299
.data=malloc(PIPE_BUF),
3301
.allocated=PIPE_BUF,
3303
g_assert_nonnull(password.data);
3304
if(mlock(password.data, password.allocated) != 0){
3305
g_assert_true(errno == EPERM or errno == ENOMEM);
3308
memset(password.data, 'x', PIPE_BUF);
3309
char password_expected[PIPE_BUF];
3310
memcpy(password_expected, password.data, PIPE_BUF);
3312
bool password_is_read = false;
3313
bool quit_now = false;
3314
__attribute__((cleanup(cleanup_queue)))
3315
task_queue *queue = create_queue();
3316
g_assert_nonnull(queue);
3318
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3319
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3320
sizeof(dummy_test_password)),
3321
==, (int)sizeof(dummy_test_password));
3323
task_context task = {
3324
.func=read_mandos_client_output,
3327
.password=&password,
3328
.password_is_read=&password_is_read,
3329
.quit_now=&quit_now,
3331
task.func(task, queue);
3333
g_assert_false(password_is_read);
3334
g_assert_cmpint((int)password.length, ==,
3335
PIPE_BUF + sizeof(dummy_test_password));
3336
g_assert_nonnull(password.data);
3337
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3339
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3340
dummy_test_password,
3341
sizeof(dummy_test_password)), ==, 0);
3342
g_assert_false(quit_now);
3343
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3345
g_assert_nonnull(find_matching_task(queue, (task_context){
3346
.func=read_mandos_client_output,
3349
.password=&password,
3350
.password_is_read=&password_is_read,
3351
.quit_now=&quit_now,
3354
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3355
EPOLLIN | EPOLLRDHUP));
3358
static char *make_temporary_directory(void);
3360
static void test_add_inotify_dir_watch(__attribute__((unused))
3361
test_fixture *fixture,
3362
__attribute__((unused))
3363
gconstpointer user_data){
3364
__attribute__((cleanup(cleanup_close)))
3365
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3366
g_assert_cmpint(epoll_fd, >=, 0);
3367
__attribute__((cleanup(cleanup_queue)))
3368
task_queue *queue = create_queue();
3369
g_assert_nonnull(queue);
3370
__attribute__((cleanup(string_set_clear)))
3371
string_set cancelled_filenames = {};
3372
const mono_microsecs current_time = 0;
3374
bool quit_now = false;
3375
buffer password = {};
3376
bool mandos_client_exited = false;
3377
bool password_is_read = false;
3379
__attribute__((cleanup(cleanup_string)))
3380
char *tempdir = make_temporary_directory();
3381
g_assert_nonnull(tempdir);
3383
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3385
&cancelled_filenames,
3387
&mandos_client_exited,
3388
&password_is_read));
3390
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3392
const task_context *const added_read_task
3393
= find_matching_task(queue, (task_context){
3394
.func=read_inotify_event,
3396
.quit_now=&quit_now,
3397
.password=&password,
3399
.cancelled_filenames=&cancelled_filenames,
3400
.current_time=¤t_time,
3401
.mandos_client_exited=&mandos_client_exited,
3402
.password_is_read=&password_is_read,
3404
g_assert_nonnull(added_read_task);
3406
g_assert_cmpint(added_read_task->fd, >, 2);
3407
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3408
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3409
added_read_task->fd,
3410
EPOLLIN | EPOLLRDHUP));
3412
g_assert_cmpint(rmdir(tempdir), ==, 0);
3415
static char *make_temporary_directory(void){
3416
char *name = strdup("/tmp/mandosXXXXXX");
3417
g_assert_nonnull(name);
3418
char *result = mkdtemp(name);
3425
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3426
test_fixture *fixture,
3427
__attribute__((unused))
3428
gconstpointer user_data){
3429
__attribute__((cleanup(cleanup_close)))
3430
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3431
g_assert_cmpint(epoll_fd, >=, 0);
3432
__attribute__((cleanup(cleanup_queue)))
3433
task_queue *queue = create_queue();
3434
g_assert_nonnull(queue);
3435
__attribute__((cleanup(string_set_clear)))
3436
string_set cancelled_filenames = {};
3437
const mono_microsecs current_time = 0;
3439
bool quit_now = false;
3440
buffer password = {};
3441
bool mandos_client_exited = false;
3442
bool password_is_read = false;
3444
const char nonexistent_dir[] = "/nonexistent";
3446
FILE *real_stderr = stderr;
3447
FILE *devnull = fopen("/dev/null", "we");
3448
g_assert_nonnull(devnull);
3450
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3451
&password, nonexistent_dir,
3452
&cancelled_filenames,
3454
&mandos_client_exited,
3455
&password_is_read));
3456
stderr = real_stderr;
3457
g_assert_cmpint(fclose(devnull), ==, 0);
3459
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3462
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3463
test_fixture *fixture,
3464
__attribute__((unused))
3467
__attribute__((cleanup(cleanup_close)))
3468
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3469
g_assert_cmpint(epoll_fd, >=, 0);
3470
__attribute__((cleanup(cleanup_queue)))
3471
task_queue *queue = create_queue();
3472
g_assert_nonnull(queue);
3473
__attribute__((cleanup(string_set_clear)))
3474
string_set cancelled_filenames = {};
3475
const mono_microsecs current_time = 0;
3477
bool quit_now = false;
3478
buffer password = {};
3479
bool mandos_client_exited = false;
3480
bool password_is_read = false;
3482
const char not_a_directory[] = "/dev/tty";
3484
FILE *real_stderr = stderr;
3485
FILE *devnull = fopen("/dev/null", "we");
3486
g_assert_nonnull(devnull);
3488
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3489
&password, not_a_directory,
3490
&cancelled_filenames,
3492
&mandos_client_exited,
3493
&password_is_read));
3494
stderr = real_stderr;
3495
g_assert_cmpint(fclose(devnull), ==, 0);
3497
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3500
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3501
test_fixture *fixture,
3502
__attribute__((unused))
3505
__attribute__((cleanup(cleanup_close)))
3506
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3507
g_assert_cmpint(epoll_fd, >=, 0);
3508
__attribute__((cleanup(cleanup_queue)))
3509
task_queue *queue = create_queue();
3510
g_assert_nonnull(queue);
3511
__attribute__((cleanup(string_set_clear)))
3512
string_set cancelled_filenames = {};
3513
const mono_microsecs current_time = 0;
3515
bool quit_now = false;
3516
buffer password = {};
3517
bool mandos_client_exited = false;
3518
bool password_is_read = false;
3520
__attribute__((cleanup(cleanup_string)))
3521
char *tempdir = make_temporary_directory();
3522
g_assert_nonnull(tempdir);
3524
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3526
&cancelled_filenames,
3528
&mandos_client_exited,
3529
&password_is_read));
3531
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3533
const task_context *const added_read_task
3534
= find_matching_task(queue,
3535
(task_context){ .func=read_inotify_event });
3536
g_assert_nonnull(added_read_task);
3538
g_assert_cmpint(added_read_task->fd, >, 2);
3539
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3541
/* "sufficient to read at least one event." - inotify(7) */
3542
const size_t ievent_size = (sizeof(struct inotify_event)
3544
struct inotify_event *ievent = malloc(ievent_size);
3545
g_assert_nonnull(ievent);
3547
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3549
g_assert_cmpint(errno, ==, EAGAIN);
3553
g_assert_cmpint(rmdir(tempdir), ==, 0);
3556
static char *make_temporary_file_in_directory(const char
3560
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3561
test_fixture *fixture,
3562
__attribute__((unused))
3565
__attribute__((cleanup(cleanup_close)))
3566
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3567
g_assert_cmpint(epoll_fd, >=, 0);
3568
__attribute__((cleanup(cleanup_queue)))
3569
task_queue *queue = create_queue();
3570
g_assert_nonnull(queue);
3571
__attribute__((cleanup(string_set_clear)))
3572
string_set cancelled_filenames = {};
3573
const mono_microsecs current_time = 0;
3575
bool quit_now = false;
3576
buffer password = {};
3577
bool mandos_client_exited = false;
3578
bool password_is_read = false;
3580
__attribute__((cleanup(cleanup_string)))
3581
char *tempdir = make_temporary_directory();
3582
g_assert_nonnull(tempdir);
3584
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3586
&cancelled_filenames,
3588
&mandos_client_exited,
3589
&password_is_read));
3591
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3593
const task_context *const added_read_task
3594
= find_matching_task(queue,
3595
(task_context){ .func=read_inotify_event });
3596
g_assert_nonnull(added_read_task);
3598
g_assert_cmpint(added_read_task->fd, >, 2);
3599
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3601
__attribute__((cleanup(cleanup_string)))
3602
char *filename = make_temporary_file_in_directory(tempdir);
3603
g_assert_nonnull(filename);
3605
/* "sufficient to read at least one event." - inotify(7) */
3606
const size_t ievent_size = (sizeof(struct inotify_event)
3608
struct inotify_event *ievent = malloc(ievent_size);
3609
g_assert_nonnull(ievent);
3611
ssize_t read_size = 0;
3612
read_size = read(added_read_task->fd, ievent, ievent_size);
3614
g_assert_cmpint((int)read_size, >, 0);
3615
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3616
g_assert_cmpstr(ievent->name, ==, basename(filename));
3620
g_assert_cmpint(unlink(filename), ==, 0);
3621
g_assert_cmpint(rmdir(tempdir), ==, 0);
3624
static char *make_temporary_prefixed_file_in_directory(const char
3628
char *filename = NULL;
3629
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3631
g_assert_nonnull(filename);
3632
const int fd = mkostemp(filename, O_CLOEXEC);
3633
g_assert_cmpint(fd, >=, 0);
3634
g_assert_cmpint(close(fd), ==, 0);
3638
static char *make_temporary_file_in_directory(const char
3640
return make_temporary_prefixed_file_in_directory("temp", dir);
3644
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3645
test_fixture *fixture,
3646
__attribute__((unused))
3647
gconstpointer user_data){
3648
__attribute__((cleanup(cleanup_close)))
3649
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3650
g_assert_cmpint(epoll_fd, >=, 0);
3651
__attribute__((cleanup(cleanup_queue)))
3652
task_queue *queue = create_queue();
3653
g_assert_nonnull(queue);
3654
__attribute__((cleanup(string_set_clear)))
3655
string_set cancelled_filenames = {};
3656
const mono_microsecs current_time = 0;
3658
bool quit_now = false;
3659
buffer password = {};
3660
bool mandos_client_exited = false;
3661
bool password_is_read = false;
3663
__attribute__((cleanup(cleanup_string)))
3664
char *watchdir = make_temporary_directory();
3665
g_assert_nonnull(watchdir);
3667
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3668
&password, watchdir,
3669
&cancelled_filenames,
3671
&mandos_client_exited,
3672
&password_is_read));
3674
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3676
const task_context *const added_read_task
3677
= find_matching_task(queue,
3678
(task_context){ .func=read_inotify_event });
3679
g_assert_nonnull(added_read_task);
3681
g_assert_cmpint(added_read_task->fd, >, 2);
3682
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3684
char *sourcedir = make_temporary_directory();
3685
g_assert_nonnull(sourcedir);
3687
__attribute__((cleanup(cleanup_string)))
3688
char *filename = make_temporary_file_in_directory(sourcedir);
3689
g_assert_nonnull(filename);
3691
__attribute__((cleanup(cleanup_string)))
3692
char *targetfilename = NULL;
3693
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3694
basename(filename)), >, 0);
3695
g_assert_nonnull(targetfilename);
3697
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3698
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3701
/* "sufficient to read at least one event." - inotify(7) */
3702
const size_t ievent_size = (sizeof(struct inotify_event)
3704
struct inotify_event *ievent = malloc(ievent_size);
3705
g_assert_nonnull(ievent);
3707
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3709
g_assert_cmpint((int)read_size, >, 0);
3710
g_assert_true(ievent->mask & IN_MOVED_TO);
3711
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3715
g_assert_cmpint(unlink(targetfilename), ==, 0);
3716
g_assert_cmpint(rmdir(watchdir), ==, 0);
3720
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3721
test_fixture *fixture,
3722
__attribute__((unused))
3725
__attribute__((cleanup(cleanup_close)))
3726
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3727
g_assert_cmpint(epoll_fd, >=, 0);
3728
__attribute__((cleanup(cleanup_queue)))
3729
task_queue *queue = create_queue();
3730
g_assert_nonnull(queue);
3731
__attribute__((cleanup(string_set_clear)))
3732
string_set cancelled_filenames = {};
3733
const mono_microsecs current_time = 0;
3735
bool quit_now = false;
3736
buffer password = {};
3737
bool mandos_client_exited = false;
3738
bool password_is_read = false;
3740
__attribute__((cleanup(cleanup_string)))
3741
char *tempdir = make_temporary_directory();
3742
g_assert_nonnull(tempdir);
3744
__attribute__((cleanup(cleanup_string)))
3745
char *tempfilename = make_temporary_file_in_directory(tempdir);
3746
g_assert_nonnull(tempfilename);
3748
__attribute__((cleanup(cleanup_string)))
3749
char *targetdir = make_temporary_directory();
3750
g_assert_nonnull(targetdir);
3752
__attribute__((cleanup(cleanup_string)))
3753
char *targetfilename = NULL;
3754
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3755
basename(tempfilename)), >, 0);
3756
g_assert_nonnull(targetfilename);
3758
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3760
&cancelled_filenames,
3762
&mandos_client_exited,
3763
&password_is_read));
3765
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3767
const task_context *const added_read_task
3768
= find_matching_task(queue,
3769
(task_context){ .func=read_inotify_event });
3770
g_assert_nonnull(added_read_task);
3772
/* "sufficient to read at least one event." - inotify(7) */
3773
const size_t ievent_size = (sizeof(struct inotify_event)
3775
struct inotify_event *ievent = malloc(ievent_size);
3776
g_assert_nonnull(ievent);
3778
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3780
g_assert_cmpint((int)read_size, >, 0);
3781
g_assert_true(ievent->mask & IN_MOVED_FROM);
3782
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3786
g_assert_cmpint(unlink(targetfilename), ==, 0);
3787
g_assert_cmpint(rmdir(targetdir), ==, 0);
3788
g_assert_cmpint(rmdir(tempdir), ==, 0);
3792
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3793
test_fixture *fixture,
3794
__attribute__((unused))
3795
gconstpointer user_data){
3796
__attribute__((cleanup(cleanup_close)))
3797
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3798
g_assert_cmpint(epoll_fd, >=, 0);
3799
__attribute__((cleanup(cleanup_queue)))
3800
task_queue *queue = create_queue();
3801
g_assert_nonnull(queue);
3802
__attribute__((cleanup(string_set_clear)))
3803
string_set cancelled_filenames = {};
3804
const mono_microsecs current_time = 0;
3806
bool quit_now = false;
3807
buffer password = {};
3808
bool mandos_client_exited = false;
3809
bool password_is_read = false;
3811
__attribute__((cleanup(cleanup_string)))
3812
char *tempdir = make_temporary_directory();
3813
g_assert_nonnull(tempdir);
3815
__attribute__((cleanup(cleanup_string)))
3816
char *tempfile = make_temporary_file_in_directory(tempdir);
3817
g_assert_nonnull(tempfile);
3819
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3821
&cancelled_filenames,
3823
&mandos_client_exited,
3824
&password_is_read));
3825
g_assert_cmpint(unlink(tempfile), ==, 0);
3827
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3829
const task_context *const added_read_task
3830
= find_matching_task(queue,
3831
(task_context){ .func=read_inotify_event });
3832
g_assert_nonnull(added_read_task);
3834
g_assert_cmpint(added_read_task->fd, >, 2);
3835
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3837
/* "sufficient to read at least one event." - inotify(7) */
3838
const size_t ievent_size = (sizeof(struct inotify_event)
3840
struct inotify_event *ievent = malloc(ievent_size);
3841
g_assert_nonnull(ievent);
3843
ssize_t read_size = 0;
3844
read_size = read(added_read_task->fd, ievent, ievent_size);
3846
g_assert_cmpint((int)read_size, >, 0);
3847
g_assert_true(ievent->mask & IN_DELETE);
3848
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3852
g_assert_cmpint(rmdir(tempdir), ==, 0);
3856
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3857
test_fixture *fixture,
3858
__attribute__((unused))
3861
__attribute__((cleanup(cleanup_close)))
3862
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3863
g_assert_cmpint(epoll_fd, >=, 0);
3864
__attribute__((cleanup(cleanup_queue)))
3865
task_queue *queue = create_queue();
3866
g_assert_nonnull(queue);
3867
__attribute__((cleanup(string_set_clear)))
3868
string_set cancelled_filenames = {};
3869
const mono_microsecs current_time = 0;
3871
bool quit_now = false;
3872
buffer password = {};
3873
bool mandos_client_exited = false;
3874
bool password_is_read = false;
3876
__attribute__((cleanup(cleanup_string)))
3877
char *tempdir = make_temporary_directory();
3878
g_assert_nonnull(tempdir);
3880
__attribute__((cleanup(cleanup_string)))
3881
char *tempfile = make_temporary_file_in_directory(tempdir);
3882
g_assert_nonnull(tempfile);
3883
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3885
g_assert_cmpint(tempfile_fd, >, 2);
3887
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3889
&cancelled_filenames,
3891
&mandos_client_exited,
3892
&password_is_read));
3893
g_assert_cmpint(unlink(tempfile), ==, 0);
3895
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3897
const task_context *const added_read_task
3898
= find_matching_task(queue,
3899
(task_context){ .func=read_inotify_event });
3900
g_assert_nonnull(added_read_task);
3902
g_assert_cmpint(added_read_task->fd, >, 2);
3903
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3905
/* "sufficient to read at least one event." - inotify(7) */
3906
const size_t ievent_size = (sizeof(struct inotify_event)
3908
struct inotify_event *ievent = malloc(ievent_size);
3909
g_assert_nonnull(ievent);
3911
ssize_t read_size = 0;
3912
read_size = read(added_read_task->fd, ievent, ievent_size);
3914
g_assert_cmpint((int)read_size, >, 0);
3915
g_assert_true(ievent->mask & IN_DELETE);
3916
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3918
g_assert_cmpint(close(tempfile_fd), ==, 0);
3920
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3921
file not appear as an ievent, so we should not see it now. */
3922
read_size = read(added_read_task->fd, ievent, ievent_size);
3923
g_assert_cmpint((int)read_size, ==, -1);
3924
g_assert_true(errno == EAGAIN);
3928
g_assert_cmpint(rmdir(tempdir), ==, 0);
3931
static void test_read_inotify_event_readerror(__attribute__((unused))
3932
test_fixture *fixture,
3933
__attribute__((unused))
3936
__attribute__((cleanup(cleanup_close)))
3937
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3938
g_assert_cmpint(epoll_fd, >=, 0);
3939
const mono_microsecs current_time = 0;
3941
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3942
const int fd = open("/proc/self/mem",
3943
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3945
bool quit_now = false;
3946
__attribute__((cleanup(cleanup_queue)))
3947
task_queue *queue = create_queue();
3948
g_assert_nonnull(queue);
3950
task_context task = {
3951
.func=read_inotify_event,
3954
.quit_now=&quit_now,
3955
.filename=strdup("/nonexistent"),
3956
.cancelled_filenames = &(string_set){},
3958
.current_time=¤t_time,
3960
g_assert_nonnull(task.filename);
3961
run_task_with_stderr_to_dev_null(task, queue);
3962
g_assert_true(quit_now);
3963
g_assert_true(queue->next_run == 0);
3964
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3966
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3968
g_assert_cmpint(close(fd), ==, -1);
3971
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3972
test_fixture *fixture,
3973
__attribute__((unused))
3976
const mono_microsecs current_time = 17;
3979
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3980
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3982
bool quit_now = false;
3983
buffer password = {};
3984
bool mandos_client_exited = false;
3985
bool password_is_read = false;
3986
__attribute__((cleanup(cleanup_queue)))
3987
task_queue *queue = create_queue();
3988
g_assert_nonnull(queue);
3990
task_context task = {
3991
.func=read_inotify_event,
3994
.quit_now=&quit_now,
3995
.password=&password,
3996
.filename=strdup("/nonexistent"),
3997
.cancelled_filenames = &(string_set){},
3999
.current_time=¤t_time,
4000
.mandos_client_exited=&mandos_client_exited,
4001
.password_is_read=&password_is_read,
4003
g_assert_nonnull(task.filename);
4004
run_task_with_stderr_to_dev_null(task, queue);
4006
g_assert_nonnull(find_matching_task(queue, task));
4007
g_assert_true(queue->next_run == 1000000 + current_time);
4009
g_assert_cmpint(close(pipefds[0]), ==, 0);
4010
g_assert_cmpint(close(pipefds[1]), ==, 0);
4013
static void test_read_inotify_event_nodata(__attribute__((unused))
4014
test_fixture *fixture,
4015
__attribute__((unused))
4016
gconstpointer user_data){
4017
__attribute__((cleanup(cleanup_close)))
4018
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4019
g_assert_cmpint(epoll_fd, >=, 0);
4020
const mono_microsecs current_time = 0;
4023
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4025
bool quit_now = false;
4026
buffer password = {};
4027
bool mandos_client_exited = false;
4028
bool password_is_read = false;
4029
__attribute__((cleanup(cleanup_queue)))
4030
task_queue *queue = create_queue();
4031
g_assert_nonnull(queue);
4033
task_context task = {
4034
.func=read_inotify_event,
4037
.quit_now=&quit_now,
4038
.password=&password,
4039
.filename=strdup("/nonexistent"),
4040
.cancelled_filenames = &(string_set){},
4042
.current_time=¤t_time,
4043
.mandos_client_exited=&mandos_client_exited,
4044
.password_is_read=&password_is_read,
4046
g_assert_nonnull(task.filename);
4047
task.func(task, queue);
4048
g_assert_false(quit_now);
4049
g_assert_true(queue->next_run == 0);
4050
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4052
g_assert_nonnull(find_matching_task(queue, (task_context){
4053
.func=read_inotify_event,
4056
.quit_now=&quit_now,
4057
.password=&password,
4058
.filename=task.filename,
4059
.cancelled_filenames=task.cancelled_filenames,
4060
.current_time=¤t_time,
4061
.mandos_client_exited=&mandos_client_exited,
4062
.password_is_read=&password_is_read,
4065
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4066
EPOLLIN | EPOLLRDHUP));
4068
g_assert_cmpint(close(pipefds[1]), ==, 0);
4071
static void test_read_inotify_event_eof(__attribute__((unused))
4072
test_fixture *fixture,
4073
__attribute__((unused))
4074
gconstpointer user_data){
4075
__attribute__((cleanup(cleanup_close)))
4076
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4077
g_assert_cmpint(epoll_fd, >=, 0);
4078
const mono_microsecs current_time = 0;
4081
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4082
g_assert_cmpint(close(pipefds[1]), ==, 0);
4084
bool quit_now = false;
4085
buffer password = {};
4086
__attribute__((cleanup(cleanup_queue)))
4087
task_queue *queue = create_queue();
4088
g_assert_nonnull(queue);
4090
task_context task = {
4091
.func=read_inotify_event,
4094
.quit_now=&quit_now,
4095
.password=&password,
4096
.filename=strdup("/nonexistent"),
4097
.cancelled_filenames = &(string_set){},
4099
.current_time=¤t_time,
4101
run_task_with_stderr_to_dev_null(task, queue);
4102
g_assert_true(quit_now);
4103
g_assert_true(queue->next_run == 0);
4104
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4106
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4108
g_assert_cmpint(close(pipefds[0]), ==, -1);
4112
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4113
test_fixture *fixture,
4114
__attribute__((unused))
4115
gconstpointer user_data){
4116
__attribute__((cleanup(cleanup_close)))
4117
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4118
g_assert_cmpint(epoll_fd, >=, 0);
4119
const mono_microsecs current_time = 0;
4122
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4124
/* "sufficient to read at least one event." - inotify(7) */
4125
const size_t ievent_max_size = (sizeof(struct inotify_event)
4127
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4129
struct inotify_event event;
4130
char name_buffer[NAME_MAX + 1];
4132
struct inotify_event *const ievent = &ievent_buffer.event;
4134
const char dummy_file_name[] = "ask.dummy_file_name";
4135
ievent->mask = IN_CLOSE_WRITE;
4136
ievent->len = sizeof(dummy_file_name);
4137
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4138
const size_t ievent_size = (sizeof(struct inotify_event)
4139
+ sizeof(dummy_file_name));
4140
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4142
g_assert_cmpint(close(pipefds[1]), ==, 0);
4144
bool quit_now = false;
4145
buffer password = {};
4146
bool mandos_client_exited = false;
4147
bool password_is_read = false;
4148
__attribute__((cleanup(cleanup_queue)))
4149
task_queue *queue = create_queue();
4150
g_assert_nonnull(queue);
4152
task_context task = {
4153
.func=read_inotify_event,
4156
.quit_now=&quit_now,
4157
.password=&password,
4158
.filename=strdup("/nonexistent"),
4159
.cancelled_filenames = &(string_set){},
4161
.current_time=¤t_time,
4162
.mandos_client_exited=&mandos_client_exited,
4163
.password_is_read=&password_is_read,
4165
task.func(task, queue);
4166
g_assert_false(quit_now);
4167
g_assert_true(queue->next_run != 0);
4168
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4170
g_assert_nonnull(find_matching_task(queue, (task_context){
4171
.func=read_inotify_event,
4174
.quit_now=&quit_now,
4175
.password=&password,
4176
.filename=task.filename,
4177
.cancelled_filenames=task.cancelled_filenames,
4178
.current_time=¤t_time,
4179
.mandos_client_exited=&mandos_client_exited,
4180
.password_is_read=&password_is_read,
4183
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4184
EPOLLIN | EPOLLRDHUP));
4186
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4188
__attribute__((cleanup(cleanup_string)))
4189
char *filename = NULL;
4190
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4191
dummy_file_name), >, 0);
4192
g_assert_nonnull(filename);
4193
g_assert_nonnull(find_matching_task(queue, (task_context){
4194
.func=open_and_parse_question,
4197
.question_filename=filename,
4198
.password=&password,
4199
.cancelled_filenames=task.cancelled_filenames,
4200
.current_time=¤t_time,
4201
.mandos_client_exited=&mandos_client_exited,
4202
.password_is_read=&password_is_read,
4207
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4208
test_fixture *fixture,
4209
__attribute__((unused))
4210
gconstpointer user_data){
4211
__attribute__((cleanup(cleanup_close)))
4212
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4213
g_assert_cmpint(epoll_fd, >=, 0);
4214
const mono_microsecs current_time = 0;
4217
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4219
/* "sufficient to read at least one event." - inotify(7) */
4220
const size_t ievent_max_size = (sizeof(struct inotify_event)
4222
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4224
struct inotify_event event;
4225
char name_buffer[NAME_MAX + 1];
4227
struct inotify_event *const ievent = &ievent_buffer.event;
4229
const char dummy_file_name[] = "ask.dummy_file_name";
4230
ievent->mask = IN_MOVED_TO;
4231
ievent->len = sizeof(dummy_file_name);
4232
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4233
const size_t ievent_size = (sizeof(struct inotify_event)
4234
+ sizeof(dummy_file_name));
4235
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4237
g_assert_cmpint(close(pipefds[1]), ==, 0);
4239
bool quit_now = false;
4240
buffer password = {};
4241
bool mandos_client_exited = false;
4242
bool password_is_read = false;
4243
__attribute__((cleanup(cleanup_queue)))
4244
task_queue *queue = create_queue();
4245
g_assert_nonnull(queue);
4247
task_context task = {
4248
.func=read_inotify_event,
4251
.quit_now=&quit_now,
4252
.password=&password,
4253
.filename=strdup("/nonexistent"),
4254
.cancelled_filenames = &(string_set){},
4256
.current_time=¤t_time,
4257
.mandos_client_exited=&mandos_client_exited,
4258
.password_is_read=&password_is_read,
4260
task.func(task, queue);
4261
g_assert_false(quit_now);
4262
g_assert_true(queue->next_run != 0);
4263
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4265
g_assert_nonnull(find_matching_task(queue, (task_context){
4266
.func=read_inotify_event,
4269
.quit_now=&quit_now,
4270
.password=&password,
4271
.filename=task.filename,
4272
.cancelled_filenames=task.cancelled_filenames,
4273
.current_time=¤t_time,
4274
.mandos_client_exited=&mandos_client_exited,
4275
.password_is_read=&password_is_read,
4278
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4279
EPOLLIN | EPOLLRDHUP));
4281
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4283
__attribute__((cleanup(cleanup_string)))
4284
char *filename = NULL;
4285
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4286
dummy_file_name), >, 0);
4287
g_assert_nonnull(filename);
4288
g_assert_nonnull(find_matching_task(queue, (task_context){
4289
.func=open_and_parse_question,
4292
.question_filename=filename,
4293
.password=&password,
4294
.cancelled_filenames=task.cancelled_filenames,
4295
.current_time=¤t_time,
4296
.mandos_client_exited=&mandos_client_exited,
4297
.password_is_read=&password_is_read,
4302
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4303
test_fixture *fixture,
4304
__attribute__((unused))
4305
gconstpointer user_data){
4306
__attribute__((cleanup(cleanup_close)))
4307
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4308
g_assert_cmpint(epoll_fd, >=, 0);
4309
__attribute__((cleanup(string_set_clear)))
4310
string_set cancelled_filenames = {};
4311
const mono_microsecs current_time = 0;
4314
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4316
/* "sufficient to read at least one event." - inotify(7) */
4317
const size_t ievent_max_size = (sizeof(struct inotify_event)
4319
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4321
struct inotify_event event;
4322
char name_buffer[NAME_MAX + 1];
4324
struct inotify_event *const ievent = &ievent_buffer.event;
4326
const char dummy_file_name[] = "ask.dummy_file_name";
4327
ievent->mask = IN_MOVED_FROM;
4328
ievent->len = sizeof(dummy_file_name);
4329
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4330
const size_t ievent_size = (sizeof(struct inotify_event)
4331
+ sizeof(dummy_file_name));
4332
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4334
g_assert_cmpint(close(pipefds[1]), ==, 0);
4336
bool quit_now = false;
4337
buffer password = {};
4338
bool mandos_client_exited = false;
4339
bool password_is_read = false;
4340
__attribute__((cleanup(cleanup_queue)))
4341
task_queue *queue = create_queue();
4342
g_assert_nonnull(queue);
4344
task_context task = {
4345
.func=read_inotify_event,
4348
.quit_now=&quit_now,
4349
.password=&password,
4350
.filename=strdup("/nonexistent"),
4351
.cancelled_filenames=&cancelled_filenames,
4352
.current_time=¤t_time,
4353
.mandos_client_exited=&mandos_client_exited,
4354
.password_is_read=&password_is_read,
4356
task.func(task, queue);
4357
g_assert_false(quit_now);
4358
g_assert_true(queue->next_run == 0);
4359
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4361
g_assert_nonnull(find_matching_task(queue, (task_context){
4362
.func=read_inotify_event,
4365
.quit_now=&quit_now,
4366
.password=&password,
4367
.filename=task.filename,
4368
.cancelled_filenames=&cancelled_filenames,
4369
.current_time=¤t_time,
4370
.mandos_client_exited=&mandos_client_exited,
4371
.password_is_read=&password_is_read,
4374
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4375
EPOLLIN | EPOLLRDHUP));
4377
__attribute__((cleanup(cleanup_string)))
4378
char *filename = NULL;
4379
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4380
dummy_file_name), >, 0);
4381
g_assert_nonnull(filename);
4382
g_assert_true(string_set_contains(*task.cancelled_filenames,
4386
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4387
test_fixture *fixture,
4388
__attribute__((unused))
4391
__attribute__((cleanup(cleanup_close)))
4392
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4393
g_assert_cmpint(epoll_fd, >=, 0);
4394
__attribute__((cleanup(string_set_clear)))
4395
string_set cancelled_filenames = {};
4396
const mono_microsecs current_time = 0;
4399
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4401
/* "sufficient to read at least one event." - inotify(7) */
4402
const size_t ievent_max_size = (sizeof(struct inotify_event)
4404
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4406
struct inotify_event event;
4407
char name_buffer[NAME_MAX + 1];
4409
struct inotify_event *const ievent = &ievent_buffer.event;
4411
const char dummy_file_name[] = "ask.dummy_file_name";
4412
ievent->mask = IN_DELETE;
4413
ievent->len = sizeof(dummy_file_name);
4414
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4415
const size_t ievent_size = (sizeof(struct inotify_event)
4416
+ sizeof(dummy_file_name));
4417
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4419
g_assert_cmpint(close(pipefds[1]), ==, 0);
4421
bool quit_now = false;
4422
buffer password = {};
4423
bool mandos_client_exited = false;
4424
bool password_is_read = false;
4425
__attribute__((cleanup(cleanup_queue)))
4426
task_queue *queue = create_queue();
4427
g_assert_nonnull(queue);
4429
task_context task = {
4430
.func=read_inotify_event,
4433
.quit_now=&quit_now,
4434
.password=&password,
4435
.filename=strdup("/nonexistent"),
4436
.cancelled_filenames=&cancelled_filenames,
4437
.current_time=¤t_time,
4438
.mandos_client_exited=&mandos_client_exited,
4439
.password_is_read=&password_is_read,
4441
task.func(task, queue);
4442
g_assert_false(quit_now);
4443
g_assert_true(queue->next_run == 0);
4444
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4446
g_assert_nonnull(find_matching_task(queue, (task_context){
4447
.func=read_inotify_event,
4450
.quit_now=&quit_now,
4451
.password=&password,
4452
.filename=task.filename,
4453
.cancelled_filenames=&cancelled_filenames,
4454
.current_time=¤t_time,
4455
.mandos_client_exited=&mandos_client_exited,
4456
.password_is_read=&password_is_read,
4459
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4460
EPOLLIN | EPOLLRDHUP));
4462
__attribute__((cleanup(cleanup_string)))
4463
char *filename = NULL;
4464
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4465
dummy_file_name), >, 0);
4466
g_assert_nonnull(filename);
4467
g_assert_true(string_set_contains(*task.cancelled_filenames,
4472
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4473
test_fixture *fixture,
4474
__attribute__((unused))
4477
__attribute__((cleanup(cleanup_close)))
4478
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4479
g_assert_cmpint(epoll_fd, >=, 0);
4480
const mono_microsecs current_time = 0;
4483
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4485
/* "sufficient to read at least one event." - inotify(7) */
4486
const size_t ievent_max_size = (sizeof(struct inotify_event)
4488
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4490
struct inotify_event event;
4491
char name_buffer[NAME_MAX + 1];
4493
struct inotify_event *const ievent = &ievent_buffer.event;
4495
const char dummy_file_name[] = "ignored.dummy_file_name";
4496
ievent->mask = IN_CLOSE_WRITE;
4497
ievent->len = sizeof(dummy_file_name);
4498
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4499
const size_t ievent_size = (sizeof(struct inotify_event)
4500
+ sizeof(dummy_file_name));
4501
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4503
g_assert_cmpint(close(pipefds[1]), ==, 0);
4505
bool quit_now = false;
4506
buffer password = {};
4507
bool mandos_client_exited = false;
4508
bool password_is_read = false;
4509
__attribute__((cleanup(cleanup_queue)))
4510
task_queue *queue = create_queue();
4511
g_assert_nonnull(queue);
4513
task_context task = {
4514
.func=read_inotify_event,
4517
.quit_now=&quit_now,
4518
.password=&password,
4519
.filename=strdup("/nonexistent"),
4520
.cancelled_filenames = &(string_set){},
4522
.current_time=¤t_time,
4523
.mandos_client_exited=&mandos_client_exited,
4524
.password_is_read=&password_is_read,
4526
task.func(task, queue);
4527
g_assert_false(quit_now);
4528
g_assert_true(queue->next_run == 0);
4529
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4531
g_assert_nonnull(find_matching_task(queue, (task_context){
4532
.func=read_inotify_event,
4535
.quit_now=&quit_now,
4536
.password=&password,
4537
.filename=task.filename,
4538
.cancelled_filenames=task.cancelled_filenames,
4539
.current_time=¤t_time,
4540
.mandos_client_exited=&mandos_client_exited,
4541
.password_is_read=&password_is_read,
4544
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4545
EPOLLIN | EPOLLRDHUP));
4549
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4550
test_fixture *fixture,
4551
__attribute__((unused))
4552
gconstpointer user_data){
4553
__attribute__((cleanup(cleanup_close)))
4554
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4555
g_assert_cmpint(epoll_fd, >=, 0);
4556
const mono_microsecs current_time = 0;
4559
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4561
/* "sufficient to read at least one event." - inotify(7) */
4562
const size_t ievent_max_size = (sizeof(struct inotify_event)
4564
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4566
struct inotify_event event;
4567
char name_buffer[NAME_MAX + 1];
4569
struct inotify_event *const ievent = &ievent_buffer.event;
4571
const char dummy_file_name[] = "ignored.dummy_file_name";
4572
ievent->mask = IN_MOVED_TO;
4573
ievent->len = sizeof(dummy_file_name);
4574
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4575
const size_t ievent_size = (sizeof(struct inotify_event)
4576
+ sizeof(dummy_file_name));
4577
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4579
g_assert_cmpint(close(pipefds[1]), ==, 0);
4581
bool quit_now = false;
4582
buffer password = {};
4583
bool mandos_client_exited = false;
4584
bool password_is_read = false;
4585
__attribute__((cleanup(cleanup_queue)))
4586
task_queue *queue = create_queue();
4587
g_assert_nonnull(queue);
4589
task_context task = {
4590
.func=read_inotify_event,
4593
.quit_now=&quit_now,
4594
.password=&password,
4595
.filename=strdup("/nonexistent"),
4596
.cancelled_filenames = &(string_set){},
4598
.current_time=¤t_time,
4599
.mandos_client_exited=&mandos_client_exited,
4600
.password_is_read=&password_is_read,
4602
task.func(task, queue);
4603
g_assert_false(quit_now);
4604
g_assert_true(queue->next_run == 0);
4605
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4607
g_assert_nonnull(find_matching_task(queue, (task_context){
4608
.func=read_inotify_event,
4611
.quit_now=&quit_now,
4612
.password=&password,
4613
.filename=task.filename,
4614
.cancelled_filenames=task.cancelled_filenames,
4615
.current_time=¤t_time,
4616
.mandos_client_exited=&mandos_client_exited,
4617
.password_is_read=&password_is_read,
4620
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4621
EPOLLIN | EPOLLRDHUP));
4625
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4626
test_fixture *fixture,
4627
__attribute__((unused))
4630
__attribute__((cleanup(cleanup_close)))
4631
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4632
g_assert_cmpint(epoll_fd, >=, 0);
4633
__attribute__((cleanup(string_set_clear)))
4634
string_set cancelled_filenames = {};
4635
const mono_microsecs current_time = 0;
4638
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4640
/* "sufficient to read at least one event." - inotify(7) */
4641
const size_t ievent_max_size = (sizeof(struct inotify_event)
4643
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4645
struct inotify_event event;
4646
char name_buffer[NAME_MAX + 1];
4648
struct inotify_event *const ievent = &ievent_buffer.event;
4650
const char dummy_file_name[] = "ignored.dummy_file_name";
4651
ievent->mask = IN_MOVED_FROM;
4652
ievent->len = sizeof(dummy_file_name);
4653
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4654
const size_t ievent_size = (sizeof(struct inotify_event)
4655
+ sizeof(dummy_file_name));
4656
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4658
g_assert_cmpint(close(pipefds[1]), ==, 0);
4660
bool quit_now = false;
4661
buffer password = {};
4662
bool mandos_client_exited = false;
4663
bool password_is_read = false;
4664
__attribute__((cleanup(cleanup_queue)))
4665
task_queue *queue = create_queue();
4666
g_assert_nonnull(queue);
4668
task_context task = {
4669
.func=read_inotify_event,
4672
.quit_now=&quit_now,
4673
.password=&password,
4674
.filename=strdup("/nonexistent"),
4675
.cancelled_filenames=&cancelled_filenames,
4676
.current_time=¤t_time,
4677
.mandos_client_exited=&mandos_client_exited,
4678
.password_is_read=&password_is_read,
4680
task.func(task, queue);
4681
g_assert_false(quit_now);
4682
g_assert_true(queue->next_run == 0);
4683
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4685
g_assert_nonnull(find_matching_task(queue, (task_context){
4686
.func=read_inotify_event,
4689
.quit_now=&quit_now,
4690
.password=&password,
4691
.filename=task.filename,
4692
.cancelled_filenames=&cancelled_filenames,
4693
.current_time=¤t_time,
4694
.mandos_client_exited=&mandos_client_exited,
4695
.password_is_read=&password_is_read,
4698
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4699
EPOLLIN | EPOLLRDHUP));
4701
__attribute__((cleanup(cleanup_string)))
4702
char *filename = NULL;
4703
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4704
dummy_file_name), >, 0);
4705
g_assert_nonnull(filename);
4706
g_assert_false(string_set_contains(cancelled_filenames, filename));
4710
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4711
test_fixture *fixture,
4712
__attribute__((unused))
4715
__attribute__((cleanup(cleanup_close)))
4716
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4717
g_assert_cmpint(epoll_fd, >=, 0);
4718
__attribute__((cleanup(string_set_clear)))
4719
string_set cancelled_filenames = {};
4720
const mono_microsecs current_time = 0;
4723
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4725
/* "sufficient to read at least one event." - inotify(7) */
4726
const size_t ievent_max_size = (sizeof(struct inotify_event)
4728
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4730
struct inotify_event event;
4731
char name_buffer[NAME_MAX + 1];
4733
struct inotify_event *const ievent = &ievent_buffer.event;
4735
const char dummy_file_name[] = "ignored.dummy_file_name";
4736
ievent->mask = IN_DELETE;
4737
ievent->len = sizeof(dummy_file_name);
4738
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4739
const size_t ievent_size = (sizeof(struct inotify_event)
4740
+ sizeof(dummy_file_name));
4741
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4743
g_assert_cmpint(close(pipefds[1]), ==, 0);
4745
bool quit_now = false;
4746
buffer password = {};
4747
bool mandos_client_exited = false;
4748
bool password_is_read = false;
4749
__attribute__((cleanup(cleanup_queue)))
4750
task_queue *queue = create_queue();
4751
g_assert_nonnull(queue);
4753
task_context task = {
4754
.func=read_inotify_event,
4757
.quit_now=&quit_now,
4758
.password=&password,
4759
.filename=strdup("/nonexistent"),
4760
.cancelled_filenames=&cancelled_filenames,
4761
.current_time=¤t_time,
4762
.mandos_client_exited=&mandos_client_exited,
4763
.password_is_read=&password_is_read,
4765
task.func(task, queue);
4766
g_assert_false(quit_now);
4767
g_assert_true(queue->next_run == 0);
4768
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4770
g_assert_nonnull(find_matching_task(queue, (task_context){
4771
.func=read_inotify_event,
4774
.quit_now=&quit_now,
4775
.password=&password,
4776
.filename=task.filename,
4777
.cancelled_filenames=&cancelled_filenames,
4778
.current_time=¤t_time,
4779
.mandos_client_exited=&mandos_client_exited,
4780
.password_is_read=&password_is_read,
4783
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4784
EPOLLIN | EPOLLRDHUP));
4786
__attribute__((cleanup(cleanup_string)))
4787
char *filename = NULL;
4788
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4789
dummy_file_name), >, 0);
4790
g_assert_nonnull(filename);
4791
g_assert_false(string_set_contains(cancelled_filenames, filename));
4795
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4796
test_fixture *fixture,
4797
__attribute__((unused))
4798
gconstpointer user_data){
4799
__attribute__((cleanup(cleanup_close)))
4800
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4801
g_assert_cmpint(epoll_fd, >=, 0);
4802
__attribute__((cleanup(string_set_clear)))
4803
string_set cancelled_filenames = {};
4804
bool mandos_client_exited = false;
4805
bool password_is_read = false;
4806
__attribute__((cleanup(cleanup_queue)))
4807
task_queue *queue = create_queue();
4808
g_assert_nonnull(queue);
4810
char *const filename = strdup("/nonexistent");
4811
g_assert_nonnull(filename);
4812
task_context task = {
4813
.func=open_and_parse_question,
4814
.question_filename=filename,
4816
.password=(buffer[]){{}},
4818
.cancelled_filenames=&cancelled_filenames,
4819
.current_time=(mono_microsecs[]){0},
4820
.mandos_client_exited=&mandos_client_exited,
4821
.password_is_read=&password_is_read,
4823
task.func(task, queue);
4824
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4827
static void test_open_and_parse_question_EIO(__attribute__((unused))
4828
test_fixture *fixture,
4829
__attribute__((unused))
4830
gconstpointer user_data){
4831
__attribute__((cleanup(cleanup_close)))
4832
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4833
g_assert_cmpint(epoll_fd, >=, 0);
4834
__attribute__((cleanup(string_set_clear)))
4835
string_set cancelled_filenames = {};
4836
buffer password = {};
4837
bool mandos_client_exited = false;
4838
bool password_is_read = false;
4839
__attribute__((cleanup(cleanup_queue)))
4840
task_queue *queue = create_queue();
4841
g_assert_nonnull(queue);
4842
const mono_microsecs current_time = 0;
4844
char *filename = strdup("/proc/self/mem");
4845
task_context task = {
4846
.func=open_and_parse_question,
4847
.question_filename=filename,
4849
.password=&password,
4851
.cancelled_filenames=&cancelled_filenames,
4852
.current_time=¤t_time,
4853
.mandos_client_exited=&mandos_client_exited,
4854
.password_is_read=&password_is_read,
4856
run_task_with_stderr_to_dev_null(task, queue);
4857
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4861
test_open_and_parse_question_parse_error(__attribute__((unused))
4862
test_fixture *fixture,
4863
__attribute__((unused))
4864
gconstpointer user_data){
4865
__attribute__((cleanup(cleanup_close)))
4866
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4867
g_assert_cmpint(epoll_fd, >=, 0);
4868
__attribute__((cleanup(string_set_clear)))
4869
string_set cancelled_filenames = {};
4870
__attribute__((cleanup(cleanup_queue)))
4871
task_queue *queue = create_queue();
4872
g_assert_nonnull(queue);
4874
__attribute__((cleanup(cleanup_string)))
4875
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4876
g_assert_nonnull(tempfilename);
4877
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4878
g_assert_cmpint(tempfile, >, 0);
4879
const char bad_data[] = "this is bad syntax\n";
4880
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4881
==, sizeof(bad_data));
4882
g_assert_cmpint(close(tempfile), ==, 0);
4884
char *const filename = strdup(tempfilename);
4885
g_assert_nonnull(filename);
4886
task_context task = {
4887
.func=open_and_parse_question,
4888
.question_filename=filename,
4890
.password=(buffer[]){{}},
4892
.cancelled_filenames=&cancelled_filenames,
4893
.current_time=(mono_microsecs[]){0},
4894
.mandos_client_exited=(bool[]){false},
4895
.password_is_read=(bool[]){false},
4897
run_task_with_stderr_to_dev_null(task, queue);
4899
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4901
g_assert_cmpint(unlink(tempfilename), ==, 0);
4905
void test_open_and_parse_question_nosocket(__attribute__((unused))
4906
test_fixture *fixture,
4907
__attribute__((unused))
4908
gconstpointer user_data){
4909
__attribute__((cleanup(cleanup_close)))
4910
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4911
g_assert_cmpint(epoll_fd, >=, 0);
4912
__attribute__((cleanup(string_set_clear)))
4913
string_set cancelled_filenames = {};
4914
__attribute__((cleanup(cleanup_queue)))
4915
task_queue *queue = create_queue();
4916
g_assert_nonnull(queue);
4918
__attribute__((cleanup(cleanup_string)))
4919
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4920
g_assert_nonnull(tempfilename);
4921
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4922
g_assert_cmpint(questionfile, >, 0);
4923
FILE *qf = fdopen(questionfile, "w");
4924
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4925
g_assert_cmpint(fclose(qf), ==, 0);
4927
char *const filename = strdup(tempfilename);
4928
g_assert_nonnull(filename);
4929
task_context task = {
4930
.func=open_and_parse_question,
4931
.question_filename=filename,
4933
.password=(buffer[]){{}},
4935
.cancelled_filenames=&cancelled_filenames,
4936
.current_time=(mono_microsecs[]){0},
4937
.mandos_client_exited=(bool[]){false},
4938
.password_is_read=(bool[]){false},
4940
run_task_with_stderr_to_dev_null(task, queue);
4941
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4943
g_assert_cmpint(unlink(tempfilename), ==, 0);
4947
void test_open_and_parse_question_badsocket(__attribute__((unused))
4948
test_fixture *fixture,
4949
__attribute__((unused))
4950
gconstpointer user_data){
4951
__attribute__((cleanup(cleanup_close)))
4952
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4953
g_assert_cmpint(epoll_fd, >=, 0);
4954
__attribute__((cleanup(string_set_clear)))
4955
string_set cancelled_filenames = {};
4956
__attribute__((cleanup(cleanup_queue)))
4957
task_queue *queue = create_queue();
4958
g_assert_nonnull(queue);
4960
__attribute__((cleanup(cleanup_string)))
4961
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4962
g_assert_nonnull(tempfilename);
4963
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4964
g_assert_cmpint(questionfile, >, 0);
4965
FILE *qf = fdopen(questionfile, "w");
4966
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4967
g_assert_cmpint(fclose(qf), ==, 0);
4969
char *const filename = strdup(tempfilename);
4970
g_assert_nonnull(filename);
4971
task_context task = {
4972
.func=open_and_parse_question,
4973
.question_filename=filename,
4975
.password=(buffer[]){{}},
4977
.cancelled_filenames=&cancelled_filenames,
4978
.current_time=(mono_microsecs[]){0},
4979
.mandos_client_exited=(bool[]){false},
4980
.password_is_read=(bool[]){false},
4982
run_task_with_stderr_to_dev_null(task, queue);
4983
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4985
g_assert_cmpint(unlink(tempfilename), ==, 0);
4989
void test_open_and_parse_question_nopid(__attribute__((unused))
4990
test_fixture *fixture,
4991
__attribute__((unused))
4992
gconstpointer user_data){
4993
__attribute__((cleanup(cleanup_close)))
4994
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4995
g_assert_cmpint(epoll_fd, >=, 0);
4996
__attribute__((cleanup(string_set_clear)))
4997
string_set cancelled_filenames = {};
4998
__attribute__((cleanup(cleanup_queue)))
4999
task_queue *queue = create_queue();
5000
g_assert_nonnull(queue);
5002
__attribute__((cleanup(cleanup_string)))
5003
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5004
g_assert_nonnull(tempfilename);
5005
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5006
g_assert_cmpint(questionfile, >, 0);
5007
FILE *qf = fdopen(questionfile, "w");
5008
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5009
g_assert_cmpint(fclose(qf), ==, 0);
5011
char *const filename = strdup(tempfilename);
5012
g_assert_nonnull(filename);
5013
task_context task = {
5014
.func=open_and_parse_question,
5015
.question_filename=filename,
5017
.password=(buffer[]){{}},
5019
.cancelled_filenames=&cancelled_filenames,
5020
.current_time=(mono_microsecs[]){0},
5021
.mandos_client_exited=(bool[]){false},
5022
.password_is_read=(bool[]){false},
5024
run_task_with_stderr_to_dev_null(task, queue);
5025
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5027
g_assert_cmpint(unlink(tempfilename), ==, 0);
5031
void test_open_and_parse_question_badpid(__attribute__((unused))
5032
test_fixture *fixture,
5033
__attribute__((unused))
5034
gconstpointer user_data){
5035
__attribute__((cleanup(cleanup_close)))
5036
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5037
g_assert_cmpint(epoll_fd, >=, 0);
5038
__attribute__((cleanup(string_set_clear)))
5039
string_set cancelled_filenames = {};
5040
__attribute__((cleanup(cleanup_queue)))
5041
task_queue *queue = create_queue();
5042
g_assert_nonnull(queue);
5044
__attribute__((cleanup(cleanup_string)))
5045
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5046
g_assert_nonnull(tempfilename);
5047
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5048
g_assert_cmpint(questionfile, >, 0);
5049
FILE *qf = fdopen(questionfile, "w");
5050
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5052
g_assert_cmpint(fclose(qf), ==, 0);
5054
char *const filename = strdup(tempfilename);
5055
g_assert_nonnull(filename);
5056
task_context task = {
5057
.func=open_and_parse_question,
5058
.question_filename=filename,
5060
.password=(buffer[]){{}},
5062
.cancelled_filenames=&cancelled_filenames,
5063
.current_time=(mono_microsecs[]){0},
5064
.mandos_client_exited=(bool[]){false},
5065
.password_is_read=(bool[]){false},
5067
run_task_with_stderr_to_dev_null(task, queue);
5068
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5070
g_assert_cmpint(unlink(tempfilename), ==, 0);
5074
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5075
test_fixture *fixture,
5076
__attribute__((unused))
5077
gconstpointer user_data){
5078
__attribute__((cleanup(cleanup_close)))
5079
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5080
g_assert_cmpint(epoll_fd, >=, 0);
5081
__attribute__((cleanup(string_set_clear)))
5082
string_set cancelled_filenames = {};
5083
buffer password = {};
5084
bool mandos_client_exited = false;
5085
bool password_is_read = false;
5086
__attribute__((cleanup(cleanup_queue)))
5087
task_queue *queue = create_queue();
5088
g_assert_nonnull(queue);
5089
const mono_microsecs current_time = 0;
5091
/* Find value of sysctl kernel.pid_max */
5092
uintmax_t pid_max = 0;
5093
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5094
g_assert_nonnull(sysctl_pid_max);
5095
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5097
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5099
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5100
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5102
__attribute__((cleanup(cleanup_string)))
5103
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5104
g_assert_nonnull(tempfilename);
5105
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5106
g_assert_cmpint(questionfile, >, 0);
5107
FILE *qf = fdopen(questionfile, "w");
5108
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5109
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5111
g_assert_cmpint(fclose(qf), ==, 0);
5113
char *const question_filename = strdup(tempfilename);
5114
g_assert_nonnull(question_filename);
5115
task_context task = {
5116
.func=open_and_parse_question,
5117
.question_filename=question_filename,
5119
.password=&password,
5120
.filename=question_filename,
5121
.cancelled_filenames=&cancelled_filenames,
5122
.current_time=¤t_time,
5123
.mandos_client_exited=&mandos_client_exited,
5124
.password_is_read=&password_is_read,
5126
run_task_with_stderr_to_dev_null(task, queue);
5127
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5129
g_assert_cmpint(unlink(tempfilename), ==, 0);
5133
test_open_and_parse_question_no_notafter(__attribute__((unused))
5134
test_fixture *fixture,
5135
__attribute__((unused))
5136
gconstpointer user_data){
5137
__attribute__((cleanup(cleanup_close)))
5138
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5139
g_assert_cmpint(epoll_fd, >=, 0);
5140
__attribute__((cleanup(string_set_clear)))
5141
string_set cancelled_filenames = {};
5142
buffer password = {};
5143
bool mandos_client_exited = false;
5144
bool password_is_read = false;
5145
__attribute__((cleanup(cleanup_queue)))
5146
task_queue *queue = create_queue();
5147
g_assert_nonnull(queue);
5148
const mono_microsecs current_time = 0;
5150
__attribute__((cleanup(cleanup_string)))
5151
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5152
g_assert_nonnull(tempfilename);
5153
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5154
g_assert_cmpint(questionfile, >, 0);
5155
FILE *qf = fdopen(questionfile, "w");
5156
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5157
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5158
g_assert_cmpint(fclose(qf), ==, 0);
5160
char *const filename = strdup(tempfilename);
5161
g_assert_nonnull(filename);
5162
task_context task = {
5163
.func=open_and_parse_question,
5164
.question_filename=filename,
5166
.password=&password,
5168
.cancelled_filenames=&cancelled_filenames,
5169
.current_time=¤t_time,
5170
.mandos_client_exited=&mandos_client_exited,
5171
.password_is_read=&password_is_read,
5173
task.func(task, queue);
5174
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5176
__attribute__((cleanup(cleanup_string)))
5177
char *socket_filename = strdup("/nonexistent");
5178
g_assert_nonnull(socket_filename);
5179
g_assert_nonnull(find_matching_task(queue, (task_context){
5180
.func=connect_question_socket,
5181
.question_filename=tempfilename,
5182
.filename=socket_filename,
5184
.password=&password,
5185
.current_time=¤t_time,
5186
.mandos_client_exited=&mandos_client_exited,
5187
.password_is_read=&password_is_read,
5190
g_assert_true(queue->next_run != 0);
5192
g_assert_cmpint(unlink(tempfilename), ==, 0);
5196
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5197
test_fixture *fixture,
5198
__attribute__((unused))
5199
gconstpointer user_data){
5200
__attribute__((cleanup(cleanup_close)))
5201
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5202
g_assert_cmpint(epoll_fd, >=, 0);
5203
__attribute__((cleanup(string_set_clear)))
5204
string_set cancelled_filenames = {};
5205
buffer password = {};
5206
bool mandos_client_exited = false;
5207
bool password_is_read = false;
5208
__attribute__((cleanup(cleanup_queue)))
5209
task_queue *queue = create_queue();
5210
g_assert_nonnull(queue);
5211
const mono_microsecs current_time = 0;
5213
__attribute__((cleanup(cleanup_string)))
5214
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5215
g_assert_nonnull(tempfilename);
5216
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5217
g_assert_cmpint(questionfile, >, 0);
5218
FILE *qf = fdopen(questionfile, "w");
5219
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5220
PRIuMAX "\nNotAfter=\n",
5221
(uintmax_t)getpid()), >, 0);
5222
g_assert_cmpint(fclose(qf), ==, 0);
5224
char *const filename = strdup(tempfilename);
5225
g_assert_nonnull(filename);
5226
task_context task = {
5227
.func=open_and_parse_question,
5228
.question_filename=filename,
5230
.password=&password,
5232
.cancelled_filenames=&cancelled_filenames,
5233
.current_time=¤t_time,
5234
.mandos_client_exited=&mandos_client_exited,
5235
.password_is_read=&password_is_read,
5237
run_task_with_stderr_to_dev_null(task, queue);
5238
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5240
__attribute__((cleanup(cleanup_string)))
5241
char *socket_filename = strdup("/nonexistent");
5242
g_assert_nonnull(find_matching_task(queue, (task_context){
5243
.func=connect_question_socket,
5244
.question_filename=tempfilename,
5245
.filename=socket_filename,
5247
.password=&password,
5248
.current_time=¤t_time,
5249
.mandos_client_exited=&mandos_client_exited,
5250
.password_is_read=&password_is_read,
5252
g_assert_true(queue->next_run != 0);
5254
g_assert_cmpint(unlink(tempfilename), ==, 0);
5258
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5260
const mono_microsecs
5262
const mono_microsecs
5264
__attribute__((cleanup(cleanup_close)))
5265
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5266
g_assert_cmpint(epoll_fd, >=, 0);
5267
__attribute__((cleanup(string_set_clear)))
5268
string_set cancelled_filenames = {};
5269
buffer password = {};
5270
bool mandos_client_exited = false;
5271
bool password_is_read = false;
5272
__attribute__((cleanup(cleanup_queue)))
5273
task_queue *queue = create_queue();
5274
g_assert_nonnull(queue);
5275
queue->next_run = next_queue_run;
5277
__attribute__((cleanup(cleanup_string)))
5278
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5279
g_assert_nonnull(tempfilename);
5280
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5281
g_assert_cmpint(questionfile, >, 0);
5282
FILE *qf = fdopen(questionfile, "w");
5283
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5284
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5285
(uintmax_t)getpid(), notafter), >, 0);
5286
g_assert_cmpint(fclose(qf), ==, 0);
5288
char *const filename = strdup(tempfilename);
5289
g_assert_nonnull(filename);
5290
task_context task = {
5291
.func=open_and_parse_question,
5292
.question_filename=filename,
5294
.password=&password,
5296
.cancelled_filenames=&cancelled_filenames,
5297
.current_time=¤t_time,
5298
.mandos_client_exited=&mandos_client_exited,
5299
.password_is_read=&password_is_read,
5301
task.func(task, queue);
5303
if(queue->length >= 1){
5304
__attribute__((cleanup(cleanup_string)))
5305
char *socket_filename = strdup("/nonexistent");
5306
g_assert_nonnull(find_matching_task(queue, (task_context){
5307
.func=connect_question_socket,
5308
.filename=socket_filename,
5310
.password=&password,
5311
.current_time=¤t_time,
5312
.cancelled_filenames=&cancelled_filenames,
5313
.mandos_client_exited=&mandos_client_exited,
5314
.password_is_read=&password_is_read,
5316
g_assert_true(queue->next_run != 0);
5320
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5321
} else if(current_time >= notafter) {
5322
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5324
g_assert_nonnull(find_matching_task(queue, (task_context){
5325
.func=cancel_old_question,
5326
.question_filename=tempfilename,
5327
.filename=tempfilename,
5329
.cancelled_filenames=&cancelled_filenames,
5330
.current_time=¤t_time,
5333
g_assert_true(queue->next_run == 1);
5335
g_assert_cmpint(unlink(tempfilename), ==, 0);
5339
test_open_and_parse_question_notafter_0(__attribute__((unused))
5340
test_fixture *fixture,
5341
__attribute__((unused))
5342
gconstpointer user_data){
5343
/* current_time, notafter, next_queue_run */
5344
assert_open_and_parse_question_with_notafter(0, 0, 0);
5348
test_open_and_parse_question_notafter_1(__attribute__((unused))
5349
test_fixture *fixture,
5350
__attribute__((unused))
5351
gconstpointer user_data){
5352
/* current_time, notafter, next_queue_run */
5353
assert_open_and_parse_question_with_notafter(0, 1, 0);
5357
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5358
test_fixture *fixture,
5359
__attribute__((unused))
5360
gconstpointer user_data){
5361
/* current_time, notafter, next_queue_run */
5362
assert_open_and_parse_question_with_notafter(0, 1, 1);
5366
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5367
test_fixture *fixture,
5368
__attribute__((unused))
5369
gconstpointer user_data){
5370
/* current_time, notafter, next_queue_run */
5371
assert_open_and_parse_question_with_notafter(0, 1, 2);
5375
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5376
test_fixture *fixture,
5377
__attribute__((unused))
5378
gconstpointer user_data){
5379
/* current_time, notafter, next_queue_run */
5380
assert_open_and_parse_question_with_notafter(1, 1, 0);
5384
test_open_and_parse_question_late_notafter(__attribute__((unused))
5385
test_fixture *fixture,
5386
__attribute__((unused))
5387
gconstpointer user_data){
5388
/* current_time, notafter, next_queue_run */
5389
assert_open_and_parse_question_with_notafter(2, 1, 0);
5392
static void assert_cancel_old_question_param(const mono_microsecs
5394
const mono_microsecs
5396
const mono_microsecs
5398
const mono_microsecs
5400
__attribute__((cleanup(string_set_clear)))
5401
string_set cancelled_filenames = {};
5402
__attribute__((cleanup(cleanup_queue)))
5403
task_queue *queue = create_queue();
5404
g_assert_nonnull(queue);
5405
queue->next_run = next_queue_run;
5407
char *const question_filename = strdup("/nonexistent");
5408
g_assert_nonnull(question_filename);
5409
task_context task = {
5410
.func=cancel_old_question,
5411
.question_filename=question_filename,
5412
.filename=question_filename,
5414
.cancelled_filenames=&cancelled_filenames,
5415
.current_time=¤t_time,
5417
task.func(task, queue);
5419
if(current_time >= notafter){
5420
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5421
g_assert_true(string_set_contains(cancelled_filenames,
5424
g_assert_nonnull(find_matching_task(queue, (task_context){
5425
.func=cancel_old_question,
5426
.question_filename=question_filename,
5427
.filename=question_filename,
5429
.cancelled_filenames=&cancelled_filenames,
5430
.current_time=¤t_time,
5433
g_assert_false(string_set_contains(cancelled_filenames,
5434
question_filename));
5436
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5437
(unsigned int)next_set_to);
5440
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5441
test_fixture *fixture,
5442
__attribute__((unused))
5443
gconstpointer user_data){
5444
/* next_queue_run unset,
5445
cancellation should happen because time has come,
5446
next_queue_run should be unchanged */
5447
/* next_queue_run, notafter, current_time, next_set_to */
5448
assert_cancel_old_question_param(0, 1, 2, 0);
5451
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5452
test_fixture *fixture,
5453
__attribute__((unused))
5454
gconstpointer user_data){
5455
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5456
and current_time is not yet notafter or greater,
5457
update value of next_queue_run to value of notafter */
5458
/* next_queue_run, notafter, current_time, next_set_to */
5459
assert_cancel_old_question_param(0, 2, 1, 2);
5462
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5463
test_fixture *fixture,
5464
__attribute__((unused))
5465
gconstpointer user_data){
5466
/* next_queue_run 1,
5467
cancellation should happen because time has come,
5468
next_queue_run should be unchanged */
5469
/* next_queue_run, notafter, current_time, next_set_to */
5470
assert_cancel_old_question_param(1, 2, 3, 1);
5473
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5474
test_fixture *fixture,
5475
__attribute__((unused))
5476
gconstpointer user_data){
5477
/* If next_queue_run is set,
5478
and current_time is not yet notafter or greater,
5479
and notafter is larger than next_queue_run
5480
next_queue_run should be unchanged */
5481
/* next_queue_run, notafter, current_time, next_set_to */
5482
assert_cancel_old_question_param(1, 3, 2, 1);
5485
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5486
test_fixture *fixture,
5487
__attribute__((unused))
5488
gconstpointer user_data){
5489
/* next_queue_run 2,
5490
cancellation should happen because time has come,
5491
next_queue_run should be unchanged */
5492
/* next_queue_run, notafter, current_time, next_set_to */
5493
assert_cancel_old_question_param(2, 1, 3, 2);
5496
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5497
test_fixture *fixture,
5498
__attribute__((unused))
5499
gconstpointer user_data){
5500
/* If next_queue_run is set,
5501
and current_time is not yet notafter or greater,
5502
and notafter is larger than next_queue_run
5503
next_queue_run should be unchanged */
5504
/* next_queue_run, notafter, current_time, next_set_to */
5505
assert_cancel_old_question_param(2, 3, 1, 2);
5508
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5509
test_fixture *fixture,
5510
__attribute__((unused))
5511
gconstpointer user_data){
5512
/* next_queue_run 3,
5513
cancellation should happen because time has come,
5514
next_queue_run should be unchanged */
5515
/* next_queue_run, notafter, current_time, next_set_to */
5516
assert_cancel_old_question_param(3, 1, 2, 3);
5519
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5520
test_fixture *fixture,
5521
__attribute__((unused))
5522
gconstpointer user_data){
5523
/* If next_queue_run is set,
5524
and current_time is not yet notafter or greater,
5525
and notafter is smaller than next_queue_run
5526
update value of next_queue_run to value of notafter */
5527
/* next_queue_run, notafter, current_time, next_set_to */
5528
assert_cancel_old_question_param(3, 2, 1, 2);
5532
test_connect_question_socket_name_too_long(__attribute__((unused))
5533
test_fixture *fixture,
5534
__attribute__((unused))
5535
gconstpointer user_data){
5536
__attribute__((cleanup(cleanup_close)))
5537
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5538
g_assert_cmpint(epoll_fd, >=, 0);
5539
const char question_filename[] = "/nonexistent/question";
5540
__attribute__((cleanup(string_set_clear)))
5541
string_set cancelled_filenames = {};
5542
__attribute__((cleanup(cleanup_queue)))
5543
task_queue *queue = create_queue();
5544
g_assert_nonnull(queue);
5545
__attribute__((cleanup(cleanup_string)))
5546
char *tempdir = make_temporary_directory();
5547
g_assert_nonnull(tempdir);
5548
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5549
char socket_name[sizeof(unix_socket.sun_path)];
5550
memset(socket_name, 'x', sizeof(socket_name));
5551
socket_name[sizeof(socket_name)-1] = '\0';
5552
char *filename = NULL;
5553
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5555
g_assert_nonnull(filename);
5557
task_context task = {
5558
.func=connect_question_socket,
5559
.question_filename=strdup(question_filename),
5561
.password=(buffer[]){{}},
5563
.cancelled_filenames=&cancelled_filenames,
5564
.mandos_client_exited=(bool[]){false},
5565
.password_is_read=(bool[]){false},
5566
.current_time=(mono_microsecs[]){0},
5568
g_assert_nonnull(task.question_filename);
5569
run_task_with_stderr_to_dev_null(task, queue);
5571
g_assert_true(string_set_contains(cancelled_filenames,
5572
question_filename));
5573
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5574
g_assert_true(queue->next_run == 0);
5576
g_assert_cmpint(rmdir(tempdir), ==, 0);
5580
void test_connect_question_socket_connect_fail(__attribute__((unused))
5581
test_fixture *fixture,
5582
__attribute__((unused))
5585
__attribute__((cleanup(cleanup_close)))
5586
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5587
g_assert_cmpint(epoll_fd, >=, 0);
5588
const char question_filename[] = "/nonexistent/question";
5589
__attribute__((cleanup(string_set_clear)))
5590
string_set cancelled_filenames = {};
5591
const mono_microsecs current_time = 3;
5592
__attribute__((cleanup(cleanup_queue)))
5593
task_queue *queue = create_queue();
5594
g_assert_nonnull(queue);
5595
__attribute__((cleanup(cleanup_string)))
5596
char *tempdir = make_temporary_directory();
5597
g_assert_nonnull(tempdir);
5598
char socket_name[] = "nonexistent";
5599
char *filename = NULL;
5600
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5602
g_assert_nonnull(filename);
5604
task_context task = {
5605
.func=connect_question_socket,
5606
.question_filename=strdup(question_filename),
5608
.password=(buffer[]){{}},
5610
.cancelled_filenames=&cancelled_filenames,
5611
.mandos_client_exited=(bool[]){false},
5612
.password_is_read=(bool[]){false},
5613
.current_time=¤t_time,
5615
g_assert_nonnull(task.question_filename);
5616
run_task_with_stderr_to_dev_null(task, queue);
5618
g_assert_nonnull(find_matching_task(queue, task));
5620
g_assert_false(string_set_contains(cancelled_filenames,
5621
question_filename));
5622
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5623
g_assert_true(queue->next_run == 1000000 + current_time);
5625
g_assert_cmpint(rmdir(tempdir), ==, 0);
5629
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5630
test_fixture *fixture,
5631
__attribute__((unused))
5632
gconstpointer user_data){
5633
__attribute__((cleanup(cleanup_close)))
5634
const int epoll_fd = open("/dev/null",
5635
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5636
__attribute__((cleanup(cleanup_string)))
5637
char *const question_filename = strdup("/nonexistent/question");
5638
g_assert_nonnull(question_filename);
5639
__attribute__((cleanup(string_set_clear)))
5640
string_set cancelled_filenames = {};
5641
const mono_microsecs current_time = 5;
5642
__attribute__((cleanup(cleanup_queue)))
5643
task_queue *queue = create_queue();
5644
g_assert_nonnull(queue);
5645
__attribute__((cleanup(cleanup_string)))
5646
char *tempdir = make_temporary_directory();
5647
g_assert_nonnull(tempdir);
5648
__attribute__((cleanup(cleanup_close)))
5649
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5650
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5651
g_assert_cmpint(sock_fd, >=, 0);
5652
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5653
const char socket_name[] = "socket_name";
5654
__attribute__((cleanup(cleanup_string)))
5655
char *filename = NULL;
5656
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5658
g_assert_nonnull(filename);
5659
g_assert_cmpint((int)strlen(filename), <,
5660
(int)sizeof(sock_name.sun_path));
5661
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5662
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5663
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5664
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5665
task_context task = {
5666
.func=connect_question_socket,
5667
.question_filename=strdup(question_filename),
5669
.password=(buffer[]){{}},
5670
.filename=strdup(filename),
5671
.cancelled_filenames=&cancelled_filenames,
5672
.mandos_client_exited=(bool[]){false},
5673
.password_is_read=(bool[]){false},
5674
.current_time=¤t_time,
5676
g_assert_nonnull(task.question_filename);
5677
run_task_with_stderr_to_dev_null(task, queue);
5679
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5680
const task_context *const added_task
5681
= find_matching_task(queue, task);
5682
g_assert_nonnull(added_task);
5683
g_assert_true(queue->next_run == 1000000 + current_time);
5685
g_assert_cmpint(unlink(filename), ==, 0);
5686
g_assert_cmpint(rmdir(tempdir), ==, 0);
5690
void test_connect_question_socket_usable(__attribute__((unused))
5691
test_fixture *fixture,
5692
__attribute__((unused))
5693
gconstpointer user_data){
5694
__attribute__((cleanup(cleanup_close)))
5695
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5696
g_assert_cmpint(epoll_fd, >=, 0);
5697
__attribute__((cleanup(cleanup_string)))
5698
char *const question_filename = strdup("/nonexistent/question");
5699
g_assert_nonnull(question_filename);
5700
__attribute__((cleanup(string_set_clear)))
5701
string_set cancelled_filenames = {};
5702
buffer password = {};
5703
bool mandos_client_exited = false;
5704
bool password_is_read = false;
5705
const mono_microsecs current_time = 0;
5706
__attribute__((cleanup(cleanup_queue)))
5707
task_queue *queue = create_queue();
5708
g_assert_nonnull(queue);
5709
__attribute__((cleanup(cleanup_string)))
5710
char *tempdir = make_temporary_directory();
5711
g_assert_nonnull(tempdir);
5712
__attribute__((cleanup(cleanup_close)))
5713
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5714
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5715
g_assert_cmpint(sock_fd, >=, 0);
5716
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5717
const char socket_name[] = "socket_name";
5718
__attribute__((cleanup(cleanup_string)))
5719
char *filename = NULL;
5720
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5722
g_assert_nonnull(filename);
5723
g_assert_cmpint((int)strlen(filename), <,
5724
(int)sizeof(sock_name.sun_path));
5725
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5726
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5727
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5728
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5729
task_context task = {
5730
.func=connect_question_socket,
5731
.question_filename=strdup(question_filename),
5733
.password=&password,
5734
.filename=strdup(filename),
5735
.cancelled_filenames=&cancelled_filenames,
5736
.mandos_client_exited=&mandos_client_exited,
5737
.password_is_read=&password_is_read,
5738
.current_time=¤t_time,
5740
g_assert_nonnull(task.question_filename);
5741
task.func(task, queue);
5743
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5744
const task_context *const added_task
5745
= find_matching_task(queue, (task_context){
5746
.func=send_password_to_socket,
5747
.question_filename=question_filename,
5750
.password=&password,
5751
.cancelled_filenames=&cancelled_filenames,
5752
.mandos_client_exited=&mandos_client_exited,
5753
.password_is_read=&password_is_read,
5754
.current_time=¤t_time,
5756
g_assert_nonnull(added_task);
5757
g_assert_cmpint(added_task->fd, >, 0);
5759
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5762
const int fd = added_task->fd;
5763
g_assert_cmpint(fd, >, 0);
5764
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5767
char write_data[PIPE_BUF];
5769
/* Construct test password buffer */
5770
/* Start with + since that is what the real procotol uses */
5771
write_data[0] = '+';
5772
/* Set a special character at string end just to mark the end */
5773
write_data[sizeof(write_data)-2] = 'y';
5774
/* Set NUL at buffer end, as suggested by the protocol */
5775
write_data[sizeof(write_data)-1] = '\0';
5776
/* Fill rest of password with 'x' */
5777
memset(write_data+1, 'x', sizeof(write_data)-3);
5778
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5779
MSG_NOSIGNAL), ==, sizeof(write_data));
5782
/* read from sock_fd */
5783
char read_data[sizeof(write_data)];
5784
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5785
==, sizeof(read_data));
5787
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5790
/* writing to sock_fd should fail */
5791
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5792
MSG_NOSIGNAL), <, 0);
5794
/* reading from fd should fail */
5795
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5796
MSG_NOSIGNAL), <, 0);
5798
g_assert_cmpint(unlink(filename), ==, 0);
5799
g_assert_cmpint(rmdir(tempdir), ==, 0);
5803
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5804
test_fixture *fixture,
5805
__attribute__((unused))
5808
__attribute__((cleanup(cleanup_close)))
5809
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5810
g_assert_cmpint(epoll_fd, >=, 0);
5811
__attribute__((cleanup(cleanup_string)))
5812
char *const question_filename = strdup("/nonexistent/question");
5813
g_assert_nonnull(question_filename);
5814
__attribute__((cleanup(cleanup_string)))
5815
char *const filename = strdup("/nonexistent/socket");
5816
g_assert_nonnull(filename);
5817
__attribute__((cleanup(string_set_clear)))
5818
string_set cancelled_filenames = {};
5819
buffer password = {};
5820
bool password_is_read = true;
5821
__attribute__((cleanup(cleanup_queue)))
5822
task_queue *queue = create_queue();
5823
g_assert_nonnull(queue);
5825
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5826
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5828
__attribute__((cleanup(cleanup_close)))
5829
const int read_socket = socketfds[0];
5830
const int write_socket = socketfds[1];
5831
task_context task = {
5832
.func=send_password_to_socket,
5833
.question_filename=strdup(question_filename),
5834
.filename=strdup(filename),
5837
.password=&password,
5838
.cancelled_filenames=&cancelled_filenames,
5839
.mandos_client_exited=(bool[]){false},
5840
.password_is_read=&password_is_read,
5841
.current_time=(mono_microsecs[]){0},
5843
g_assert_nonnull(task.question_filename);
5845
task.func(task, queue);
5847
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5849
const task_context *const added_task
5850
= find_matching_task(queue, task);
5851
g_assert_nonnull(added_task);
5852
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5853
g_assert_true(password_is_read);
5855
g_assert_cmpint(added_task->fd, >, 0);
5856
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5861
test_send_password_to_socket_password_not_read(__attribute__((unused))
5862
test_fixture *fixture,
5863
__attribute__((unused))
5866
__attribute__((cleanup(cleanup_close)))
5867
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5868
g_assert_cmpint(epoll_fd, >=, 0);
5869
__attribute__((cleanup(cleanup_string)))
5870
char *const question_filename = strdup("/nonexistent/question");
5871
g_assert_nonnull(question_filename);
5872
__attribute__((cleanup(cleanup_string)))
5873
char *const filename = strdup("/nonexistent/socket");
5874
__attribute__((cleanup(string_set_clear)))
5875
string_set cancelled_filenames = {};
5876
buffer password = {};
5877
__attribute__((cleanup(cleanup_queue)))
5878
task_queue *queue = create_queue();
5879
g_assert_nonnull(queue);
5881
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5882
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5884
__attribute__((cleanup(cleanup_close)))
5885
const int read_socket = socketfds[0];
5886
const int write_socket = socketfds[1];
5887
task_context task = {
5888
.func=send_password_to_socket,
5889
.question_filename=strdup(question_filename),
5890
.filename=strdup(filename),
5893
.password=&password,
5894
.cancelled_filenames=&cancelled_filenames,
5895
.mandos_client_exited=(bool[]){false},
5896
.password_is_read=(bool[]){false},
5897
.current_time=(mono_microsecs[]){0},
5899
g_assert_nonnull(task.question_filename);
5901
task.func(task, queue);
5903
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5905
const task_context *const added_task = find_matching_task(queue,
5907
g_assert_nonnull(added_task);
5908
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5909
g_assert_true(queue->next_run == 0);
5911
g_assert_cmpint(added_task->fd, >, 0);
5912
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5917
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5918
test_fixture *fixture,
5919
__attribute__((unused))
5920
gconstpointer user_data){
5921
__attribute__((cleanup(cleanup_close)))
5922
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5923
g_assert_cmpint(epoll_fd, >=, 0);
5924
const char question_filename[] = "/nonexistent/question";
5925
char *const filename = strdup("/nonexistent/socket");
5926
__attribute__((cleanup(string_set_clear)))
5927
string_set cancelled_filenames = {};
5928
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5929
__attribute__((cleanup(cleanup_buffer)))
5931
.data=malloc(oversized),
5933
.allocated=oversized,
5935
g_assert_nonnull(password.data);
5936
if(mlock(password.data, password.allocated) != 0){
5937
g_assert_true(errno == EPERM or errno == ENOMEM);
5939
/* Construct test password buffer */
5940
/* Start with + since that is what the real procotol uses */
5941
password.data[0] = '+';
5942
/* Set a special character at string end just to mark the end */
5943
password.data[oversized-3] = 'y';
5944
/* Set NUL at buffer end, as suggested by the protocol */
5945
password.data[oversized-2] = '\0';
5946
/* Fill rest of password with 'x' */
5947
memset(password.data+1, 'x', oversized-3);
5949
__attribute__((cleanup(cleanup_queue)))
5950
task_queue *queue = create_queue();
5951
g_assert_nonnull(queue);
5953
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5954
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5956
__attribute__((cleanup(cleanup_close)))
5957
const int read_socket = socketfds[0];
5958
__attribute__((cleanup(cleanup_close)))
5959
const int write_socket = socketfds[1];
5960
task_context task = {
5961
.func=send_password_to_socket,
5962
.question_filename=strdup(question_filename),
5966
.password=&password,
5967
.cancelled_filenames=&cancelled_filenames,
5968
.mandos_client_exited=(bool[]){true},
5969
.password_is_read=(bool[]){true},
5970
.current_time=(mono_microsecs[]){0},
5972
g_assert_nonnull(task.question_filename);
5974
run_task_with_stderr_to_dev_null(task, queue);
5976
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5977
g_assert_true(string_set_contains(cancelled_filenames,
5978
question_filename));
5981
static void test_send_password_to_socket_retry(__attribute__((unused))
5982
test_fixture *fixture,
5983
__attribute__((unused))
5986
__attribute__((cleanup(cleanup_close)))
5987
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5988
g_assert_cmpint(epoll_fd, >=, 0);
5989
__attribute__((cleanup(cleanup_string)))
5990
char *const question_filename = strdup("/nonexistent/question");
5991
g_assert_nonnull(question_filename);
5992
__attribute__((cleanup(cleanup_string)))
5993
char *const filename = strdup("/nonexistent/socket");
5994
g_assert_nonnull(filename);
5995
__attribute__((cleanup(string_set_clear)))
5996
string_set cancelled_filenames = {};
5997
__attribute__((cleanup(cleanup_buffer)))
5998
buffer password = {};
6000
__attribute__((cleanup(cleanup_queue)))
6001
task_queue *queue = create_queue();
6002
g_assert_nonnull(queue);
6004
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6005
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6007
__attribute__((cleanup(cleanup_close)))
6008
const int read_socket = socketfds[0];
6009
const int write_socket = socketfds[1];
6010
/* Close the server side socket to force ECONNRESET on client */
6011
g_assert_cmpint(close(read_socket), ==, 0);
6012
task_context task = {
6013
.func=send_password_to_socket,
6014
.question_filename=strdup(question_filename),
6015
.filename=strdup(filename),
6018
.password=&password,
6019
.cancelled_filenames=&cancelled_filenames,
6020
.mandos_client_exited=(bool[]){true},
6021
.password_is_read=(bool[]){true},
6022
.current_time=(mono_microsecs[]){0},
6024
g_assert_nonnull(task.question_filename);
6026
task.func(task, queue);
6028
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6030
const task_context *const added_task = find_matching_task(queue,
6032
g_assert_nonnull(added_task);
6033
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6035
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6040
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6041
test_fixture *fixture,
6042
__attribute__((unused))
6043
gconstpointer user_data){
6044
__attribute__((cleanup(cleanup_close)))
6045
const int epoll_fd = open("/dev/null",
6046
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6047
__attribute__((cleanup(cleanup_string)))
6048
char *const question_filename = strdup("/nonexistent/question");
6049
g_assert_nonnull(question_filename);
6050
__attribute__((cleanup(cleanup_string)))
6051
char *const filename = strdup("/nonexistent/socket");
6052
g_assert_nonnull(filename);
6053
__attribute__((cleanup(string_set_clear)))
6054
string_set cancelled_filenames = {};
6055
__attribute__((cleanup(cleanup_buffer)))
6056
buffer password = {};
6058
const mono_microsecs current_time = 11;
6059
__attribute__((cleanup(cleanup_queue)))
6060
task_queue *queue = create_queue();
6061
g_assert_nonnull(queue);
6063
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6064
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6066
__attribute__((cleanup(cleanup_close)))
6067
const int read_socket = socketfds[0];
6068
const int write_socket = socketfds[1];
6069
/* Close the server side socket to force ECONNRESET on client */
6070
g_assert_cmpint(close(read_socket), ==, 0);
6071
task_context task = {
6072
.func=send_password_to_socket,
6073
.question_filename=strdup(question_filename),
6074
.filename=strdup(filename),
6077
.password=&password,
6078
.cancelled_filenames=&cancelled_filenames,
6079
.mandos_client_exited=(bool[]){true},
6080
.password_is_read=(bool[]){true},
6081
.current_time=¤t_time,
6083
g_assert_nonnull(task.question_filename);
6085
run_task_with_stderr_to_dev_null(task, queue);
6087
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6089
const task_context *const added_task = find_matching_task(queue,
6091
g_assert_nonnull(added_task);
6092
g_assert_true(queue->next_run == current_time + 1000000);
6093
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6096
static void assert_send_password_to_socket_password(buffer password){
6097
__attribute__((cleanup(cleanup_close)))
6098
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6099
g_assert_cmpint(epoll_fd, >=, 0);
6100
char *const question_filename = strdup("/nonexistent/question");
6101
g_assert_nonnull(question_filename);
6102
char *const filename = strdup("/nonexistent/socket");
6103
g_assert_nonnull(filename);
6104
__attribute__((cleanup(string_set_clear)))
6105
string_set cancelled_filenames = {};
6107
__attribute__((cleanup(cleanup_queue)))
6108
task_queue *queue = create_queue();
6109
g_assert_nonnull(queue);
6111
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6112
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6114
__attribute__((cleanup(cleanup_close)))
6115
const int read_socket = socketfds[0];
6116
const int write_socket = socketfds[1];
6117
task_context task = {
6118
.func=send_password_to_socket,
6119
.question_filename=question_filename,
6123
.password=&password,
6124
.cancelled_filenames=&cancelled_filenames,
6125
.mandos_client_exited=(bool[]){true},
6126
.password_is_read=(bool[]){true},
6127
.current_time=(mono_microsecs[]){0},
6130
char *expected_written_data = malloc(password.length + 2);
6131
g_assert_nonnull(expected_written_data);
6132
expected_written_data[0] = '+';
6133
expected_written_data[password.length + 1] = '\0';
6134
if(password.length > 0){
6135
g_assert_nonnull(password.data);
6136
memcpy(expected_written_data + 1, password.data, password.length);
6139
task.func(task, queue);
6142
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6143
(int)(password.length + 2));
6144
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6146
g_assert_true(memcmp(expected_written_data, buf,
6147
password.length + 2) == 0);
6149
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6151
free(expected_written_data);
6155
test_send_password_to_socket_null_password(__attribute__((unused))
6156
test_fixture *fixture,
6157
__attribute__((unused))
6158
gconstpointer user_data){
6159
__attribute__((cleanup(cleanup_buffer)))
6160
buffer password = {};
6161
assert_send_password_to_socket_password(password);
6165
test_send_password_to_socket_empty_password(__attribute__((unused))
6166
test_fixture *fixture,
6167
__attribute__((unused))
6168
gconstpointer user_data){
6169
__attribute__((cleanup(cleanup_buffer)))
6171
.data=malloc(1), /* because malloc(0) may return NULL */
6173
.allocated=0, /* deliberate lie */
6175
g_assert_nonnull(password.data);
6176
assert_send_password_to_socket_password(password);
6180
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6181
test_fixture *fixture,
6182
__attribute__((unused))
6183
gconstpointer user_data){
6184
__attribute__((cleanup(cleanup_buffer)))
6190
if(mlock(password.data, password.allocated) != 0){
6191
g_assert_true(errno == EPERM or errno == ENOMEM);
6193
assert_send_password_to_socket_password(password);
6197
test_send_password_to_socket_text_password(__attribute__((unused))
6198
test_fixture *fixture,
6199
__attribute__((unused))
6200
gconstpointer user_data){
6201
const char dummy_test_password[] = "dummy test password";
6202
__attribute__((cleanup(cleanup_buffer)))
6204
.data = strdup(dummy_test_password),
6205
.length = strlen(dummy_test_password),
6206
.allocated = sizeof(dummy_test_password),
6208
if(mlock(password.data, password.allocated) != 0){
6209
g_assert_true(errno == EPERM or errno == ENOMEM);
6211
assert_send_password_to_socket_password(password);
6215
test_send_password_to_socket_binary_password(__attribute__((unused))
6216
test_fixture *fixture,
6217
__attribute__((unused))
6218
gconstpointer user_data){
6219
__attribute__((cleanup(cleanup_buffer)))
6225
g_assert_nonnull(password.data);
6226
if(mlock(password.data, password.allocated) != 0){
6227
g_assert_true(errno == EPERM or errno == ENOMEM);
6229
char c = 1; /* Start at 1, avoiding NUL */
6230
for(int i=0; i < 255; i++){
6231
password.data[i] = c++;
6233
assert_send_password_to_socket_password(password);
6237
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6238
test_fixture *fixture,
6239
__attribute__((unused))
6242
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6243
__attribute__((cleanup(cleanup_buffer)))
6245
.data=malloc(sizeof(test_password)),
6246
.length=sizeof(test_password),
6247
.allocated=sizeof(test_password),
6249
g_assert_nonnull(password.data);
6250
if(mlock(password.data, password.allocated) !=0){
6251
g_assert_true(errno == EPERM or errno == ENOMEM);
6253
memcpy(password.data, test_password, password.allocated);
6254
assert_send_password_to_socket_password(password);
6257
static bool assert_add_existing_questions_to_devnull(task_queue
6270
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6271
test_fixture *fixture,
6272
__attribute__((unused))
6275
__attribute__((cleanup(cleanup_queue)))
6276
task_queue *queue = create_queue();
6277
g_assert_nonnull(queue);
6278
__attribute__((cleanup(cleanup_close)))
6279
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6280
g_assert_cmpint(epoll_fd, >=, 0);
6281
__attribute__((cleanup(string_set_clear)))
6282
string_set cancelled_filenames = {};
6284
g_assert_false(assert_add_existing_questions_to_devnull
6287
(buffer[]){{}}, /* password */
6288
&cancelled_filenames,
6289
(mono_microsecs[]){0}, /* current_time */
6290
(bool[]){false}, /* mandos_client_exited */
6291
(bool[]){false}, /* password_is_read */
6292
"/nonexistent")); /* dirname */
6294
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6298
bool assert_add_existing_questions_to_devnull(task_queue
6305
*cancelled_filenames,
6306
const mono_microsecs
6307
*const current_time,
6309
mandos_client_exited,
6314
__attribute__((cleanup(cleanup_close)))
6315
const int devnull_fd = open("/dev/null",
6316
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6317
g_assert_cmpint(devnull_fd, >=, 0);
6318
__attribute__((cleanup(cleanup_close)))
6319
const int real_stderr_fd = dup(STDERR_FILENO);
6320
g_assert_cmpint(real_stderr_fd, >=, 0);
6321
dup2(devnull_fd, STDERR_FILENO);
6322
const bool ret = add_existing_questions(queue, epoll_fd, password,
6323
cancelled_filenames,
6325
mandos_client_exited,
6326
password_is_read, dirname);
6327
dup2(real_stderr_fd, STDERR_FILENO);
6332
void test_add_existing_questions_no_questions(__attribute__((unused))
6333
test_fixture *fixture,
6334
__attribute__((unused))
6337
__attribute__((cleanup(cleanup_queue)))
6338
task_queue *queue = create_queue();
6339
g_assert_nonnull(queue);
6340
__attribute__((cleanup(cleanup_close)))
6341
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6342
g_assert_cmpint(epoll_fd, >=, 0);
6343
__attribute__((cleanup(string_set_clear)))
6344
string_set cancelled_filenames = {};
6345
__attribute__((cleanup(cleanup_string)))
6346
char *tempdir = make_temporary_directory();
6347
g_assert_nonnull(tempdir);
6349
g_assert_false(assert_add_existing_questions_to_devnull
6352
(buffer[]){{}}, /* password */
6353
&cancelled_filenames,
6354
(mono_microsecs[]){0}, /* current_time */
6355
(bool[]){false}, /* mandos_client_exited */
6356
(bool[]){false}, /* password_is_read */
6359
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6361
g_assert_cmpint(rmdir(tempdir), ==, 0);
6364
static char *make_question_file_in_directory(const char *const);
6367
void test_add_existing_questions_one_question(__attribute__((unused))
6368
test_fixture *fixture,
6369
__attribute__((unused))
6372
__attribute__((cleanup(cleanup_queue)))
6373
task_queue *queue = create_queue();
6374
g_assert_nonnull(queue);
6375
__attribute__((cleanup(cleanup_close)))
6376
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6377
g_assert_cmpint(epoll_fd, >=, 0);
6378
__attribute__((cleanup(cleanup_buffer)))
6379
buffer password = {};
6380
__attribute__((cleanup(string_set_clear)))
6381
string_set cancelled_filenames = {};
6382
const mono_microsecs current_time = 0;
6383
bool mandos_client_exited = false;
6384
bool password_is_read = false;
6385
__attribute__((cleanup(cleanup_string)))
6386
char *tempdir = make_temporary_directory();
6387
g_assert_nonnull(tempdir);
6388
__attribute__((cleanup(cleanup_string)))
6389
char *question_filename
6390
= make_question_file_in_directory(tempdir);
6391
g_assert_nonnull(question_filename);
6393
g_assert_true(assert_add_existing_questions_to_devnull
6397
&cancelled_filenames,
6399
&mandos_client_exited,
6403
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6405
g_assert_nonnull(find_matching_task(queue, (task_context){
6406
.func=open_and_parse_question,
6408
.filename=question_filename,
6409
.question_filename=question_filename,
6410
.password=&password,
6411
.cancelled_filenames=&cancelled_filenames,
6412
.current_time=¤t_time,
6413
.mandos_client_exited=&mandos_client_exited,
6414
.password_is_read=&password_is_read,
6417
g_assert_true(queue->next_run == 1);
6419
g_assert_cmpint(unlink(question_filename), ==, 0);
6420
g_assert_cmpint(rmdir(tempdir), ==, 0);
6423
static char *make_question_file_in_directory(const char
6425
return make_temporary_prefixed_file_in_directory("ask.", dir);
6429
void test_add_existing_questions_two_questions(__attribute__((unused))
6430
test_fixture *fixture,
6431
__attribute__((unused))
6434
__attribute__((cleanup(cleanup_queue)))
6435
task_queue *queue = create_queue();
6436
g_assert_nonnull(queue);
6437
__attribute__((cleanup(cleanup_close)))
6438
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6439
g_assert_cmpint(epoll_fd, >=, 0);
6440
__attribute__((cleanup(cleanup_buffer)))
6441
buffer password = {};
6442
__attribute__((cleanup(string_set_clear)))
6443
string_set cancelled_filenames = {};
6444
const mono_microsecs current_time = 0;
6445
bool mandos_client_exited = false;
6446
bool password_is_read = false;
6447
__attribute__((cleanup(cleanup_string)))
6448
char *tempdir = make_temporary_directory();
6449
g_assert_nonnull(tempdir);
6450
__attribute__((cleanup(cleanup_string)))
6451
char *question_filename1
6452
= make_question_file_in_directory(tempdir);
6453
g_assert_nonnull(question_filename1);
6454
__attribute__((cleanup(cleanup_string)))
6455
char *question_filename2
6456
= make_question_file_in_directory(tempdir);
6457
g_assert_nonnull(question_filename2);
6459
g_assert_true(assert_add_existing_questions_to_devnull
6463
&cancelled_filenames,
6465
&mandos_client_exited,
6469
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6471
g_assert_true(queue->next_run == 1);
6473
__attribute__((cleanup(string_set_clear)))
6474
string_set seen_questions = {};
6476
bool queue_contains_question_opener(char *const question_filename){
6477
return(find_matching_task(queue, (task_context){
6478
.func=open_and_parse_question,
6480
.question_filename=question_filename,
6481
.password=&password,
6482
.cancelled_filenames=&cancelled_filenames,
6483
.current_time=¤t_time,
6484
.mandos_client_exited=&mandos_client_exited,
6485
.password_is_read=&password_is_read,
6489
g_assert_true(queue_contains_question_opener(question_filename1));
6490
g_assert_true(queue_contains_question_opener(question_filename2));
6492
g_assert_true(queue->next_run == 1);
6494
g_assert_cmpint(unlink(question_filename1), ==, 0);
6495
g_assert_cmpint(unlink(question_filename2), ==, 0);
6496
g_assert_cmpint(rmdir(tempdir), ==, 0);
6500
test_add_existing_questions_non_questions(__attribute__((unused))
6501
test_fixture *fixture,
6502
__attribute__((unused))
6503
gconstpointer user_data){
6504
__attribute__((cleanup(cleanup_queue)))
6505
task_queue *queue = create_queue();
6506
g_assert_nonnull(queue);
6507
__attribute__((cleanup(cleanup_close)))
6508
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6509
g_assert_cmpint(epoll_fd, >=, 0);
6510
__attribute__((cleanup(string_set_clear)))
6511
string_set cancelled_filenames = {};
6512
__attribute__((cleanup(cleanup_string)))
6513
char *tempdir = make_temporary_directory();
6514
g_assert_nonnull(tempdir);
6515
__attribute__((cleanup(cleanup_string)))
6516
char *question_filename1
6517
= make_temporary_file_in_directory(tempdir);
6518
g_assert_nonnull(question_filename1);
6519
__attribute__((cleanup(cleanup_string)))
6520
char *question_filename2
6521
= make_temporary_file_in_directory(tempdir);
6522
g_assert_nonnull(question_filename2);
6524
g_assert_false(assert_add_existing_questions_to_devnull
6527
(buffer[]){{}}, /* password */
6528
&cancelled_filenames,
6529
(mono_microsecs[]){0}, /* current_time */
6530
(bool[]){false}, /* mandos_client_exited */
6531
(bool[]){false}, /* password_is_read */
6534
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6536
g_assert_cmpint(unlink(question_filename1), ==, 0);
6537
g_assert_cmpint(unlink(question_filename2), ==, 0);
6538
g_assert_cmpint(rmdir(tempdir), ==, 0);
6542
test_add_existing_questions_both_types(__attribute__((unused))
6543
test_fixture *fixture,
6544
__attribute__((unused))
6545
gconstpointer user_data){
6546
__attribute__((cleanup(cleanup_queue)))
6547
task_queue *queue = create_queue();
6548
g_assert_nonnull(queue);
6549
__attribute__((cleanup(cleanup_close)))
6550
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6551
g_assert_cmpint(epoll_fd, >=, 0);
6552
__attribute__((cleanup(cleanup_buffer)))
6553
buffer password = {};
6554
__attribute__((cleanup(string_set_clear)))
6555
string_set cancelled_filenames = {};
6556
const mono_microsecs current_time = 0;
6557
bool mandos_client_exited = false;
6558
bool password_is_read = false;
6559
__attribute__((cleanup(cleanup_string)))
6560
char *tempdir = make_temporary_directory();
6561
g_assert_nonnull(tempdir);
6562
__attribute__((cleanup(cleanup_string)))
6563
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6564
g_assert_nonnull(tempfilename1);
6565
__attribute__((cleanup(cleanup_string)))
6566
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6567
g_assert_nonnull(tempfilename2);
6568
__attribute__((cleanup(cleanup_string)))
6569
char *question_filename
6570
= make_question_file_in_directory(tempdir);
6571
g_assert_nonnull(question_filename);
6573
g_assert_true(assert_add_existing_questions_to_devnull
6577
&cancelled_filenames,
6579
&mandos_client_exited,
6583
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6585
g_assert_nonnull(find_matching_task(queue, (task_context){
6586
.func=open_and_parse_question,
6588
.filename=question_filename,
6589
.question_filename=question_filename,
6590
.password=&password,
6591
.cancelled_filenames=&cancelled_filenames,
6592
.current_time=¤t_time,
6593
.mandos_client_exited=&mandos_client_exited,
6594
.password_is_read=&password_is_read,
6597
g_assert_true(queue->next_run == 1);
6599
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6600
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6601
g_assert_cmpint(unlink(question_filename), ==, 0);
6602
g_assert_cmpint(rmdir(tempdir), ==, 0);
6605
static void test_wait_for_event_timeout(__attribute__((unused))
6606
test_fixture *fixture,
6607
__attribute__((unused))
6608
gconstpointer user_data){
6609
__attribute__((cleanup(cleanup_close)))
6610
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6611
g_assert_cmpint(epoll_fd, >=, 0);
6613
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6616
static void test_wait_for_event_event(__attribute__((unused))
6617
test_fixture *fixture,
6618
__attribute__((unused))
6619
gconstpointer user_data){
6620
__attribute__((cleanup(cleanup_close)))
6621
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6622
g_assert_cmpint(epoll_fd, >=, 0);
6624
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6625
__attribute__((cleanup(cleanup_close)))
6626
const int read_pipe = pipefds[0];
6627
__attribute__((cleanup(cleanup_close)))
6628
const int write_pipe = pipefds[1];
6629
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6630
&(struct epoll_event)
6631
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6632
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6634
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6637
static void test_wait_for_event_sigchld(test_fixture *fixture,
6638
__attribute__((unused))
6639
gconstpointer user_data){
6640
const pid_t pid = fork();
6641
if(pid == 0){ /* Child */
6642
if(not restore_signal_handler(&fixture->orig_sigaction)){
6643
_exit(EXIT_FAILURE);
6645
if(not restore_sigmask(&fixture->orig_sigmask)){
6646
_exit(EXIT_FAILURE);
6650
g_assert_true(pid != -1);
6651
__attribute__((cleanup(cleanup_close)))
6652
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6653
g_assert_cmpint(epoll_fd, >=, 0);
6655
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6658
g_assert_true(waitpid(pid, &status, 0) == pid);
6659
g_assert_true(WIFEXITED(status));
6660
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6663
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6664
test_fixture *fixture,
6665
__attribute__((unused))
6666
gconstpointer user_data){
6667
__attribute__((cleanup(cleanup_queue)))
6668
task_queue *queue = create_queue();
6669
g_assert_nonnull(queue);
6670
queue->next_run = 1;
6671
__attribute__((cleanup(cleanup_close)))
6672
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6673
__attribute__((cleanup(string_set_clear)))
6674
string_set cancelled_filenames = {};
6675
bool quit_now = false;
6677
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6678
g_assert_false(quit_now);
6679
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6683
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6684
test_fixture *fixture,
6685
__attribute__((unused))
6688
__attribute__((cleanup(cleanup_queue)))
6689
task_queue *queue = create_queue();
6690
g_assert_nonnull(queue);
6691
__attribute__((cleanup(string_set_clear)))
6692
string_set cancelled_filenames = {};
6693
bool quit_now = false;
6694
const char question_filename[] = "/nonexistent/question_filename";
6695
g_assert_true(string_set_add(&cancelled_filenames,
6696
question_filename));
6698
g_assert_true(add_to_queue(queue,
6699
(task_context){ .func=dummy_func }));
6701
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6702
g_assert_false(quit_now);
6703
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6704
g_assert_false(string_set_contains(cancelled_filenames,
6705
question_filename));
6709
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6710
test_fixture *fixture,
6711
__attribute__((unused))
6714
__attribute__((cleanup(cleanup_queue)))
6715
task_queue *queue = create_queue();
6716
g_assert_nonnull(queue);
6717
__attribute__((cleanup(string_set_clear)))
6718
string_set cancelled_filenames = {};
6719
bool quit_now = false;
6721
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6722
__attribute__((cleanup(cleanup_close)))
6723
const int read_pipe = pipefds[0];
6724
g_assert_cmpint(close(pipefds[1]), ==, 0);
6725
const char question_filename[] = "/nonexistent/question_filename";
6726
g_assert_true(string_set_add(&cancelled_filenames,
6727
question_filename));
6728
__attribute__((nonnull))
6729
void quit_func(const task_context task,
6730
__attribute__((unused)) task_queue *const q){
6731
g_assert_nonnull(task.quit_now);
6732
*task.quit_now = true;
6734
task_context task = {
6736
.question_filename=strdup(question_filename),
6737
.quit_now=&quit_now,
6740
g_assert_nonnull(task.question_filename);
6742
g_assert_true(add_to_queue(queue, task));
6744
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6745
g_assert_false(quit_now);
6747
/* read_pipe should be closed already */
6749
bool read_pipe_closed = (close(read_pipe) == -1);
6750
read_pipe_closed &= (errno == EBADF);
6751
g_assert_true(read_pipe_closed);
6754
static void test_run_queue_one_task(__attribute__((unused))
6755
test_fixture *fixture,
6756
__attribute__((unused))
6757
gconstpointer user_data){
6758
__attribute__((cleanup(cleanup_queue)))
6759
task_queue *queue = create_queue();
6760
g_assert_nonnull(queue);
6761
__attribute__((cleanup(string_set_clear)))
6762
string_set cancelled_filenames = {};
6763
bool quit_now = false;
6765
__attribute__((nonnull))
6766
void next_run_func(__attribute__((unused))
6767
const task_context task,
6768
task_queue *const q){
6772
task_context task = {
6773
.func=next_run_func,
6775
g_assert_true(add_to_queue(queue, task));
6777
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6778
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6779
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6782
static void test_run_queue_two_tasks(__attribute__((unused))
6783
test_fixture *fixture,
6784
__attribute__((unused))
6785
gconstpointer user_data){
6786
__attribute__((cleanup(cleanup_queue)))
6787
task_queue *queue = create_queue();
6788
g_assert_nonnull(queue);
6789
queue->next_run = 1;
6790
__attribute__((cleanup(string_set_clear)))
6791
string_set cancelled_filenames = {};
6792
bool quit_now = false;
6793
bool mandos_client_exited = false;
6795
__attribute__((nonnull))
6796
void next_run_func(__attribute__((unused))
6797
const task_context task,
6798
task_queue *const q){
6802
__attribute__((nonnull))
6803
void exited_func(const task_context task,
6804
__attribute__((unused)) task_queue *const q){
6805
*task.mandos_client_exited = true;
6808
task_context task1 = {
6809
.func=next_run_func,
6811
g_assert_true(add_to_queue(queue, task1));
6813
task_context task2 = {
6815
.mandos_client_exited=&mandos_client_exited,
6817
g_assert_true(add_to_queue(queue, task2));
6819
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6820
g_assert_false(quit_now);
6821
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6822
g_assert_true(mandos_client_exited);
6823
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6826
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6827
test_fixture *fixture,
6828
__attribute__((unused))
6829
gconstpointer user_data){
6830
__attribute__((cleanup(cleanup_queue)))
6831
task_queue *queue = create_queue();
6832
g_assert_nonnull(queue);
6833
__attribute__((cleanup(string_set_clear)))
6834
string_set cancelled_filenames = {};
6835
bool quit_now = false;
6836
bool mandos_client_exited = false;
6837
bool password_is_read = false;
6839
__attribute__((nonnull))
6840
void set_exited_func(const task_context task,
6841
__attribute__((unused)) task_queue *const q){
6842
*task.mandos_client_exited = true;
6843
*task.quit_now = true;
6845
task_context task1 = {
6846
.func=set_exited_func,
6847
.quit_now=&quit_now,
6848
.mandos_client_exited=&mandos_client_exited,
6850
g_assert_true(add_to_queue(queue, task1));
6852
__attribute__((nonnull))
6853
void set_read_func(const task_context task,
6854
__attribute__((unused)) task_queue *const q){
6855
*task.quit_now = true;
6856
*task.password_is_read = true;
6858
task_context task2 = {
6859
.func=set_read_func,
6860
.quit_now=&quit_now,
6861
.password_is_read=&password_is_read,
6863
g_assert_true(add_to_queue(queue, task2));
6865
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6866
g_assert_true(quit_now);
6867
g_assert_true(mandos_client_exited xor password_is_read);
6868
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6871
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6872
test_fixture *fixture,
6873
__attribute__((unused))
6874
gconstpointer user_data){
6875
__attribute__((cleanup(cleanup_queue)))
6876
task_queue *queue = create_queue();
6877
g_assert_nonnull(queue);
6878
__attribute__((cleanup(string_set_clear)))
6879
string_set cancelled_filenames = {};
6881
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6882
__attribute__((cleanup(cleanup_close)))
6883
const int read_pipe = pipefds[0];
6884
__attribute__((cleanup(cleanup_close)))
6885
const int write_pipe = pipefds[1];
6886
bool quit_now = false;
6888
__attribute__((nonnull))
6889
void read_func(const task_context task,
6890
__attribute__((unused)) task_queue *const q){
6891
*task.quit_now = true;
6893
task_context task1 = {
6895
.quit_now=&quit_now,
6898
g_assert_true(add_to_queue(queue, task1));
6900
__attribute__((nonnull))
6901
void write_func(const task_context task,
6902
__attribute__((unused)) task_queue *const q){
6903
*task.quit_now = true;
6905
task_context task2 = {
6907
.quit_now=&quit_now,
6910
g_assert_true(add_to_queue(queue, task2));
6912
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6913
g_assert_true(quit_now);
6915
/* Either read_pipe or write_pipe should be closed already */
6917
bool close_read_pipe = (close(read_pipe) == -1);
6918
close_read_pipe &= (errno == EBADF);
6920
bool close_write_pipe = (close(write_pipe) == -1);
6921
close_write_pipe &= (errno == EBADF);
6922
g_assert_true(close_read_pipe xor close_write_pipe);
6923
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6926
static void test_setup_signal_handler(__attribute__((unused))
6927
test_fixture *fixture,
6928
__attribute__((unused))
6929
gconstpointer user_data){
6930
/* Save current SIGCHLD action, whatever it is */
6931
struct sigaction expected_sigchld_action;
6932
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6935
/* Act; i.e. run the setup_signal_handler() function */
6936
struct sigaction actual_old_sigchld_action;
6937
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6939
/* Check that the function correctly set "actual_old_sigchld_action"
6940
to the same values as the previously saved
6941
"expected_sigchld_action" */
6942
/* Check member sa_handler */
6943
g_assert_true(actual_old_sigchld_action.sa_handler
6944
== expected_sigchld_action.sa_handler);
6945
/* Check member sa_mask */
6946
for(int signum = 1; signum < NSIG; signum++){
6947
const int expected_old_block_state
6948
= sigismember(&expected_sigchld_action.sa_mask, signum);
6949
g_assert_cmpint(expected_old_block_state, >=, 0);
6950
const int actual_old_block_state
6951
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6952
g_assert_cmpint(actual_old_block_state, >=, 0);
6953
g_assert_cmpint(actual_old_block_state,
6954
==, expected_old_block_state);
6956
/* Check member sa_flags */
6957
g_assert_true((actual_old_sigchld_action.sa_flags
6958
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6959
== (expected_sigchld_action.sa_flags
6960
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6962
/* Retrieve the current signal handler for SIGCHLD as set by
6963
setup_signal_handler() */
6964
struct sigaction actual_new_sigchld_action;
6965
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6966
&actual_new_sigchld_action), ==, 0);
6967
/* Check that the signal handler (member sa_handler) is correctly
6968
set to the "handle_sigchld" function */
6969
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6970
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6971
g_assert_true(actual_new_sigchld_action.sa_handler
6973
/* Check (in member sa_mask) that at least a handful of signals are
6974
actually blocked during the signal handler */
6975
for(int signum = 1; signum < NSIG; signum++){
6976
int actual_new_block_state;
6982
actual_new_block_state
6983
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6984
g_assert_cmpint(actual_new_block_state, ==, 1);
6986
case SIGKILL: /* non-blockable */
6987
case SIGSTOP: /* non-blockable */
6988
case SIGCHLD: /* always blocked */
6993
/* Check member sa_flags */
6994
g_assert_true((actual_new_sigchld_action.sa_flags
6995
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6996
== (SA_NOCLDSTOP | SA_RESTART));
6998
/* Restore signal handler */
6999
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7003
static void test_restore_signal_handler(__attribute__((unused))
7004
test_fixture *fixture,
7005
__attribute__((unused))
7006
gconstpointer user_data){
7007
/* Save current SIGCHLD action, whatever it is */
7008
struct sigaction expected_sigchld_action;
7009
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7011
/* Since we haven't established a signal handler yet, there should
7012
not be one established. But another test may have relied on
7013
restore_signal_handler() to restore the signal handler, and if
7014
restore_signal_handler() is buggy (which we should be prepared
7015
for in this test) the signal handler may not have been restored
7016
properly; check for this: */
7017
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7019
/* Establish a signal handler */
7020
struct sigaction sigchld_action = {
7021
.sa_handler=handle_sigchld,
7022
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7024
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7025
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7027
/* Act; i.e. run the restore_signal_handler() function */
7028
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7030
/* Retrieve the restored signal handler data */
7031
struct sigaction actual_restored_sigchld_action;
7032
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7033
&actual_restored_sigchld_action), ==, 0);
7035
/* Check that the function correctly restored the signal action, as
7036
saved in "actual_restored_sigchld_action", to the same values as
7037
the previously saved "expected_sigchld_action" */
7038
/* Check member sa_handler */
7039
g_assert_true(actual_restored_sigchld_action.sa_handler
7040
== expected_sigchld_action.sa_handler);
7041
/* Check member sa_mask */
7042
for(int signum = 1; signum < NSIG; signum++){
7043
const int expected_old_block_state
7044
= sigismember(&expected_sigchld_action.sa_mask, signum);
7045
g_assert_cmpint(expected_old_block_state, >=, 0);
7046
const int actual_restored_block_state
7047
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7048
g_assert_cmpint(actual_restored_block_state, >=, 0);
7049
g_assert_cmpint(actual_restored_block_state,
7050
==, expected_old_block_state);
7052
/* Check member sa_flags */
7053
g_assert_true((actual_restored_sigchld_action.sa_flags
7054
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7055
== (expected_sigchld_action.sa_flags
7056
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7059
static void test_block_sigchld(__attribute__((unused))
7060
test_fixture *fixture,
7061
__attribute__((unused))
7062
gconstpointer user_data){
7063
/* Save original signal mask */
7064
sigset_t expected_sigmask;
7065
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7068
/* Make sure SIGCHLD is unblocked for this test */
7069
sigset_t sigchld_sigmask;
7070
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7071
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7072
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7075
/* Act; i.e. run the block_sigchld() function */
7076
sigset_t actual_old_sigmask;
7077
g_assert_true(block_sigchld(&actual_old_sigmask));
7079
/* Check the actual_old_sigmask; it should be the same as the
7080
previously saved signal mask "expected_sigmask". */
7081
for(int signum = 1; signum < NSIG; signum++){
7082
const int expected_old_block_state
7083
= sigismember(&expected_sigmask, signum);
7084
g_assert_cmpint(expected_old_block_state, >=, 0);
7085
const int actual_old_block_state
7086
= sigismember(&actual_old_sigmask, signum);
7087
g_assert_cmpint(actual_old_block_state, >=, 0);
7088
g_assert_cmpint(actual_old_block_state,
7089
==, expected_old_block_state);
7092
/* Retrieve the newly set signal mask */
7093
sigset_t actual_sigmask;
7094
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7096
/* SIGCHLD should be blocked */
7097
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7099
/* Restore signal mask */
7100
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7104
static void test_restore_sigmask(__attribute__((unused))
7105
test_fixture *fixture,
7106
__attribute__((unused))
7107
gconstpointer user_data){
7108
/* Save original signal mask */
7109
sigset_t orig_sigmask;
7110
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7112
/* Make sure SIGCHLD is blocked for this test */
7113
sigset_t sigchld_sigmask;
7114
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7115
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7116
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7119
/* Act; i.e. run the restore_sigmask() function */
7120
g_assert_true(restore_sigmask(&orig_sigmask));
7122
/* Retrieve the newly restored signal mask */
7123
sigset_t restored_sigmask;
7124
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7127
/* Check the restored_sigmask; it should be the same as the
7128
previously saved signal mask "orig_sigmask". */
7129
for(int signum = 1; signum < NSIG; signum++){
7130
const int orig_block_state = sigismember(&orig_sigmask, signum);
7131
g_assert_cmpint(orig_block_state, >=, 0);
7132
const int restored_block_state = sigismember(&restored_sigmask,
7134
g_assert_cmpint(restored_block_state, >=, 0);
7135
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7138
/* Restore signal mask */
7139
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7143
static void test_parse_arguments_noargs(__attribute__((unused))
7144
test_fixture *fixture,
7145
__attribute__((unused))
7146
gconstpointer user_data){
7150
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7152
char *agent_directory = NULL;
7153
char *helper_directory = NULL;
7156
char *mandos_argz = NULL;
7157
size_t mandos_argz_length = 0;
7159
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7160
&helper_directory, &user, &group,
7161
&mandos_argz, &mandos_argz_length));
7162
g_assert_null(agent_directory);
7163
g_assert_null(helper_directory);
7164
g_assert_true(user == 0);
7165
g_assert_true(group == 0);
7166
g_assert_null(mandos_argz);
7167
g_assert_true(mandos_argz_length == 0);
7169
for(char **arg = argv; *arg != NULL; arg++){
7174
__attribute__((nonnull))
7175
static bool parse_arguments_devnull(int argc, char *argv[],
7176
const bool exit_failure,
7177
char **agent_directory,
7178
char **helper_directory,
7182
size_t *mandos_argz_length){
7184
FILE *real_stderr = stderr;
7185
FILE *devnull = fopen("/dev/null", "we");
7186
g_assert_nonnull(devnull);
7189
const bool ret = parse_arguments(argc, argv, exit_failure,
7191
helper_directory, user, group,
7192
mandos_argz, mandos_argz_length);
7193
const error_t saved_errno = errno;
7195
stderr = real_stderr;
7196
g_assert_cmpint(fclose(devnull), ==, 0);
7198
errno = saved_errno;
7203
static void test_parse_arguments_invalid(__attribute__((unused))
7204
test_fixture *fixture,
7205
__attribute__((unused))
7206
gconstpointer user_data){
7209
strdup("--invalid"),
7211
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7213
char *agent_directory = NULL;
7214
char *helper_directory = NULL;
7217
char *mandos_argz = NULL;
7218
size_t mandos_argz_length = 0;
7220
g_assert_false(parse_arguments_devnull(argc, argv, false,
7222
&helper_directory, &user,
7223
&group, &mandos_argz,
7224
&mandos_argz_length));
7226
g_assert_true(errno == EINVAL);
7227
g_assert_null(agent_directory);
7228
g_assert_null(helper_directory);
7229
g_assert_null(mandos_argz);
7230
g_assert_true(mandos_argz_length == 0);
7232
for(char **arg = argv; *arg != NULL; arg++){
7237
static void test_parse_arguments_long_dir(__attribute__((unused))
7238
test_fixture *fixture,
7239
__attribute__((unused))
7240
gconstpointer user_data){
7243
strdup("--agent-directory"),
7246
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7248
__attribute__((cleanup(cleanup_string)))
7249
char *agent_directory = NULL;
7250
char *helper_directory = NULL;
7253
__attribute__((cleanup(cleanup_string)))
7254
char *mandos_argz = NULL;
7255
size_t mandos_argz_length = 0;
7257
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7258
&helper_directory, &user, &group,
7259
&mandos_argz, &mandos_argz_length));
7261
g_assert_cmpstr(agent_directory, ==, "/tmp");
7262
g_assert_null(helper_directory);
7263
g_assert_true(user == 0);
7264
g_assert_true(group == 0);
7265
g_assert_null(mandos_argz);
7266
g_assert_true(mandos_argz_length == 0);
7268
for(char **arg = argv; *arg != NULL; arg++){
7273
static void test_parse_arguments_short_dir(__attribute__((unused))
7274
test_fixture *fixture,
7275
__attribute__((unused))
7276
gconstpointer user_data){
7282
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7284
__attribute__((cleanup(cleanup_string)))
7285
char *agent_directory = NULL;
7286
char *helper_directory = NULL;
7289
__attribute__((cleanup(cleanup_string)))
7290
char *mandos_argz = NULL;
7291
size_t mandos_argz_length = 0;
7293
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7294
&helper_directory, &user, &group,
7295
&mandos_argz, &mandos_argz_length));
7297
g_assert_cmpstr(agent_directory, ==, "/tmp");
7298
g_assert_null(helper_directory);
7299
g_assert_true(user == 0);
7300
g_assert_true(group == 0);
7301
g_assert_null(mandos_argz);
7302
g_assert_true(mandos_argz_length == 0);
7304
for(char **arg = argv; *arg != NULL; arg++){
7310
void test_parse_arguments_helper_directory(__attribute__((unused))
7311
test_fixture *fixture,
7312
__attribute__((unused))
7313
gconstpointer user_data){
7316
strdup("--helper-directory"),
7319
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7321
char *agent_directory = NULL;
7322
__attribute__((cleanup(cleanup_string)))
7323
char *helper_directory = NULL;
7326
__attribute__((cleanup(cleanup_string)))
7327
char *mandos_argz = NULL;
7328
size_t mandos_argz_length = 0;
7330
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7331
&helper_directory, &user, &group,
7332
&mandos_argz, &mandos_argz_length));
7334
g_assert_cmpstr(helper_directory, ==, "/tmp");
7335
g_assert_null(agent_directory);
7336
g_assert_true(user == 0);
7337
g_assert_true(group == 0);
7338
g_assert_null(mandos_argz);
7339
g_assert_true(mandos_argz_length == 0);
7341
for(char **arg = argv; *arg != NULL; arg++){
7347
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7348
test_fixture *fixture,
7349
__attribute__((unused))
7350
gconstpointer user_data){
7353
strdup("--plugin-helper-dir"),
7356
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7358
char *agent_directory = NULL;
7359
__attribute__((cleanup(cleanup_string)))
7360
char *helper_directory = NULL;
7363
__attribute__((cleanup(cleanup_string)))
7364
char *mandos_argz = NULL;
7365
size_t mandos_argz_length = 0;
7367
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7368
&helper_directory, &user, &group,
7369
&mandos_argz, &mandos_argz_length));
7371
g_assert_cmpstr(helper_directory, ==, "/tmp");
7372
g_assert_null(agent_directory);
7373
g_assert_true(user == 0);
7374
g_assert_true(group == 0);
7375
g_assert_null(mandos_argz);
7376
g_assert_true(mandos_argz_length == 0);
7378
for(char **arg = argv; *arg != NULL; arg++){
7383
static void test_parse_arguments_user(__attribute__((unused))
7384
test_fixture *fixture,
7385
__attribute__((unused))
7386
gconstpointer user_data){
7392
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7394
char *agent_directory = NULL;
7395
__attribute__((cleanup(cleanup_string)))
7396
char *helper_directory = NULL;
7399
__attribute__((cleanup(cleanup_string)))
7400
char *mandos_argz = NULL;
7401
size_t mandos_argz_length = 0;
7403
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7404
&helper_directory, &user, &group,
7405
&mandos_argz, &mandos_argz_length));
7407
g_assert_null(helper_directory);
7408
g_assert_null(agent_directory);
7409
g_assert_cmpuint((unsigned int)user, ==, 1000);
7410
g_assert_true(group == 0);
7411
g_assert_null(mandos_argz);
7412
g_assert_true(mandos_argz_length == 0);
7414
for(char **arg = argv; *arg != NULL; arg++){
7419
static void test_parse_arguments_user_invalid(__attribute__((unused))
7420
test_fixture *fixture,
7421
__attribute__((unused))
7429
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7431
char *agent_directory = NULL;
7432
__attribute__((cleanup(cleanup_string)))
7433
char *helper_directory = NULL;
7436
__attribute__((cleanup(cleanup_string)))
7437
char *mandos_argz = NULL;
7438
size_t mandos_argz_length = 0;
7440
g_assert_false(parse_arguments_devnull(argc, argv, false,
7442
&helper_directory, &user,
7443
&group, &mandos_argz,
7444
&mandos_argz_length));
7446
g_assert_null(helper_directory);
7447
g_assert_null(agent_directory);
7448
g_assert_cmpuint((unsigned int)user, ==, 0);
7449
g_assert_true(group == 0);
7450
g_assert_null(mandos_argz);
7451
g_assert_true(mandos_argz_length == 0);
7453
for(char **arg = argv; *arg != NULL; arg++){
7459
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7460
test_fixture *fixture,
7461
__attribute__((unused))
7462
gconstpointer user_data){
7468
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7470
char *agent_directory = NULL;
7471
__attribute__((cleanup(cleanup_string)))
7472
char *helper_directory = NULL;
7475
__attribute__((cleanup(cleanup_string)))
7476
char *mandos_argz = NULL;
7477
size_t mandos_argz_length = 0;
7479
g_assert_false(parse_arguments_devnull(argc, argv, false,
7481
&helper_directory, &user,
7482
&group, &mandos_argz,
7483
&mandos_argz_length));
7485
g_assert_null(helper_directory);
7486
g_assert_null(agent_directory);
7487
g_assert_cmpuint((unsigned int)user, ==, 0);
7488
g_assert_true(group == 0);
7489
g_assert_null(mandos_argz);
7490
g_assert_true(mandos_argz_length == 0);
7492
for(char **arg = argv; *arg != NULL; arg++){
7497
static void test_parse_arguments_group(__attribute__((unused))
7498
test_fixture *fixture,
7499
__attribute__((unused))
7500
gconstpointer user_data){
7506
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7508
char *agent_directory = NULL;
7509
__attribute__((cleanup(cleanup_string)))
7510
char *helper_directory = NULL;
7513
__attribute__((cleanup(cleanup_string)))
7514
char *mandos_argz = NULL;
7515
size_t mandos_argz_length = 0;
7517
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7518
&helper_directory, &user, &group,
7519
&mandos_argz, &mandos_argz_length));
7521
g_assert_null(helper_directory);
7522
g_assert_null(agent_directory);
7523
g_assert_true(user == 0);
7524
g_assert_cmpuint((unsigned int)group, ==, 1000);
7525
g_assert_null(mandos_argz);
7526
g_assert_true(mandos_argz_length == 0);
7528
for(char **arg = argv; *arg != NULL; arg++){
7533
static void test_parse_arguments_group_invalid(__attribute__((unused))
7534
test_fixture *fixture,
7535
__attribute__((unused))
7543
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7545
char *agent_directory = NULL;
7546
__attribute__((cleanup(cleanup_string)))
7547
char *helper_directory = NULL;
7550
__attribute__((cleanup(cleanup_string)))
7551
char *mandos_argz = NULL;
7552
size_t mandos_argz_length = 0;
7554
g_assert_false(parse_arguments_devnull(argc, argv, false,
7556
&helper_directory, &user,
7557
&group, &mandos_argz,
7558
&mandos_argz_length));
7560
g_assert_null(helper_directory);
7561
g_assert_null(agent_directory);
7562
g_assert_true(user == 0);
7563
g_assert_true(group == 0);
7564
g_assert_null(mandos_argz);
7565
g_assert_true(mandos_argz_length == 0);
7567
for(char **arg = argv; *arg != NULL; arg++){
7573
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7574
test_fixture *fixture,
7575
__attribute__((unused))
7576
gconstpointer user_data){
7582
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7584
char *agent_directory = NULL;
7585
__attribute__((cleanup(cleanup_string)))
7586
char *helper_directory = NULL;
7589
__attribute__((cleanup(cleanup_string)))
7590
char *mandos_argz = NULL;
7591
size_t mandos_argz_length = 0;
7593
g_assert_false(parse_arguments_devnull(argc, argv, false,
7595
&helper_directory, &user,
7596
&group, &mandos_argz,
7597
&mandos_argz_length));
7599
g_assert_null(helper_directory);
7600
g_assert_null(agent_directory);
7601
g_assert_cmpuint((unsigned int)group, ==, 0);
7602
g_assert_true(group == 0);
7603
g_assert_null(mandos_argz);
7604
g_assert_true(mandos_argz_length == 0);
7606
for(char **arg = argv; *arg != NULL; arg++){
7611
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7612
test_fixture *fixture,
7613
__attribute__((unused))
7618
strdup("mandos-client"),
7620
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7622
__attribute__((cleanup(cleanup_string)))
7623
char *agent_directory = NULL;
7624
__attribute__((cleanup(cleanup_string)))
7625
char *helper_directory = NULL;
7628
__attribute__((cleanup(cleanup_string)))
7629
char *mandos_argz = NULL;
7630
size_t mandos_argz_length = 0;
7632
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7633
&helper_directory, &user, &group,
7634
&mandos_argz, &mandos_argz_length));
7636
g_assert_null(agent_directory);
7637
g_assert_null(helper_directory);
7638
g_assert_true(user == 0);
7639
g_assert_true(group == 0);
7640
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7641
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7642
mandos_argz_length),
7645
for(char **arg = argv; *arg != NULL; arg++){
7650
static void test_parse_arguments_mandos_args(__attribute__((unused))
7651
test_fixture *fixture,
7652
__attribute__((unused))
7653
gconstpointer user_data){
7656
strdup("mandos-client"),
7661
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7663
__attribute__((cleanup(cleanup_string)))
7664
char *agent_directory = NULL;
7665
__attribute__((cleanup(cleanup_string)))
7666
char *helper_directory = NULL;
7669
__attribute__((cleanup(cleanup_string)))
7670
char *mandos_argz = NULL;
7671
size_t mandos_argz_length = 0;
7673
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7674
&helper_directory, &user, &group,
7675
&mandos_argz, &mandos_argz_length));
7677
g_assert_null(agent_directory);
7678
g_assert_null(helper_directory);
7679
g_assert_true(user == 0);
7680
g_assert_true(group == 0);
7681
char *marg = mandos_argz;
7682
g_assert_cmpstr(marg, ==, "mandos-client");
7683
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7684
g_assert_cmpstr(marg, ==, "one");
7685
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7686
g_assert_cmpstr(marg, ==, "two");
7687
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7688
g_assert_cmpstr(marg, ==, "three");
7689
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7690
mandos_argz_length),
7693
for(char **arg = argv; *arg != NULL; arg++){
7698
static void test_parse_arguments_all_args(__attribute__((unused))
7699
test_fixture *fixture,
7700
__attribute__((unused))
7701
gconstpointer user_data){
7704
strdup("--agent-directory"),
7706
strdup("--helper-directory"),
7712
strdup("mandos-client"),
7717
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7719
__attribute__((cleanup(cleanup_string)))
7720
char *agent_directory = NULL;
7721
__attribute__((cleanup(cleanup_string)))
7722
char *helper_directory = NULL;
7725
__attribute__((cleanup(cleanup_string)))
7726
char *mandos_argz = NULL;
7727
size_t mandos_argz_length = 0;
7729
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7730
&helper_directory, &user, &group,
7731
&mandos_argz, &mandos_argz_length));
7733
g_assert_cmpstr(agent_directory, ==, "/tmp");
7734
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7735
g_assert_true(user == 1);
7736
g_assert_true(group == 2);
7737
char *marg = mandos_argz;
7738
g_assert_cmpstr(marg, ==, "mandos-client");
7739
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7740
g_assert_cmpstr(marg, ==, "one");
7741
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7742
g_assert_cmpstr(marg, ==, "two");
7743
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7744
g_assert_cmpstr(marg, ==, "three");
7745
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7746
mandos_argz_length),
7749
for(char **arg = argv; *arg != NULL; arg++){
7754
static void test_parse_arguments_mixed(__attribute__((unused))
7755
test_fixture *fixture,
7756
__attribute__((unused))
7757
gconstpointer user_data){
7760
strdup("mandos-client"),
7764
strdup("--agent-directory"),
7768
strdup("--helper-directory=/var/tmp"),
7770
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7772
__attribute__((cleanup(cleanup_string)))
7773
char *agent_directory = NULL;
7774
__attribute__((cleanup(cleanup_string)))
7775
char *helper_directory = NULL;
7778
__attribute__((cleanup(cleanup_string)))
7779
char *mandos_argz = NULL;
7780
size_t mandos_argz_length = 0;
7782
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7783
&helper_directory, &user, &group,
7784
&mandos_argz, &mandos_argz_length));
7786
g_assert_cmpstr(agent_directory, ==, "/tmp");
7787
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7788
g_assert_true(user == 1);
7789
g_assert_true(group == 0);
7790
char *marg = mandos_argz;
7791
g_assert_cmpstr(marg, ==, "mandos-client");
7792
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7793
g_assert_cmpstr(marg, ==, "one");
7794
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7795
g_assert_cmpstr(marg, ==, "two");
7796
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7797
g_assert_cmpstr(marg, ==, "three");
7798
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7799
mandos_argz_length),
7802
for(char **arg = argv; *arg != NULL; arg++){
7807
/* End of tests section */
7809
/* Test boilerplate section; New tests should be added to the test
7810
suite definition here, in the "run_tests" function.
7812
Finally, this section also contains the should_only_run_tests()
7813
function used by main() for deciding if tests should be run or to
7816
__attribute__((cold))
7817
static bool run_tests(int argc, char *argv[]){
7818
g_test_init(&argc, &argv, NULL);
7820
/* A macro to add a test with no setup or teardown functions */
7821
#define test_add(testpath, testfunc) \
7823
g_test_add((testpath), test_fixture, NULL, NULL, \
7824
(testfunc), NULL); \
7827
/* Test the signal-related functions first, since some other tests
7828
depend on these functions in their setups and teardowns */
7829
test_add("/signal-handling/setup", test_setup_signal_handler);
7830
test_add("/signal-handling/restore", test_restore_signal_handler);
7831
test_add("/signal-handling/block", test_block_sigchld);
7832
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7834
/* Regular non-signal-related tests; these use no setups or
7836
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7837
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7838
test_add("/parse_arguments/long-dir",
7839
test_parse_arguments_long_dir);
7840
test_add("/parse_arguments/short-dir",
7841
test_parse_arguments_short_dir);
7842
test_add("/parse_arguments/helper-directory",
7843
test_parse_arguments_helper_directory);
7844
test_add("/parse_arguments/plugin-helper-dir",
7845
test_parse_arguments_plugin_helper_dir);
7846
test_add("/parse_arguments/user", test_parse_arguments_user);
7847
test_add("/parse_arguments/user-invalid",
7848
test_parse_arguments_user_invalid);
7849
test_add("/parse_arguments/user-zero-invalid",
7850
test_parse_arguments_user_zero_invalid);
7851
test_add("/parse_arguments/group", test_parse_arguments_group);
7852
test_add("/parse_arguments/group-invalid",
7853
test_parse_arguments_group_invalid);
7854
test_add("/parse_arguments/group-zero-invalid",
7855
test_parse_arguments_group_zero_invalid);
7856
test_add("/parse_arguments/mandos-noargs",
7857
test_parse_arguments_mandos_noargs);
7858
test_add("/parse_arguments/mandos-args",
7859
test_parse_arguments_mandos_args);
7860
test_add("/parse_arguments/all-args",
7861
test_parse_arguments_all_args);
7862
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7863
test_add("/queue/create", test_create_queue);
7864
test_add("/queue/add", test_add_to_queue);
7865
test_add("/queue/has_question/empty",
7866
test_queue_has_question_empty);
7867
test_add("/queue/has_question/false",
7868
test_queue_has_question_false);
7869
test_add("/queue/has_question/true", test_queue_has_question_true);
7870
test_add("/queue/has_question/false2",
7871
test_queue_has_question_false2);
7872
test_add("/queue/has_question/true2",
7873
test_queue_has_question_true2);
7874
test_add("/buffer/cleanup", test_cleanup_buffer);
7875
test_add("/string_set/net-set-contains-nothing",
7876
test_string_set_new_set_contains_nothing);
7877
test_add("/string_set/with-added-string-contains-it",
7878
test_string_set_with_added_string_contains_it);
7879
test_add("/string_set/cleared-does-not-contain-string",
7880
test_string_set_cleared_does_not_contain_str);
7881
test_add("/string_set/swap/one-with-empty",
7882
test_string_set_swap_one_with_empty);
7883
test_add("/string_set/swap/empty-with-one",
7884
test_string_set_swap_empty_with_one);
7885
test_add("/string_set/swap/one-with-one",
7886
test_string_set_swap_one_with_one);
7888
/* A macro to add a test using the setup and teardown functions */
7889
#define test_add_st(path, func) \
7891
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7895
/* Signal-related tests; these use setups and teardowns which
7896
establish, during each test run, a signal handler for, and a
7897
signal mask blocking, the SIGCHLD signal, just like main() */
7898
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7899
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7900
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7901
test_add_st("/run_queue/zeroes-next-run",
7902
test_run_queue_zeroes_next_run);
7903
test_add_st("/run_queue/clears-cancelled_filenames",
7904
test_run_queue_clears_cancelled_filenames);
7905
test_add_st("/run_queue/skips-cancelled-filenames",
7906
test_run_queue_skips_cancelled_filenames);
7907
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7908
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7909
test_add_st("/run_queue/two-tasks/quit",
7910
test_run_queue_two_tasks_quit);
7911
test_add_st("/run_queue/two-tasks-cleanup",
7912
test_run_queue_two_tasks_cleanup);
7913
test_add_st("/task-creators/start_mandos_client",
7914
test_start_mandos_client);
7915
test_add_st("/task-creators/start_mandos_client/execv",
7916
test_start_mandos_client_execv);
7917
test_add_st("/task-creators/start_mandos_client/suid/euid",
7918
test_start_mandos_client_suid_euid);
7919
test_add_st("/task-creators/start_mandos_client/suid/egid",
7920
test_start_mandos_client_suid_egid);
7921
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7922
test_start_mandos_client_suid_ruid);
7923
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7924
test_start_mandos_client_suid_rgid);
7925
test_add_st("/task-creators/start_mandos_client/read",
7926
test_start_mandos_client_read);
7927
test_add_st("/task-creators/start_mandos_client/helper-directory",
7928
test_start_mandos_client_helper_directory);
7929
test_add_st("/task-creators/start_mandos_client/sigmask",
7930
test_start_mandos_client_sigmask);
7931
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7932
test_wait_for_mandos_client_exit_badpid);
7933
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7934
test_wait_for_mandos_client_exit_noexit);
7935
test_add_st("/task/wait_for_mandos_client_exit/success",
7936
test_wait_for_mandos_client_exit_success);
7937
test_add_st("/task/wait_for_mandos_client_exit/failure",
7938
test_wait_for_mandos_client_exit_failure);
7939
test_add_st("/task/wait_for_mandos_client_exit/killed",
7940
test_wait_for_mandos_client_exit_killed);
7941
test_add_st("/task/read_mandos_client_output/readerror",
7942
test_read_mandos_client_output_readerror);
7943
test_add_st("/task/read_mandos_client_output/nodata",
7944
test_read_mandos_client_output_nodata);
7945
test_add_st("/task/read_mandos_client_output/eof",
7946
test_read_mandos_client_output_eof);
7947
test_add_st("/task/read_mandos_client_output/once",
7948
test_read_mandos_client_output_once);
7949
test_add_st("/task/read_mandos_client_output/malloc",
7950
test_read_mandos_client_output_malloc);
7951
test_add_st("/task/read_mandos_client_output/append",
7952
test_read_mandos_client_output_append);
7953
test_add_st("/task-creators/add_inotify_dir_watch",
7954
test_add_inotify_dir_watch);
7955
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7956
test_add_inotify_dir_watch_fail);
7957
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7958
test_add_inotify_dir_watch_nondir);
7959
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7960
test_add_inotify_dir_watch_EAGAIN);
7961
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7962
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7963
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7964
test_add_inotify_dir_watch_IN_MOVED_TO);
7965
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7966
test_add_inotify_dir_watch_IN_MOVED_FROM);
7967
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7968
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7969
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7970
test_add_inotify_dir_watch_IN_DELETE);
7971
test_add_st("/task/read_inotify_event/readerror",
7972
test_read_inotify_event_readerror);
7973
test_add_st("/task/read_inotify_event/bad-epoll",
7974
test_read_inotify_event_bad_epoll);
7975
test_add_st("/task/read_inotify_event/nodata",
7976
test_read_inotify_event_nodata);
7977
test_add_st("/task/read_inotify_event/eof",
7978
test_read_inotify_event_eof);
7979
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7980
test_read_inotify_event_IN_CLOSE_WRITE);
7981
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7982
test_read_inotify_event_IN_MOVED_TO);
7983
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7984
test_read_inotify_event_IN_MOVED_FROM);
7985
test_add_st("/task/read_inotify_event/IN_DELETE",
7986
test_read_inotify_event_IN_DELETE);
7987
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7988
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7989
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7990
test_read_inotify_event_IN_MOVED_TO_badname);
7991
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7992
test_read_inotify_event_IN_MOVED_FROM_badname);
7993
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7994
test_read_inotify_event_IN_DELETE_badname);
7995
test_add_st("/task/open_and_parse_question/ENOENT",
7996
test_open_and_parse_question_ENOENT);
7997
test_add_st("/task/open_and_parse_question/EIO",
7998
test_open_and_parse_question_EIO);
7999
test_add_st("/task/open_and_parse_question/parse-error",
8000
test_open_and_parse_question_parse_error);
8001
test_add_st("/task/open_and_parse_question/nosocket",
8002
test_open_and_parse_question_nosocket);
8003
test_add_st("/task/open_and_parse_question/badsocket",
8004
test_open_and_parse_question_badsocket);
8005
test_add_st("/task/open_and_parse_question/nopid",
8006
test_open_and_parse_question_nopid);
8007
test_add_st("/task/open_and_parse_question/badpid",
8008
test_open_and_parse_question_badpid);
8009
test_add_st("/task/open_and_parse_question/noexist_pid",
8010
test_open_and_parse_question_noexist_pid);
8011
test_add_st("/task/open_and_parse_question/no-notafter",
8012
test_open_and_parse_question_no_notafter);
8013
test_add_st("/task/open_and_parse_question/bad-notafter",
8014
test_open_and_parse_question_bad_notafter);
8015
test_add_st("/task/open_and_parse_question/notafter-0",
8016
test_open_and_parse_question_notafter_0);
8017
test_add_st("/task/open_and_parse_question/notafter-1",
8018
test_open_and_parse_question_notafter_1);
8019
test_add_st("/task/open_and_parse_question/notafter-1-1",
8020
test_open_and_parse_question_notafter_1_1);
8021
test_add_st("/task/open_and_parse_question/notafter-1-2",
8022
test_open_and_parse_question_notafter_1_2);
8023
test_add_st("/task/open_and_parse_question/equal-notafter",
8024
test_open_and_parse_question_equal_notafter);
8025
test_add_st("/task/open_and_parse_question/late-notafter",
8026
test_open_and_parse_question_late_notafter);
8027
test_add_st("/task/cancel_old_question/0-1-2",
8028
test_cancel_old_question_0_1_2);
8029
test_add_st("/task/cancel_old_question/0-2-1",
8030
test_cancel_old_question_0_2_1);
8031
test_add_st("/task/cancel_old_question/1-2-3",
8032
test_cancel_old_question_1_2_3);
8033
test_add_st("/task/cancel_old_question/1-3-2",
8034
test_cancel_old_question_1_3_2);
8035
test_add_st("/task/cancel_old_question/2-1-3",
8036
test_cancel_old_question_2_1_3);
8037
test_add_st("/task/cancel_old_question/2-3-1",
8038
test_cancel_old_question_2_3_1);
8039
test_add_st("/task/cancel_old_question/3-1-2",
8040
test_cancel_old_question_3_1_2);
8041
test_add_st("/task/cancel_old_question/3-2-1",
8042
test_cancel_old_question_3_2_1);
8043
test_add_st("/task/connect_question_socket/name-too-long",
8044
test_connect_question_socket_name_too_long);
8045
test_add_st("/task/connect_question_socket/connect-fail",
8046
test_connect_question_socket_connect_fail);
8047
test_add_st("/task/connect_question_socket/bad-epoll",
8048
test_connect_question_socket_bad_epoll);
8049
test_add_st("/task/connect_question_socket/usable",
8050
test_connect_question_socket_usable);
8051
test_add_st("/task/send_password_to_socket/client-not-exited",
8052
test_send_password_to_socket_client_not_exited);
8053
test_add_st("/task/send_password_to_socket/password-not-read",
8054
test_send_password_to_socket_password_not_read);
8055
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8056
test_send_password_to_socket_EMSGSIZE);
8057
test_add_st("/task/send_password_to_socket/retry",
8058
test_send_password_to_socket_retry);
8059
test_add_st("/task/send_password_to_socket/bad-epoll",
8060
test_send_password_to_socket_bad_epoll);
8061
test_add_st("/task/send_password_to_socket/null-password",
8062
test_send_password_to_socket_null_password);
8063
test_add_st("/task/send_password_to_socket/empty-password",
8064
test_send_password_to_socket_empty_password);
8065
test_add_st("/task/send_password_to_socket/empty-str-password",
8066
test_send_password_to_socket_empty_str_pass);
8067
test_add_st("/task/send_password_to_socket/text-password",
8068
test_send_password_to_socket_text_password);
8069
test_add_st("/task/send_password_to_socket/binary-password",
8070
test_send_password_to_socket_binary_password);
8071
test_add_st("/task/send_password_to_socket/nuls-in-password",
8072
test_send_password_to_socket_nuls_in_password);
8073
test_add_st("/task-creators/add_existing_questions/ENOENT",
8074
test_add_existing_questions_ENOENT);
8075
test_add_st("/task-creators/add_existing_questions/no-questions",
8076
test_add_existing_questions_no_questions);
8077
test_add_st("/task-creators/add_existing_questions/one-question",
8078
test_add_existing_questions_one_question);
8079
test_add_st("/task-creators/add_existing_questions/two-questions",
8080
test_add_existing_questions_two_questions);
8081
test_add_st("/task-creators/add_existing_questions/non-questions",
8082
test_add_existing_questions_non_questions);
8083
test_add_st("/task-creators/add_existing_questions/both-types",
8084
test_add_existing_questions_both_types);
8086
return g_test_run() == 0;
8089
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8090
GOptionContext *context = g_option_context_new("");
8092
g_option_context_set_help_enabled(context, FALSE);
8093
g_option_context_set_ignore_unknown_options(context, TRUE);
8095
gboolean run_tests = FALSE;
8096
GOptionEntry entries[] = {
8097
{ "test", 0, 0, G_OPTION_ARG_NONE,
8098
&run_tests, "Run tests", NULL },
8101
g_option_context_add_main_entries(context, entries, NULL);
8103
GError *error = NULL;
8105
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8106
g_option_context_free(context);
8107
g_error("Failed to parse options: %s", error->message);
8110
g_option_context_free(context);
8111
return run_tests != FALSE;