1
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019 Teddy Hogeborn
6
* Copyright © 2019 Björn Påhlsson
8
* This file is part of Mandos.
10
* Mandos is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* Mandos is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
* Contact the authors at <mandos@recompile.se>.
27
#include <inttypes.h> /* uintmax_t, PRIuMAX, PRIdMAX,
28
intmax_t, uint32_t, SCNx32,
30
#include <stddef.h> /* size_t */
31
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
33
#include <stdbool.h> /* bool, true, false */
34
#include <signal.h> /* struct sigaction, sigset_t,
35
sigemptyset(), sigaddset(),
36
SIGCHLD, pthread_sigmask(),
37
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
SA_NOCLDSTOP, sigfillset(), kill(),
39
SIGTERM, sigdelset(), SIGKILL,
40
NSIG, sigismember(), SA_ONSTACK,
41
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
44
malloc(), free(), strtoumax(),
45
realloc(), setenv(), calloc(),
46
mkdtemp(), mkostemp() */
47
#include <iso646.h> /* not, or, and, xor */
48
#include <error.h> /* error() */
49
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
#include <errno.h> /* errno, error_t, EACCES,
51
ENAMETOOLONG, ENOENT, EEXIST,
52
ECHILD, EPERM, ENOMEM, EAGAIN,
53
EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
77
fork(), _exit(), dup2(),
78
STDOUT_FILENO, setresgid(),
79
setresuid(), execv(), ssize_t,
80
read(), dup3(), getuid(), dup(),
81
STDERR_FILENO, pause(), write(),
82
rmdir(), unlink(), getpid() */
83
#include <sys/mman.h> /* munlock(), mlock() */
84
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
F_GETFD, F_GETFL, FD_CLOEXEC,
86
open(), O_WRONLY, O_RDONLY */
87
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
89
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
90
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
91
IN_CLOEXEC, inotify_add_watch(),
92
IN_CLOSE_WRITE, IN_MOVED_TO,
93
IN_DELETE, struct inotify_event */
94
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
95
#include <stdio.h> /* asprintf(), FILE, fopen(),
96
getline(), sscanf(), feof(),
97
ferror(), fclose(), stderr,
98
rename(), fdopen(), fprintf(),
100
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
GError, g_key_file_load_from_file(),
102
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
103
g_key_file_get_string(), guint64,
104
g_key_file_get_uint64(),
105
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
106
g_assert_true(), g_assert_nonnull(),
107
g_assert_null(), g_assert_false(),
108
g_assert_cmpint(), g_assert_cmpuint(),
109
g_test_skip(), g_assert_cmpstr(),
110
g_test_init(), g_test_add(), g_test_run(),
111
GOptionContext, g_option_context_new(),
112
g_option_context_set_help_enabled(), FALSE,
113
g_option_context_set_ignore_unknown_options(),
114
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
115
g_option_context_add_main_entries(),
116
g_option_context_parse(),
117
g_option_context_free(), g_error() */
118
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
119
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
120
SOCK_DGRAM, SOCK_NONBLOCK,
121
SOCK_CLOEXEC, connect(),
122
struct sockaddr, socklen_t,
123
shutdown(), SHUT_RD, send(),
124
MSG_NOSIGNAL, bind(), recv(),
126
#include <glob.h> /* globfree(), glob_t, glob(),
127
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
128
GLOB_ABORTED, GLOB_NOMATCH,
131
/* End of includes */
133
/* Start of declarations of private types and functions */
135
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
136
typedef uintmax_t mono_microsecs;
138
/* "task_queue" - A queue of tasks to be run */
140
struct task_struct *tasks; /* Tasks in this queue */
141
size_t length; /* Number of tasks */
142
/* Memory allocated for "tasks", in bytes */
144
/* Time when this queue should be run, at the latest */
145
mono_microsecs next_run;
146
} __attribute__((designated_init)) task_queue;
148
/* "func_type" - A function type for task functions
150
I.e. functions for the code which runs when a task is run, all have
152
typedef void (task_func) (const struct task_struct,
154
__attribute__((nonnull));
156
/* "buffer" - A data buffer for a growing array of bytes
158
Used for the "password" variable */
163
} __attribute__((designated_init)) buffer;
165
/* "string_set" - A set type which can contain strings
167
Used by the "cancelled_filenames" variable */
169
char *argz; /* Do not access these except in */
170
size_t argz_len; /* the string_set_* functions */
171
} __attribute__((designated_init)) string_set;
173
/* "task_context" - local variables for tasks
175
This data structure distinguishes between different tasks which are
176
using the same function. This data structure is passed to every
177
task function when each task is run.
179
Note that not every task uses every struct member. */
180
typedef struct task_struct {
181
task_func *const func; /* The function run by this task */
182
char *const question_filename; /* The question file */
183
const pid_t pid; /* Mandos client process ID */
184
const int epoll_fd; /* The epoll set file descriptor */
185
bool *const quit_now; /* Set to true on fatal errors */
186
const int fd; /* General purpose file descriptor */
187
bool *const mandos_client_exited; /* Set true when client exits */
188
buffer *const password; /* As read from client process */
189
bool *const password_is_read; /* "password" is done growing */
190
char *filename; /* General purpose file name */
191
/* A set of strings of all the file names of questions which have
192
been cancelled for any reason; tasks pertaining to these question
193
files should not be run */
194
string_set *const cancelled_filenames;
195
const mono_microsecs notafter; /* "NotAfter" from question file */
196
/* Updated before each queue run; is compared with queue.next_run */
197
const mono_microsecs *const current_time;
198
} __attribute__((designated_init)) task_context;
200
/* Declare all our functions here so we can define them in any order
201
below. Note: test functions are *not* declared here, they are
202
declared in the test section. */
203
__attribute__((warn_unused_result))
204
static bool should_only_run_tests(int *, char **[]);
205
__attribute__((warn_unused_result, cold))
206
static bool run_tests(int, char *[]);
207
static void handle_sigchld(__attribute__((unused)) int sig){}
208
__attribute__((warn_unused_result, malloc))
209
task_queue *create_queue(void);
210
__attribute__((nonnull, warn_unused_result))
211
bool add_to_queue(task_queue *const, const task_context);
212
__attribute__((nonnull))
213
void cleanup_task(const task_context *const);
214
__attribute__((nonnull))
215
void cleanup_queue(task_queue *const *const);
216
__attribute__((pure, nonnull, warn_unused_result))
217
bool queue_has_question(const task_queue *const);
218
__attribute__((nonnull))
219
void cleanup_close(const int *const);
220
__attribute__((nonnull))
221
void cleanup_string(char *const *const);
222
__attribute__((nonnull))
223
void cleanup_buffer(buffer *const);
224
__attribute__((pure, nonnull, warn_unused_result))
225
bool string_set_contains(const string_set, const char *const);
226
__attribute__((nonnull, warn_unused_result))
227
bool string_set_add(string_set *const, const char *const);
228
__attribute__((nonnull))
229
void string_set_clear(string_set *);
230
void string_set_swap(string_set *const, string_set *const);
231
__attribute__((nonnull, warn_unused_result))
232
bool start_mandos_client(task_queue *const, const int, bool *const,
233
bool *const, buffer *const, bool *const,
234
const struct sigaction *const,
235
const sigset_t, const char *const,
236
const uid_t, const gid_t,
237
const char *const *const);
238
__attribute__((nonnull))
239
task_func wait_for_mandos_client_exit;
240
__attribute__((nonnull))
241
task_func read_mandos_client_output;
242
__attribute__((warn_unused_result))
243
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
244
buffer *const, const char *const,
245
string_set *, const mono_microsecs *const,
246
bool *const, bool *const);
247
__attribute__((nonnull))
248
task_func read_inotify_event;
249
__attribute__((nonnull))
250
task_func open_and_parse_question;
251
__attribute__((nonnull))
252
task_func cancel_old_question;
253
__attribute__((nonnull))
254
task_func connect_question_socket;
255
__attribute__((nonnull))
256
task_func send_password_to_socket;
257
__attribute__((warn_unused_result))
258
bool add_existing_questions(task_queue *const, const int,
259
buffer *const, string_set *,
260
const mono_microsecs *const,
261
bool *const, bool *const,
263
__attribute__((nonnull, warn_unused_result))
264
bool wait_for_event(const int, const mono_microsecs,
265
const mono_microsecs);
266
bool run_queue(task_queue **const, string_set *const, bool *const);
267
bool clear_all_fds_from_epoll_set(const int);
268
mono_microsecs get_current_time(void);
269
__attribute__((nonnull, warn_unused_result))
270
bool setup_signal_handler(struct sigaction *const);
271
__attribute__((nonnull))
272
bool restore_signal_handler(const struct sigaction *const);
273
__attribute__((nonnull, warn_unused_result))
274
bool block_sigchld(sigset_t *const);
275
__attribute__((nonnull))
276
bool restore_sigmask(const sigset_t *const);
277
__attribute__((nonnull))
278
bool parse_arguments(int, char *[], const bool, char **, char **,
279
uid_t *const , gid_t *const, char **, size_t *);
281
/* End of declarations of private types and functions */
283
/* Start of "main" section; this section LACKS TESTS!
285
Code here should be as simple as possible. */
287
/* These are required to be global by Argp */
288
const char *argp_program_version = "password-agent " VERSION;
289
const char *argp_program_bug_address = "<mandos@recompile.se>";
291
int main(int argc, char *argv[]){
293
/* If the --test option is passed, skip all normal operations and
294
instead only run the run_tests() function, which also does all
295
its own option parsing, so we don't have to do anything here. */
296
if(should_only_run_tests(&argc, &argv)){
297
if(run_tests(argc, argv)){
298
return EXIT_SUCCESS; /* All tests successful */
300
return EXIT_FAILURE; /* Some test(s) failed */
303
__attribute__((cleanup(cleanup_string)))
304
char *agent_directory = NULL;
306
__attribute__((cleanup(cleanup_string)))
307
char *helper_directory = NULL;
312
__attribute__((cleanup(cleanup_string)))
313
char *mandos_argz = NULL;
314
size_t mandos_argz_length = 0;
316
if(not parse_arguments(argc, argv, true, &agent_directory,
317
&helper_directory, &user, &group,
318
&mandos_argz, &mandos_argz_length)){
319
/* This should never happen, since "true" is passed as the third
320
argument to parse_arguments() above, which should make
321
argp_parse() call exit() if any parsing error occurs. */
322
error(EX_USAGE, errno, "Failed to parse arguments");
325
const char default_agent_directory[] = "/run/systemd/ask-password";
326
const char default_helper_directory[]
327
= "/lib/mandos/plugin-helpers";
328
const char *const default_argv[]
329
= {"/lib/mandos/plugins.d/mandos-client", NULL };
331
/* Set variables to default values if unset */
332
if(agent_directory == NULL){
333
agent_directory = strdup(default_agent_directory);
334
if(agent_directory == NULL){
335
error(EX_OSERR, errno, "Failed strdup()");
338
if(helper_directory == NULL){
339
helper_directory = strdup(default_helper_directory);
340
if(helper_directory == NULL){
341
error(EX_OSERR, errno, "Failed strdup()");
345
user = 65534; /* nobody */
348
group = 65534; /* nogroup */
350
/* If parse_opt did not create an argz vector, create one with
352
if(mandos_argz == NULL){
354
#pragma GCC diagnostic push
355
/* argz_create() takes a non-const argv for some unknown reason -
356
argz_create() isn't modifying the strings, just copying them.
357
Therefore, this cast to non-const should be safe. */
358
#pragma GCC diagnostic ignored "-Wcast-qual"
360
errno = argz_create((char *const *)default_argv, &mandos_argz,
361
&mandos_argz_length);
363
#pragma GCC diagnostic pop
366
error(EX_OSERR, errno, "Failed argz_create()");
369
/* Use argz vector to create a normal argv, usable by execv() */
371
char **mandos_argv = malloc((argz_count(mandos_argz,
373
+ 1) * sizeof(char *));
374
if(mandos_argv == NULL){
375
error_t saved_errno = errno;
377
error(EX_OSERR, saved_errno, "Failed malloc()");
379
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
381
sigset_t orig_sigmask;
382
if(not block_sigchld(&orig_sigmask)){
386
struct sigaction old_sigchld_action;
387
if(not setup_signal_handler(&old_sigchld_action)){
391
mono_microsecs current_time = 0;
393
bool mandos_client_exited = false;
394
bool quit_now = false;
395
__attribute__((cleanup(cleanup_close)))
396
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
398
error(EX_OSERR, errno, "Failed to create epoll set fd");
400
__attribute__((cleanup(cleanup_queue)))
401
task_queue *queue = create_queue();
403
error(EX_OSERR, errno, "Failed to create task queue");
406
__attribute__((cleanup(cleanup_buffer)))
407
buffer password = {};
408
bool password_is_read = false;
410
__attribute__((cleanup(string_set_clear)))
411
string_set cancelled_filenames = {};
413
/* Add tasks to queue */
414
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
415
&quit_now, &password, &password_is_read,
416
&old_sigchld_action, orig_sigmask,
417
helper_directory, user, group,
418
(const char *const *)mandos_argv)){
419
return EX_OSERR; /* Error has already been printed */
421
/* These variables were only for start_mandos_client() and are not
426
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
427
agent_directory, &cancelled_filenames,
428
¤t_time, &mandos_client_exited,
430
switch(errno){ /* Error has already been printed */
439
if(not add_existing_questions(queue, epoll_fd, &password,
440
&cancelled_filenames, ¤t_time,
441
&mandos_client_exited,
442
&password_is_read, agent_directory)){
443
return EXIT_FAILURE; /* Error has already been printed */
448
current_time = get_current_time();
449
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
450
const error_t saved_errno = errno;
451
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
455
current_time = get_current_time();
456
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
457
const error_t saved_errno = errno;
458
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
461
/* When no tasks about questions are left in the queue, break out
462
of the loop (and implicitly exit the program) */
463
} while(queue_has_question(queue));
465
restore_signal_handler(&old_sigchld_action);
466
restore_sigmask(&orig_sigmask);
471
__attribute__((warn_unused_result))
472
mono_microsecs get_current_time(void){
473
struct timespec currtime;
474
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
475
error(0, errno, "Failed to get current time");
478
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
479
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
482
/* End of "main" section */
484
/* Start of regular code section; ALL this code has tests */
486
__attribute__((nonnull))
487
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
488
char **agent_directory, char **helper_directory,
489
uid_t *const user, gid_t *const group,
490
char **mandos_argz, size_t *mandos_argz_length){
492
const struct argp_option options[] = {
493
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
494
.doc="Systemd password agent directory" },
495
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
496
.doc="Mandos Client password helper directory" },
497
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
498
.flags=OPTION_HIDDEN | OPTION_ALIAS },
499
{ .name="user", .key='u', .arg="USERID",
500
.doc="User ID the Mandos Client will use as its unprivileged"
502
{ .name="userid", .key=130, /* From plugin--runner */
503
.flags=OPTION_HIDDEN | OPTION_ALIAS },
504
{ .name="group", .key='g', .arg="GROUPID",
505
.doc="Group ID the Mandos Client will use as its unprivileged"
507
{ .name="groupid", .key=131, /* From plugin--runner */
508
.flags=OPTION_HIDDEN | OPTION_ALIAS },
509
{ .name="test", .key=255, /* See should_only_run_tests() */
510
.doc="Skip normal operation, and only run self-tests. See"
511
" --test --help.", .group=10, },
515
__attribute__((nonnull(3)))
516
error_t parse_opt(int key, char *arg, struct argp_state *state){
519
case 'd': /* --agent-directory */
520
*agent_directory = strdup(arg);
522
case 128: /* --helper-directory */
523
case 129: /* --plugin-helper-dir */
524
*helper_directory = strdup(arg);
526
case 'u': /* --user */
527
case 130: /* --userid */
530
uintmax_t tmp_id = 0;
532
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
533
if(errno != 0 or tmp == arg or *tmp != '\0'
534
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
535
return ARGP_ERR_UNKNOWN;
537
*user = (uid_t)tmp_id;
541
case 'g': /* --group */
542
case 131: /* --groupid */
545
uintmax_t tmp_id = 0;
547
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
548
if(errno != 0 or tmp == arg or *tmp != '\0'
549
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
550
return ARGP_ERR_UNKNOWN;
552
*group = (gid_t)tmp_id;
557
/* Copy arguments into argz vector */
558
return argz_create(state->argv + state->next, mandos_argz,
561
return ARGP_ERR_UNKNOWN;
566
const struct argp argp = {
569
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
570
.doc = "Mandos password agent -- runs Mandos client as a"
571
" systemd password agent",
574
errno = argp_parse(&argp, argc, argv,
575
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
580
__attribute__((nonnull, warn_unused_result))
581
bool block_sigchld(sigset_t *const orig_sigmask){
582
sigset_t sigchld_sigmask;
583
if(sigemptyset(&sigchld_sigmask) < 0){
584
error(0, errno, "Failed to empty signal set");
587
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
588
error(0, errno, "Failed to add SIGCHLD to signal set");
591
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
592
error(0, errno, "Failed to block SIGCHLD signal");
598
__attribute__((nonnull, warn_unused_result, const))
599
bool restore_sigmask(const sigset_t *const orig_sigmask){
600
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
601
error(0, errno, "Failed to restore blocked signals");
607
__attribute__((nonnull, warn_unused_result))
608
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
609
struct sigaction sigchld_action = {
610
.sa_handler=handle_sigchld,
611
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
613
/* Set all signals in "sa_mask" struct member; this makes all
614
signals automatically blocked during signal handler */
615
if(sigfillset(&sigchld_action.sa_mask) != 0){
616
error(0, errno, "Failed to do sigfillset()");
619
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
620
error(0, errno, "Failed to set SIGCHLD signal handler");
626
__attribute__((nonnull, warn_unused_result))
627
bool restore_signal_handler(const struct sigaction *const
629
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
630
error(0, errno, "Failed to restore signal handler");
636
__attribute__((warn_unused_result, malloc))
637
task_queue *create_queue(void){
638
task_queue *queue = malloc(sizeof(task_queue));
642
queue->allocated = 0;
648
__attribute__((nonnull, warn_unused_result))
649
bool add_to_queue(task_queue *const queue, const task_context task){
650
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
if(needed_size > (queue->allocated)){
652
task_context *const new_tasks = realloc(queue->tasks,
654
if(new_tasks == NULL){
655
error(0, errno, "Failed to allocate %" PRIuMAX
656
" bytes for queue->tasks", (uintmax_t)needed_size);
659
queue->tasks = new_tasks;
660
queue->allocated = needed_size;
662
/* Using memcpy here is necessary because doing */
663
/* queue->tasks[queue->length++] = task; */
664
/* would violate const-ness of task members */
665
memcpy(&(queue->tasks[queue->length++]), &task,
666
sizeof(task_context));
670
__attribute__((nonnull))
671
void cleanup_task(const task_context *const task){
672
const error_t saved_errno = errno;
673
/* free and close all task data */
674
free(task->question_filename);
675
if(task->filename != task->question_filename){
676
free(task->filename);
679
kill(task->pid, SIGTERM);
687
__attribute__((nonnull))
688
void free_queue(task_queue *const queue){
693
__attribute__((nonnull))
694
void cleanup_queue(task_queue *const *const queue){
698
for(size_t i = 0; i < (*queue)->length; i++){
699
const task_context *const task = ((*queue)->tasks)+i;
705
__attribute__((pure, nonnull, warn_unused_result))
706
bool queue_has_question(const task_queue *const queue){
707
for(size_t i=0; i < queue->length; i++){
708
if(queue->tasks[i].question_filename != NULL){
715
__attribute__((nonnull))
716
void cleanup_close(const int *const fd){
717
const error_t saved_errno = errno;
722
__attribute__((nonnull))
723
void cleanup_string(char *const *const ptr){
727
__attribute__((nonnull))
728
void cleanup_buffer(buffer *buf){
729
if(buf->allocated > 0){
730
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
731
explicit_bzero(buf->data, buf->allocated);
733
memset(buf->data, '\0', buf->allocated);
736
if(buf->data != NULL){
737
if(munlock(buf->data, buf->allocated) != 0){
738
error(0, errno, "Failed to unlock memory of old buffer");
747
__attribute__((pure, nonnull, warn_unused_result))
748
bool string_set_contains(const string_set set, const char *const str){
749
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
750
s = argz_next(set.argz, set.argz_len, s)){
751
if(strcmp(s, str) == 0){
758
__attribute__((nonnull, warn_unused_result))
759
bool string_set_add(string_set *const set, const char *const str){
760
if(string_set_contains(*set, str)){
763
error_t error = argz_add(&set->argz, &set->argz_len, str);
771
__attribute__((nonnull))
772
void string_set_clear(string_set *set){
778
__attribute__((nonnull))
779
void string_set_swap(string_set *const set1, string_set *const set2){
780
/* Swap contents of two string sets */
782
char *const tmp_argz = set1->argz;
783
set1->argz = set2->argz;
784
set2->argz = tmp_argz;
787
const size_t tmp_argz_len = set1->argz_len;
788
set1->argz_len = set2->argz_len;
789
set2->argz_len = tmp_argz_len;
793
__attribute__((nonnull, warn_unused_result))
794
bool start_mandos_client(task_queue *const queue,
796
bool *const mandos_client_exited,
797
bool *const quit_now, buffer *const password,
798
bool *const password_is_read,
799
const struct sigaction *const
800
old_sigchld_action, const sigset_t sigmask,
801
const char *const helper_directory,
802
const uid_t user, const gid_t group,
803
const char *const *const argv){
805
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
806
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
810
const pid_t pid = fork();
812
if(not restore_signal_handler(old_sigchld_action)){
815
if(not restore_sigmask(&sigmask)){
818
if(close(pipefds[0]) != 0){
819
error(0, errno, "Failed to close() parent pipe fd");
822
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
823
error(0, errno, "Failed to dup2() pipe fd to stdout");
826
if(close(pipefds[1]) != 0){
827
error(0, errno, "Failed to close() old child pipe fd");
830
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
831
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
832
" \"%s\", 1)", helper_directory);
835
if(group != 0 and setresgid(group, 0, 0) == -1){
836
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
837
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
840
if(user != 0 and setresuid(user, 0, 0) == -1){
841
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
842
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
846
#pragma GCC diagnostic push
847
/* For historical reasons, the "argv" argument to execv() is not
848
const, but it is safe to override this. */
849
#pragma GCC diagnostic ignored "-Wcast-qual"
851
execv(argv[0], (char **)argv);
853
#pragma GCC diagnostic pop
855
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
860
if(not add_to_queue(queue, (task_context){
861
.func=wait_for_mandos_client_exit,
863
.mandos_client_exited=mandos_client_exited,
866
error(0, errno, "Failed to add wait_for_mandos_client to queue");
871
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
872
&(struct epoll_event)
873
{ .events=EPOLLIN | EPOLLRDHUP });
874
if(ret != 0 and errno != EEXIST){
875
error(0, errno, "Failed to add file descriptor to epoll set");
880
return add_to_queue(queue, (task_context){
881
.func=read_mandos_client_output,
886
.password_is_read=password_is_read,
890
__attribute__((nonnull))
891
void wait_for_mandos_client_exit(const task_context task,
892
task_queue *const queue){
893
const pid_t pid = task.pid;
894
bool *const mandos_client_exited = task.mandos_client_exited;
895
bool *const quit_now = task.quit_now;
898
switch(waitpid(pid, &status, WNOHANG)){
899
case 0: /* Not exited yet */
900
if(not add_to_queue(queue, task)){
901
error(0, errno, "Failed to add myself to queue");
906
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
912
default: /* Has exited */
913
*mandos_client_exited = true;
914
if((not WIFEXITED(status))
915
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
916
error(0, 0, "Mandos client failed or was killed");
922
__attribute__((nonnull))
923
void read_mandos_client_output(const task_context task,
924
task_queue *const queue){
925
buffer *const password = task.password;
926
bool *const quit_now = task.quit_now;
927
bool *const password_is_read = task.password_is_read;
928
const int fd = task.fd;
929
const int epoll_fd = task.epoll_fd;
931
const size_t new_potential_size = (password->length + PIPE_BUF);
932
if(password->allocated < new_potential_size){
933
char *const new_buffer = calloc(new_potential_size, 1);
934
if(new_buffer == NULL){
935
error(0, errno, "Failed to allocate %" PRIuMAX
936
" bytes for password", (uintmax_t)new_potential_size);
941
if(mlock(new_buffer, new_potential_size) != 0){
942
/* Warn but do not treat as fatal error */
943
if(errno != EPERM and errno != ENOMEM){
944
error(0, errno, "Failed to lock memory for password");
947
if(password->length > 0){
948
memcpy(new_buffer, password->data, password->length);
949
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
950
explicit_bzero(password->data, password->allocated);
952
memset(password->data, '\0', password->allocated);
955
if(password->data != NULL){
956
if(munlock(password->data, password->allocated) != 0){
957
error(0, errno, "Failed to unlock memory of old buffer");
959
free(password->data);
961
password->data = new_buffer;
962
password->allocated = new_potential_size;
965
const ssize_t read_length = read(fd, password->data
966
+ password->length, PIPE_BUF);
968
if(read_length == 0){ /* EOF */
969
*password_is_read = true;
973
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
974
error(0, errno, "Failed to read password from Mandos client");
979
if(read_length > 0){ /* Data has been read */
980
password->length += (size_t)read_length;
983
/* Either data was read, or EAGAIN was indicated, meaning no data
986
/* Re-add the fd to the epoll set */
987
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
988
&(struct epoll_event)
989
{ .events=EPOLLIN | EPOLLRDHUP });
990
if(ret != 0 and errno != EEXIST){
991
error(0, errno, "Failed to re-add file descriptor to epoll set");
997
/* Re-add myself to the queue */
998
if(not add_to_queue(queue, task)){
999
error(0, errno, "Failed to add myself to queue");
1005
__attribute__((nonnull, warn_unused_result))
1006
bool add_inotify_dir_watch(task_queue *const queue,
1007
const int epoll_fd, bool *const quit_now,
1008
buffer *const password,
1009
const char *const dir,
1010
string_set *cancelled_filenames,
1011
const mono_microsecs *const current_time,
1012
bool *const mandos_client_exited,
1013
bool *const password_is_read){
1014
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1016
error(0, errno, "Failed to create inotify instance");
1020
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1021
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK)
1023
error(0, errno, "Failed to create inotify watch on %s", dir);
1027
/* Add the inotify fd to the epoll set */
1028
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1029
&(struct epoll_event)
1030
{ .events=EPOLLIN | EPOLLRDHUP });
1031
if(ret != 0 and errno != EEXIST){
1032
error(0, errno, "Failed to add file descriptor to epoll set");
1037
const task_context read_inotify_event_task = {
1038
.func=read_inotify_event,
1043
.filename=strdup(dir),
1044
.cancelled_filenames=cancelled_filenames,
1045
.current_time=current_time,
1046
.mandos_client_exited=mandos_client_exited,
1047
.password_is_read=password_is_read,
1049
if(read_inotify_event_task.filename == NULL){
1050
error(0, errno, "Failed to strdup(\"%s\")", dir);
1055
return add_to_queue(queue, read_inotify_event_task);
1058
__attribute__((nonnull))
1059
void read_inotify_event(const task_context task,
1060
task_queue *const queue){
1061
const int fd = task.fd;
1062
const int epoll_fd = task.epoll_fd;
1063
char *const filename = task.filename;
1064
bool *quit_now = task.quit_now;
1065
buffer *const password = task.password;
1066
string_set *const cancelled_filenames = task.cancelled_filenames;
1067
const mono_microsecs *const current_time = task.current_time;
1068
bool *const mandos_client_exited = task.mandos_client_exited;
1069
bool *const password_is_read = task.password_is_read;
1071
/* "sufficient to read at least one event." - inotify(7) */
1072
const size_t ievent_size = (sizeof(struct inotify_event)
1075
struct inotify_event event;
1076
char name_buffer[NAME_MAX + 1];
1078
struct inotify_event *const ievent = &ievent_buffer.event;
1080
const ssize_t read_length = read(fd, ievent, ievent_size);
1081
if(read_length == 0){ /* EOF */
1082
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1084
cleanup_task(&task);
1087
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1088
error(0, errno, "Failed to read from inotify fd for directory %s",
1091
cleanup_task(&task);
1094
if(read_length > 0 /* Data has been read */
1095
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1096
char *question_filename = NULL;
1097
const ssize_t question_filename_length
1098
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1099
if(question_filename_length < 0){
1100
error(0, errno, "Failed to create file name from directory name"
1101
" %s and file name %s", filename, ievent->name);
1103
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1104
if(not add_to_queue(queue, (task_context){
1105
.func=open_and_parse_question,
1107
.question_filename=question_filename,
1108
.filename=question_filename,
1110
.cancelled_filenames=cancelled_filenames,
1111
.current_time=current_time,
1112
.mandos_client_exited=mandos_client_exited,
1113
.password_is_read=password_is_read,
1115
error(0, errno, "Failed to add open_and_parse_question task"
1116
" for file name %s to queue", filename);
1118
/* Force the added task (open_and_parse_question) to run
1120
queue->next_run = 1;
1122
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1123
if(not string_set_add(cancelled_filenames,
1124
question_filename)){
1125
error(0, errno, "Could not add question %s to"
1126
" cancelled_questions", question_filename);
1128
free(question_filename);
1129
cleanup_task(&task);
1132
free(question_filename);
1137
/* Either data was read, or EAGAIN was indicated, meaning no data
1140
/* Re-add myself to the queue */
1141
if(not add_to_queue(queue, task)){
1142
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1143
" queue", filename);
1145
cleanup_task(&task);
1149
/* Re-add the fd to the epoll set */
1150
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1151
&(struct epoll_event)
1152
{ .events=EPOLLIN | EPOLLRDHUP });
1153
if(ret != 0 and errno != EEXIST){
1154
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1155
" directory %s to epoll set", fd, filename);
1156
/* Force the added task (read_inotify_event) to run again, at most
1157
one second from now */
1158
if((queue->next_run == 0)
1159
or (queue->next_run > (*current_time + 1000000))){
1160
queue->next_run = *current_time + 1000000;
1165
__attribute__((nonnull))
1166
void open_and_parse_question(const task_context task,
1167
task_queue *const queue){
1168
__attribute__((cleanup(cleanup_string)))
1169
char *question_filename = task.question_filename;
1170
const int epoll_fd = task.epoll_fd;
1171
buffer *const password = task.password;
1172
string_set *const cancelled_filenames = task.cancelled_filenames;
1173
const mono_microsecs *const current_time = task.current_time;
1174
bool *const mandos_client_exited = task.mandos_client_exited;
1175
bool *const password_is_read = task.password_is_read;
1177
/* We use the GLib "Key-value file parser" functions to parse the
1178
question file. See <https://www.freedesktop.org/wiki/Software
1179
/systemd/PasswordAgents/> for specification of contents */
1180
__attribute__((nonnull))
1181
void cleanup_g_key_file(GKeyFile **key_file){
1182
if(*key_file != NULL){
1183
g_key_file_free(*key_file);
1187
__attribute__((cleanup(cleanup_g_key_file)))
1188
GKeyFile *key_file = g_key_file_new();
1189
if(key_file == NULL){
1190
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1194
GError *glib_error = NULL;
1195
if(g_key_file_load_from_file(key_file, question_filename,
1196
G_KEY_FILE_NONE, &glib_error) != TRUE){
1197
/* If a file was removed, we should ignore it, so */
1198
/* only show error message if file actually existed */
1199
if(glib_error->code != G_FILE_ERROR_NOENT){
1200
error(0, 0, "Failed to load question data from file \"%s\": %s",
1201
question_filename, glib_error->message);
1206
__attribute__((cleanup(cleanup_string)))
1207
char *socket_name = g_key_file_get_string(key_file, "Ask",
1210
if(socket_name == NULL){
1211
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1212
question_filename, glib_error->message);
1216
if(strlen(socket_name) == 0){
1217
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1222
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1224
if(glib_error != NULL){
1225
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1226
question_filename, glib_error->message);
1230
if((pid != (guint64)((pid_t)pid))
1231
or (kill((pid_t)pid, 0) != 0)){
1232
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1233
" does not exist", (uintmax_t)pid, question_filename);
1237
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1238
"NotAfter", &glib_error);
1239
if(glib_error != NULL){
1240
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1241
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1242
" %s", question_filename, glib_error->message);
1247
if(queue->next_run == 0 or (queue->next_run > notafter)){
1248
queue->next_run = notafter;
1250
if(*current_time >= notafter){
1255
const task_context connect_question_socket_task = {
1256
.func=connect_question_socket,
1257
.question_filename=strdup(question_filename),
1260
.filename=strdup(socket_name),
1261
.cancelled_filenames=task.cancelled_filenames,
1262
.mandos_client_exited=mandos_client_exited,
1263
.password_is_read=password_is_read,
1264
.current_time=current_time,
1266
if(connect_question_socket_task.question_filename == NULL
1267
or connect_question_socket_task.filename == NULL
1268
or not add_to_queue(queue, connect_question_socket_task)){
1269
error(0, errno, "Failed to add connect_question_socket for socket"
1270
" %s (from \"%s\") to queue", socket_name,
1272
cleanup_task(&connect_question_socket_task);
1275
/* Force the added task (connect_question_socket) to run
1277
queue->next_run = 1;
1280
char *const dup_filename = strdup(question_filename);
1281
const task_context cancel_old_question_task = {
1282
.func=cancel_old_question,
1283
.question_filename=dup_filename,
1285
.filename=dup_filename,
1286
.cancelled_filenames=cancelled_filenames,
1287
.current_time=current_time,
1289
if(cancel_old_question_task.question_filename == NULL
1290
or not add_to_queue(queue, cancel_old_question_task)){
1291
error(0, errno, "Failed to add cancel_old_question for file "
1292
"\"%s\" to queue", question_filename);
1293
cleanup_task(&cancel_old_question_task);
1299
__attribute__((nonnull))
1300
void cancel_old_question(const task_context task,
1301
task_queue *const queue){
1302
char *const question_filename = task.question_filename;
1303
string_set *const cancelled_filenames = task.cancelled_filenames;
1304
const mono_microsecs notafter = task.notafter;
1305
const mono_microsecs *const current_time = task.current_time;
1307
if(*current_time >= notafter){
1308
if(not string_set_add(cancelled_filenames, question_filename)){
1309
error(0, errno, "Failed to cancel question for file %s",
1312
cleanup_task(&task);
1316
if(not add_to_queue(queue, task)){
1317
error(0, errno, "Failed to add cancel_old_question for file "
1318
"%s to queue", question_filename);
1319
cleanup_task(&task);
1323
if((queue->next_run == 0) or (queue->next_run > notafter)){
1324
queue->next_run = notafter;
1328
__attribute__((nonnull))
1329
void connect_question_socket(const task_context task,
1330
task_queue *const queue){
1331
char *const question_filename = task.question_filename;
1332
char *const filename = task.filename;
1333
const int epoll_fd = task.epoll_fd;
1334
buffer *const password = task.password;
1335
string_set *const cancelled_filenames = task.cancelled_filenames;
1336
bool *const mandos_client_exited = task.mandos_client_exited;
1337
bool *const password_is_read = task.password_is_read;
1338
const mono_microsecs *const current_time = task.current_time;
1340
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1342
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1343
error(0, 0, "Socket filename is larger than"
1344
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1345
(uintmax_t)sizeof(sock_name.sun_path), filename);
1346
if(not string_set_add(cancelled_filenames, question_filename)){
1347
error(0, errno, "Failed to cancel question for file %s",
1350
cleanup_task(&task);
1354
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1355
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1358
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1359
if(not add_to_queue(queue, task)){
1360
error(0, errno, "Failed to add connect_question_socket for file"
1361
" \"%s\" and socket \"%s\" to queue", question_filename,
1363
cleanup_task(&task);
1365
/* Force the added task (connect_question_socket) to run
1367
queue->next_run = 1;
1372
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1373
if(connect(fd, (struct sockaddr *)&sock_name,
1374
(socklen_t)SUN_LEN(&sock_name)) != 0){
1375
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1376
if(not add_to_queue(queue, task)){
1377
error(0, errno, "Failed to add connect_question_socket for file"
1378
" \"%s\" and socket \"%s\" to queue", question_filename,
1380
cleanup_task(&task);
1382
/* Force the added task (connect_question_socket) to run again,
1383
at most one second from now */
1384
if((queue->next_run == 0)
1385
or (queue->next_run > (*current_time + 1000000))){
1386
queue->next_run = *current_time + 1000000;
1392
/* Not necessary, but we can try, and merely warn on failure */
1393
if(shutdown(fd, SHUT_RD) != 0){
1394
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1398
/* Add the fd to the epoll set */
1399
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1400
&(struct epoll_event){ .events=EPOLLOUT })
1402
error(0, errno, "Failed to add inotify file descriptor %d for"
1403
" socket %s to epoll set", fd, filename);
1404
if(not add_to_queue(queue, task)){
1405
error(0, errno, "Failed to add connect_question_socket for file"
1406
" \"%s\" and socket \"%s\" to queue", question_filename,
1408
cleanup_task(&task);
1410
/* Force the added task (connect_question_socket) to run again,
1411
at most one second from now */
1412
if((queue->next_run == 0)
1413
or (queue->next_run > (*current_time + 1000000))){
1414
queue->next_run = *current_time + 1000000;
1420
/* add task send_password_to_socket to queue */
1421
const task_context send_password_to_socket_task = {
1422
.func=send_password_to_socket,
1423
.question_filename=question_filename,
1428
.cancelled_filenames=cancelled_filenames,
1429
.mandos_client_exited=mandos_client_exited,
1430
.password_is_read=password_is_read,
1431
.current_time=current_time,
1434
if(not add_to_queue(queue, send_password_to_socket_task)){
1435
error(0, errno, "Failed to add send_password_to_socket for"
1436
" file \"%s\" and socket \"%s\" to queue",
1437
question_filename, filename);
1438
cleanup_task(&send_password_to_socket_task);
1442
__attribute__((nonnull))
1443
void send_password_to_socket(const task_context task,
1444
task_queue *const queue){
1445
char *const question_filename=task.question_filename;
1446
char *const filename=task.filename;
1447
const int epoll_fd=task.epoll_fd;
1448
const int fd=task.fd;
1449
buffer *const password=task.password;
1450
string_set *const cancelled_filenames=task.cancelled_filenames;
1451
bool *const mandos_client_exited = task.mandos_client_exited;
1452
bool *const password_is_read = task.password_is_read;
1453
const mono_microsecs *const current_time = task.current_time;
1455
if(*mandos_client_exited and *password_is_read){
1457
const size_t send_buffer_length = password->length + 2;
1458
char *send_buffer = malloc(send_buffer_length);
1459
if(send_buffer == NULL){
1460
error(0, errno, "Failed to allocate send_buffer");
1462
if(mlock(send_buffer, send_buffer_length) != 0){
1463
/* Warn but do not treat as fatal error */
1464
if(errno != EPERM and errno != ENOMEM){
1465
error(0, errno, "Failed to lock memory for password"
1469
/* “[…] send a single datagram to the socket consisting of the
1470
password string either prefixed with "+" or with "-"
1471
depending on whether the password entry was successful or
1472
not. You may but don't have to include a final NUL byte in
1475
— <https://www.freedesktop.org/wiki/Software/systemd/
1476
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1478
send_buffer[0] = '+'; /* Prefix with "+" */
1479
/* Always add an extra NUL */
1480
send_buffer[password->length + 1] = '\0';
1481
if(password->length > 0){
1482
memcpy(send_buffer + 1, password->data, password->length);
1485
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1487
const error_t saved_errno = errno;
1488
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1489
explicit_bzero(send_buffer, send_buffer_length);
1491
memset(send_buffer, '\0', send_buffer_length);
1493
if(munlock(send_buffer, send_buffer_length) != 0){
1494
error(0, errno, "Failed to unlock memory of send buffer");
1497
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1498
switch(saved_errno){
1511
error(0, 0, "Password of size %" PRIuMAX " is too big",
1512
(uintmax_t)password->length);
1516
__attribute__((fallthrough));
1519
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1520
error(0, 0, "Password only partially sent to socket");
1525
__attribute__((fallthrough));
1528
error(0, saved_errno, "Failed to send() to socket %s",
1530
if(not string_set_add(cancelled_filenames,
1531
question_filename)){
1532
error(0, errno, "Failed to cancel question for file %s",
1535
cleanup_task(&task);
1540
cleanup_task(&task);
1546
/* We failed or are not ready yet; retry later */
1548
if(not add_to_queue(queue, task)){
1549
error(0, errno, "Failed to add send_password_to_socket for"
1550
" file %s and socket %s to queue", question_filename,
1552
cleanup_task(&task);
1555
/* Add the fd to the epoll set */
1556
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1557
&(struct epoll_event){ .events=EPOLLOUT })
1559
error(0, errno, "Failed to add socket file descriptor %d for"
1560
" socket %s to epoll set", fd, filename);
1561
/* Force the added task (send_password_to_socket) to run again, at
1562
most one second from now */
1563
if((queue->next_run == 0)
1564
or (queue->next_run > (*current_time + 1000000))){
1565
queue->next_run = *current_time + 1000000;
1570
__attribute__((warn_unused_result))
1571
bool add_existing_questions(task_queue *const queue,
1573
buffer *const password,
1574
string_set *cancelled_filenames,
1575
const mono_microsecs *const current_time,
1576
bool *const mandos_client_exited,
1577
bool *const password_is_read,
1578
const char *const dirname){
1579
__attribute__((cleanup(cleanup_string)))
1580
char *dir_pattern = NULL;
1581
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1582
if(ret < 0 or dir_pattern == NULL){
1583
error(0, errno, "Could not create glob pattern for directory %s",
1587
__attribute__((cleanup(globfree)))
1588
glob_t question_filenames = {};
1589
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1590
NULL, &question_filenames)){
1593
error(0, errno, "Failed to open directory %s", dirname);
1596
error(0, errno, "There are no question files in %s", dirname);
1599
error(0, errno, "Could not allocate memory for question file"
1600
" names in %s", dirname);
1604
__attribute__((fallthrough));
1607
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1608
char *const question_filename = strdup(question_filenames
1610
const task_context task = {
1611
.func=open_and_parse_question,
1613
.question_filename=question_filename,
1614
.filename=question_filename,
1616
.cancelled_filenames=cancelled_filenames,
1617
.current_time=current_time,
1618
.mandos_client_exited=mandos_client_exited,
1619
.password_is_read=password_is_read,
1622
if(question_filename == NULL
1623
or not add_to_queue(queue, task)){
1624
error(0, errno, "Failed to add open_and_parse_question for"
1625
" file %s to queue",
1626
question_filenames.gl_pathv[i]);
1627
free(question_filename);
1629
queue->next_run = 1;
1636
__attribute__((nonnull, warn_unused_result))
1637
bool wait_for_event(const int epoll_fd,
1638
const mono_microsecs queue_next_run,
1639
const mono_microsecs current_time){
1640
__attribute__((const))
1641
int milliseconds_to_wait(const mono_microsecs currtime,
1642
const mono_microsecs nextrun){
1643
if(currtime >= nextrun){
1646
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1647
if(wait_time_ms > (uintmax_t)INT_MAX){
1650
return (int)wait_time_ms;
1653
const int wait_time_ms = milliseconds_to_wait(current_time,
1656
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1657
sigset_t temporary_unblocked_sigmask;
1658
/* Get current signal mask */
1659
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1662
/* Remove SIGCHLD from the signal mask */
1663
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1666
struct epoll_event events[8]; /* Ignored */
1667
int ret = epoll_pwait(epoll_fd, events,
1668
sizeof(events) / sizeof(struct epoll_event),
1669
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1670
&temporary_unblocked_sigmask);
1671
if(ret < 0 and errno != EINTR){
1672
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1674
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1677
return clear_all_fds_from_epoll_set(epoll_fd);
1680
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1681
/* Create a new empty epoll set */
1682
__attribute__((cleanup(cleanup_close)))
1683
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1684
if(new_epoll_fd < 0){
1687
/* dup3() the new epoll set fd over the old one, replacing it */
1688
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1694
__attribute__((nonnull, warn_unused_result))
1695
bool run_queue(task_queue **const queue,
1696
string_set *const cancelled_filenames,
1697
bool *const quit_now){
1699
task_queue *new_queue = create_queue();
1700
if(new_queue == NULL){
1704
__attribute__((cleanup(string_set_clear)))
1705
string_set old_cancelled_filenames = {};
1706
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1708
/* Declare i outside the for loop, since we might need i after the
1709
loop in case we aborted in the middle */
1711
for(i=0; i < (*queue)->length and not *quit_now; i++){
1712
task_context *const task = &((*queue)->tasks[i]);
1713
const char *const question_filename = task->question_filename;
1714
/* Skip any task referencing a cancelled question filename */
1715
if(question_filename != NULL
1716
and string_set_contains(old_cancelled_filenames,
1717
question_filename)){
1721
task->func(*task, new_queue);
1725
/* we might be in the middle of the queue, so clean up any
1726
remaining tasks in the current queue */
1727
for(; i < (*queue)->length; i++){
1728
cleanup_task(&((*queue)->tasks[i]));
1742
/* End of regular code section */
1744
/* Start of tests section; here are the tests for the above code */
1746
/* This "fixture" data structure is used by the test setup and
1747
teardown functions */
1749
struct sigaction orig_sigaction;
1750
sigset_t orig_sigmask;
1753
static void test_setup(test_fixture *fixture,
1754
__attribute__((unused))
1755
gconstpointer user_data){
1756
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1757
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1760
static void test_teardown(test_fixture *fixture,
1761
__attribute__((unused))
1762
gconstpointer user_data){
1763
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1764
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1767
/* Utility function used by tests to search queue for matching task */
1768
__attribute__((pure, nonnull, warn_unused_result))
1769
static task_context *find_matching_task(const task_queue *const queue,
1770
const task_context task){
1771
/* The argument "task" structure is a pattern to match; 0 in any
1772
member means any value matches, otherwise the value must match.
1773
The filename strings are compared by strcmp(), not by pointer. */
1774
for(size_t i = 0; i < queue->length; i++){
1775
task_context *const current_task = queue->tasks+i;
1776
/* Check all members of task_context, if set to a non-zero value.
1777
If a member does not match, continue to next task in queue */
1779
/* task_func *const func */
1780
if(task.func != NULL and current_task->func != task.func){
1783
/* char *const question_filename; */
1784
if(task.question_filename != NULL
1785
and (current_task->question_filename == NULL
1786
or strcmp(current_task->question_filename,
1787
task.question_filename) != 0)){
1790
/* const pid_t pid; */
1791
if(task.pid != 0 and current_task->pid != task.pid){
1794
/* const int epoll_fd; */
1795
if(task.epoll_fd != 0
1796
and current_task->epoll_fd != task.epoll_fd){
1799
/* bool *const quit_now; */
1800
if(task.quit_now != NULL
1801
and current_task->quit_now != task.quit_now){
1805
if(task.fd != 0 and current_task->fd != task.fd){
1808
/* bool *const mandos_client_exited; */
1809
if(task.mandos_client_exited != NULL
1810
and current_task->mandos_client_exited
1811
!= task.mandos_client_exited){
1814
/* buffer *const password; */
1815
if(task.password != NULL
1816
and current_task->password != task.password){
1819
/* bool *const password_is_read; */
1820
if(task.password_is_read != NULL
1821
and current_task->password_is_read != task.password_is_read){
1824
/* char *filename; */
1825
if(task.filename != NULL
1826
and (current_task->filename == NULL
1827
or strcmp(current_task->filename, task.filename) != 0)){
1830
/* string_set *const cancelled_filenames; */
1831
if(task.cancelled_filenames != NULL
1832
and current_task->cancelled_filenames
1833
!= task.cancelled_filenames){
1836
/* const mono_microsecs notafter; */
1837
if(task.notafter != 0
1838
and current_task->notafter != task.notafter){
1841
/* const mono_microsecs *const current_time; */
1842
if(task.current_time != NULL
1843
and current_task->current_time != task.current_time){
1846
/* Current task matches all members; return it */
1847
return current_task;
1849
/* No task in queue matches passed pattern task */
1853
static void test_create_queue(__attribute__((unused))
1854
test_fixture *fixture,
1855
__attribute__((unused))
1856
gconstpointer user_data){
1857
__attribute__((cleanup(cleanup_queue)))
1858
task_queue *const queue = create_queue();
1859
g_assert_nonnull(queue);
1860
g_assert_null(queue->tasks);
1861
g_assert_true(queue->length == 0);
1862
g_assert_true(queue->next_run == 0);
1865
static task_func dummy_func;
1867
static void test_add_to_queue(__attribute__((unused))
1868
test_fixture *fixture,
1869
__attribute__((unused))
1870
gconstpointer user_data){
1871
__attribute__((cleanup(cleanup_queue)))
1872
task_queue *queue = create_queue();
1873
g_assert_nonnull(queue);
1875
g_assert_true(add_to_queue(queue,
1876
(task_context){ .func=dummy_func }));
1877
g_assert_true(queue->length == 1);
1878
g_assert_nonnull(queue->tasks);
1879
g_assert_true(queue->tasks[0].func == dummy_func);
1882
static void dummy_func(__attribute__((unused))
1883
const task_context task,
1884
__attribute__((unused))
1885
task_queue *const queue){
1888
static void test_queue_has_question_empty(__attribute__((unused))
1889
test_fixture *fixture,
1890
__attribute__((unused))
1891
gconstpointer user_data){
1892
__attribute__((cleanup(cleanup_queue)))
1893
task_queue *queue = create_queue();
1894
g_assert_nonnull(queue);
1895
g_assert_false(queue_has_question(queue));
1898
static void test_queue_has_question_false(__attribute__((unused))
1899
test_fixture *fixture,
1900
__attribute__((unused))
1901
gconstpointer user_data){
1902
__attribute__((cleanup(cleanup_queue)))
1903
task_queue *queue = create_queue();
1904
g_assert_nonnull(queue);
1905
g_assert_true(add_to_queue(queue,
1906
(task_context){ .func=dummy_func }));
1907
g_assert_false(queue_has_question(queue));
1910
static void test_queue_has_question_true(__attribute__((unused))
1911
test_fixture *fixture,
1912
__attribute__((unused))
1913
gconstpointer user_data){
1914
__attribute__((cleanup(cleanup_queue)))
1915
task_queue *queue = create_queue();
1916
g_assert_nonnull(queue);
1917
char *const question_filename
1918
= strdup("/nonexistent/question_filename");
1919
g_assert_nonnull(question_filename);
1920
task_context task = {
1922
.question_filename=question_filename,
1924
g_assert_true(add_to_queue(queue, task));
1925
g_assert_true(queue_has_question(queue));
1928
static void test_queue_has_question_false2(__attribute__((unused))
1929
test_fixture *fixture,
1930
__attribute__((unused))
1931
gconstpointer user_data){
1932
__attribute__((cleanup(cleanup_queue)))
1933
task_queue *queue = create_queue();
1934
g_assert_nonnull(queue);
1935
task_context task = { .func=dummy_func };
1936
g_assert_true(add_to_queue(queue, task));
1937
g_assert_true(add_to_queue(queue, task));
1938
g_assert_cmpint((int)queue->length, ==, 2);
1939
g_assert_false(queue_has_question(queue));
1942
static void test_queue_has_question_true2(__attribute__((unused))
1943
test_fixture *fixture,
1944
__attribute__((unused))
1945
gconstpointer user_data){
1946
__attribute__((cleanup(cleanup_queue)))
1947
task_queue *queue = create_queue();
1948
g_assert_nonnull(queue);
1949
task_context task1 = { .func=dummy_func };
1950
g_assert_true(add_to_queue(queue, task1));
1951
char *const question_filename
1952
= strdup("/nonexistent/question_filename");
1953
g_assert_nonnull(question_filename);
1954
task_context task2 = {
1956
.question_filename=question_filename,
1958
g_assert_true(add_to_queue(queue, task2));
1959
g_assert_cmpint((int)queue->length, ==, 2);
1960
g_assert_true(queue_has_question(queue));
1963
static void test_cleanup_buffer(__attribute__((unused))
1964
test_fixture *fixture,
1965
__attribute__((unused))
1966
gconstpointer user_data){
1969
const size_t buffersize = 10;
1971
buf.data = malloc(buffersize);
1972
g_assert_nonnull(buf.data);
1973
if(mlock(buf.data, buffersize) != 0){
1974
g_assert_true(errno == EPERM or errno == ENOMEM);
1977
cleanup_buffer(&buf);
1978
g_assert_null(buf.data);
1982
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1983
test_fixture *fixture,
1984
__attribute__((unused))
1987
__attribute__((cleanup(string_set_clear)))
1988
string_set set = {};
1989
g_assert_false(string_set_contains(set, "")); /* Empty string */
1990
g_assert_false(string_set_contains(set, "test_string"));
1994
test_string_set_with_added_string_contains_it(__attribute__((unused))
1995
test_fixture *fixture,
1996
__attribute__((unused))
1999
__attribute__((cleanup(string_set_clear)))
2000
string_set set = {};
2001
g_assert_true(string_set_add(&set, "test_string"));
2002
g_assert_true(string_set_contains(set, "test_string"));
2006
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2007
test_fixture *fixture,
2008
__attribute__((unused))
2009
gconstpointer user_data){
2010
__attribute__((cleanup(string_set_clear)))
2011
string_set set = {};
2012
g_assert_true(string_set_add(&set, "test_string"));
2013
string_set_clear(&set);
2014
g_assert_false(string_set_contains(set, "test_string"));
2018
void test_string_set_swap_one_with_empty(__attribute__((unused))
2019
test_fixture *fixture,
2020
__attribute__((unused))
2021
gconstpointer user_data){
2022
__attribute__((cleanup(string_set_clear)))
2023
string_set set1 = {};
2024
__attribute__((cleanup(string_set_clear)))
2025
string_set set2 = {};
2026
g_assert_true(string_set_add(&set1, "test_string1"));
2027
string_set_swap(&set1, &set2);
2028
g_assert_false(string_set_contains(set1, "test_string1"));
2029
g_assert_true(string_set_contains(set2, "test_string1"));
2033
void test_string_set_swap_empty_with_one(__attribute__((unused))
2034
test_fixture *fixture,
2035
__attribute__((unused))
2036
gconstpointer user_data){
2037
__attribute__((cleanup(string_set_clear)))
2038
string_set set1 = {};
2039
__attribute__((cleanup(string_set_clear)))
2040
string_set set2 = {};
2041
g_assert_true(string_set_add(&set2, "test_string2"));
2042
string_set_swap(&set1, &set2);
2043
g_assert_true(string_set_contains(set1, "test_string2"));
2044
g_assert_false(string_set_contains(set2, "test_string2"));
2047
static void test_string_set_swap_one_with_one(__attribute__((unused))
2048
test_fixture *fixture,
2049
__attribute__((unused))
2052
__attribute__((cleanup(string_set_clear)))
2053
string_set set1 = {};
2054
__attribute__((cleanup(string_set_clear)))
2055
string_set set2 = {};
2056
g_assert_true(string_set_add(&set1, "test_string1"));
2057
g_assert_true(string_set_add(&set2, "test_string2"));
2058
string_set_swap(&set1, &set2);
2059
g_assert_false(string_set_contains(set1, "test_string1"));
2060
g_assert_true(string_set_contains(set1, "test_string2"));
2061
g_assert_false(string_set_contains(set2, "test_string2"));
2062
g_assert_true(string_set_contains(set2, "test_string1"));
2065
static bool fd_has_cloexec_and_nonblock(const int);
2067
static bool epoll_set_contains(int, int, uint32_t);
2069
static void test_start_mandos_client(test_fixture *fixture,
2070
__attribute__((unused))
2071
gconstpointer user_data){
2073
bool mandos_client_exited = false;
2074
bool quit_now = false;
2075
__attribute__((cleanup(cleanup_close)))
2076
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2077
g_assert_cmpint(epoll_fd, >=, 0);
2078
__attribute__((cleanup(cleanup_queue)))
2079
task_queue *queue = create_queue();
2080
g_assert_nonnull(queue);
2081
buffer password = {};
2082
bool password_is_read = false;
2083
const char helper_directory[] = "/nonexistent";
2084
const char *const argv[] = { "/bin/true", NULL };
2086
g_assert_true(start_mandos_client(queue, epoll_fd,
2087
&mandos_client_exited, &quit_now,
2088
&password, &password_is_read,
2089
&fixture->orig_sigaction,
2090
fixture->orig_sigmask,
2091
helper_directory, 0, 0, argv));
2093
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2095
const task_context *const added_wait_task
2096
= find_matching_task(queue, (task_context){
2097
.func=wait_for_mandos_client_exit,
2098
.mandos_client_exited=&mandos_client_exited,
2099
.quit_now=&quit_now,
2101
g_assert_nonnull(added_wait_task);
2102
g_assert_cmpint(added_wait_task->pid, >, 0);
2103
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2104
waitpid(added_wait_task->pid, NULL, 0);
2106
const task_context *const added_read_task
2107
= find_matching_task(queue, (task_context){
2108
.func=read_mandos_client_output,
2110
.password=&password,
2111
.password_is_read=&password_is_read,
2112
.quit_now=&quit_now,
2114
g_assert_nonnull(added_read_task);
2115
g_assert_cmpint(added_read_task->fd, >, 2);
2116
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2117
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2118
EPOLLIN | EPOLLRDHUP));
2121
static bool fd_has_cloexec_and_nonblock(const int fd){
2122
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2123
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2124
return ((socket_fd_flags >= 0)
2125
and (socket_fd_flags & FD_CLOEXEC)
2126
and (socket_file_flags >= 0)
2127
and (socket_file_flags & O_NONBLOCK));
2130
__attribute__((const))
2131
bool is_privileged(void){
2132
uid_t user = getuid() + 1;
2133
if(user == 0){ /* Overflow check */
2136
gid_t group = getuid() + 1;
2137
if(group == 0){ /* Overflow check */
2140
const pid_t pid = fork();
2141
if(pid == 0){ /* Child */
2142
if(setresgid((uid_t)-1, group, group) == -1){
2144
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2145
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2149
if(setresuid((uid_t)-1, user, user) == -1){
2151
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2152
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2159
waitpid(pid, &status, 0);
2160
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2166
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2167
/* Only scan for events in this eventmask */
2168
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2169
__attribute__((cleanup(cleanup_string)))
2170
char *fdinfo_name = NULL;
2171
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2172
g_assert_cmpint(ret, >, 0);
2173
g_assert_nonnull(fdinfo_name);
2175
FILE *fdinfo = fopen(fdinfo_name, "r");
2176
g_assert_nonnull(fdinfo);
2177
uint32_t reported_events;
2182
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2185
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2186
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2187
&found_fd, &reported_events) == 2){
2192
} while(not feof(fdinfo) and not ferror(fdinfo));
2193
g_assert_cmpint(fclose(fdinfo), ==, 0);
2200
/* Don't check events if none are given */
2203
return (reported_events & eventmask) == (events & eventmask);
2206
static void test_start_mandos_client_execv(test_fixture *fixture,
2207
__attribute__((unused))
2208
gconstpointer user_data){
2209
bool mandos_client_exited = false;
2210
bool quit_now = false;
2211
__attribute__((cleanup(cleanup_close)))
2212
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2213
g_assert_cmpint(epoll_fd, >=, 0);
2214
__attribute__((cleanup(cleanup_queue)))
2215
task_queue *queue = create_queue();
2216
g_assert_nonnull(queue);
2217
__attribute__((cleanup(cleanup_buffer)))
2218
buffer password = {};
2219
const char helper_directory[] = "/nonexistent";
2220
/* Can't execv("/", ...), so this should fail */
2221
const char *const argv[] = { "/", NULL };
2224
__attribute__((cleanup(cleanup_close)))
2225
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2226
g_assert_cmpint(devnull_fd, >=, 0);
2227
__attribute__((cleanup(cleanup_close)))
2228
const int real_stderr_fd = dup(STDERR_FILENO);
2229
g_assert_cmpint(real_stderr_fd, >=, 0);
2230
dup2(devnull_fd, STDERR_FILENO);
2232
const bool success = start_mandos_client(queue, epoll_fd,
2233
&mandos_client_exited,
2237
&fixture->orig_sigaction,
2238
fixture->orig_sigmask,
2239
helper_directory, 0, 0,
2241
dup2(real_stderr_fd, STDERR_FILENO);
2242
g_assert_true(success);
2244
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2246
struct timespec starttime, currtime;
2247
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2249
queue->next_run = 0;
2250
string_set cancelled_filenames = {};
2253
__attribute__((cleanup(cleanup_close)))
2254
const int devnull_fd = open("/dev/null",
2255
O_WRONLY | O_CLOEXEC);
2256
g_assert_cmpint(devnull_fd, >=, 0);
2257
__attribute__((cleanup(cleanup_close)))
2258
const int real_stderr_fd = dup(STDERR_FILENO);
2259
g_assert_cmpint(real_stderr_fd, >=, 0);
2260
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2261
dup2(devnull_fd, STDERR_FILENO);
2262
const bool success = run_queue(&queue, &cancelled_filenames,
2264
dup2(real_stderr_fd, STDERR_FILENO);
2269
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2270
} while(((queue->length) > 0)
2272
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2274
g_assert_true(quit_now);
2275
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2276
g_assert_true(mandos_client_exited);
2279
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2280
__attribute__((unused))
2283
if(not is_privileged()){
2284
g_test_skip("Not privileged");
2288
bool mandos_client_exited = false;
2289
bool quit_now = false;
2290
__attribute__((cleanup(cleanup_close)))
2291
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2292
g_assert_cmpint(epoll_fd, >=, 0);
2293
__attribute__((cleanup(cleanup_queue)))
2294
task_queue *queue = create_queue();
2295
g_assert_nonnull(queue);
2296
__attribute__((cleanup(cleanup_buffer)))
2297
buffer password = {};
2298
bool password_is_read = false;
2299
const char helper_directory[] = "/nonexistent";
2300
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2304
const bool success = start_mandos_client(queue, epoll_fd,
2305
&mandos_client_exited,
2306
&quit_now, &password,
2308
&fixture->orig_sigaction,
2309
fixture->orig_sigmask,
2310
helper_directory, user,
2312
g_assert_true(success);
2313
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2315
struct timespec starttime, currtime;
2316
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2318
queue->next_run = 0;
2319
string_set cancelled_filenames = {};
2320
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2321
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2322
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2323
} while(((queue->length) > 0)
2325
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2327
g_assert_false(quit_now);
2328
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2329
g_assert_true(mandos_client_exited);
2331
g_assert_true(password_is_read);
2332
g_assert_nonnull(password.data);
2335
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2337
g_assert_true((uid_t)id == id);
2339
g_assert_cmpuint((unsigned int)id, ==, 0);
2342
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2343
__attribute__((unused))
2346
if(not is_privileged()){
2347
g_test_skip("Not privileged");
2351
bool mandos_client_exited = false;
2352
bool quit_now = false;
2353
__attribute__((cleanup(cleanup_close)))
2354
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2355
g_assert_cmpint(epoll_fd, >=, 0);
2356
__attribute__((cleanup(cleanup_queue)))
2357
task_queue *queue = create_queue();
2358
g_assert_nonnull(queue);
2359
__attribute__((cleanup(cleanup_buffer)))
2360
buffer password = {};
2361
bool password_is_read = false;
2362
const char helper_directory[] = "/nonexistent";
2363
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2367
const bool success = start_mandos_client(queue, epoll_fd,
2368
&mandos_client_exited,
2369
&quit_now, &password,
2371
&fixture->orig_sigaction,
2372
fixture->orig_sigmask,
2373
helper_directory, user,
2375
g_assert_true(success);
2376
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2378
struct timespec starttime, currtime;
2379
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2381
queue->next_run = 0;
2382
string_set cancelled_filenames = {};
2383
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2384
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2385
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2386
} while(((queue->length) > 0)
2388
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2390
g_assert_false(quit_now);
2391
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2392
g_assert_true(mandos_client_exited);
2394
g_assert_true(password_is_read);
2395
g_assert_nonnull(password.data);
2398
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2400
g_assert_true((gid_t)id == id);
2402
g_assert_cmpuint((unsigned int)id, ==, 0);
2405
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2406
__attribute__((unused))
2409
if(not is_privileged()){
2410
g_test_skip("Not privileged");
2414
bool mandos_client_exited = false;
2415
bool quit_now = false;
2416
__attribute__((cleanup(cleanup_close)))
2417
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2418
g_assert_cmpint(epoll_fd, >=, 0);
2419
__attribute__((cleanup(cleanup_queue)))
2420
task_queue *queue = create_queue();
2421
g_assert_nonnull(queue);
2422
__attribute__((cleanup(cleanup_buffer)))
2423
buffer password = {};
2424
bool password_is_read = false;
2425
const char helper_directory[] = "/nonexistent";
2426
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2431
const bool success = start_mandos_client(queue, epoll_fd,
2432
&mandos_client_exited,
2433
&quit_now, &password,
2435
&fixture->orig_sigaction,
2436
fixture->orig_sigmask,
2437
helper_directory, user,
2439
g_assert_true(success);
2440
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2442
struct timespec starttime, currtime;
2443
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2445
queue->next_run = 0;
2446
string_set cancelled_filenames = {};
2447
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2448
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2449
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2450
} while(((queue->length) > 0)
2452
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2454
g_assert_false(quit_now);
2455
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2456
g_assert_true(mandos_client_exited);
2458
g_assert_true(password_is_read);
2459
g_assert_nonnull(password.data);
2462
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2464
g_assert_true((uid_t)id == id);
2466
g_assert_cmpuint((unsigned int)id, ==, user);
2469
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2470
__attribute__((unused))
2473
if(not is_privileged()){
2474
g_test_skip("Not privileged");
2478
bool mandos_client_exited = false;
2479
bool quit_now = false;
2480
__attribute__((cleanup(cleanup_close)))
2481
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2482
g_assert_cmpint(epoll_fd, >=, 0);
2483
__attribute__((cleanup(cleanup_queue)))
2484
task_queue *queue = create_queue();
2485
g_assert_nonnull(queue);
2486
__attribute__((cleanup(cleanup_buffer)))
2487
buffer password = {};
2488
bool password_is_read = false;
2489
const char helper_directory[] = "/nonexistent";
2490
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2495
const bool success = start_mandos_client(queue, epoll_fd,
2496
&mandos_client_exited,
2497
&quit_now, &password,
2499
&fixture->orig_sigaction,
2500
fixture->orig_sigmask,
2501
helper_directory, user,
2503
g_assert_true(success);
2504
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2506
struct timespec starttime, currtime;
2507
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2509
queue->next_run = 0;
2510
string_set cancelled_filenames = {};
2511
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2512
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2513
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2514
} while(((queue->length) > 0)
2516
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2518
g_assert_false(quit_now);
2519
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2520
g_assert_true(mandos_client_exited);
2522
g_assert_true(password_is_read);
2523
g_assert_nonnull(password.data);
2526
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2528
g_assert_true((gid_t)id == id);
2530
g_assert_cmpuint((unsigned int)id, ==, group);
2533
static void test_start_mandos_client_read(test_fixture *fixture,
2534
__attribute__((unused))
2535
gconstpointer user_data){
2536
bool mandos_client_exited = false;
2537
bool quit_now = false;
2538
__attribute__((cleanup(cleanup_close)))
2539
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2540
g_assert_cmpint(epoll_fd, >=, 0);
2541
__attribute__((cleanup(cleanup_queue)))
2542
task_queue *queue = create_queue();
2543
g_assert_nonnull(queue);
2544
__attribute__((cleanup(cleanup_buffer)))
2545
buffer password = {};
2546
bool password_is_read = false;
2547
const char dummy_test_password[] = "dummy test password";
2548
const char helper_directory[] = "/nonexistent";
2549
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2552
const bool success = start_mandos_client(queue, epoll_fd,
2553
&mandos_client_exited,
2554
&quit_now, &password,
2556
&fixture->orig_sigaction,
2557
fixture->orig_sigmask,
2558
helper_directory, 0, 0,
2560
g_assert_true(success);
2561
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2563
struct timespec starttime, currtime;
2564
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2566
queue->next_run = 0;
2567
string_set cancelled_filenames = {};
2568
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2569
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2570
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2571
} while(((queue->length) > 0)
2573
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2575
g_assert_false(quit_now);
2576
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2577
g_assert_true(mandos_client_exited);
2579
g_assert_true(password_is_read);
2580
g_assert_cmpint((int)password.length, ==,
2581
sizeof(dummy_test_password)-1);
2582
g_assert_nonnull(password.data);
2583
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2584
sizeof(dummy_test_password)-1), ==, 0);
2588
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2589
__attribute__((unused))
2592
bool mandos_client_exited = false;
2593
bool quit_now = false;
2594
__attribute__((cleanup(cleanup_close)))
2595
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2596
g_assert_cmpint(epoll_fd, >=, 0);
2597
__attribute__((cleanup(cleanup_queue)))
2598
task_queue *queue = create_queue();
2599
g_assert_nonnull(queue);
2600
__attribute__((cleanup(cleanup_buffer)))
2601
buffer password = {};
2602
bool password_is_read = false;
2603
const char helper_directory[] = "/nonexistent";
2604
const char *const argv[] = { "/bin/sh", "-c",
2605
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2607
const bool success = start_mandos_client(queue, epoll_fd,
2608
&mandos_client_exited,
2609
&quit_now, &password,
2611
&fixture->orig_sigaction,
2612
fixture->orig_sigmask,
2613
helper_directory, 0, 0,
2615
g_assert_true(success);
2616
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2618
struct timespec starttime, currtime;
2619
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2621
queue->next_run = 0;
2622
string_set cancelled_filenames = {};
2623
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2624
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2625
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2626
} while(((queue->length) > 0)
2628
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2630
g_assert_false(quit_now);
2631
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2632
g_assert_true(mandos_client_exited);
2634
g_assert_true(password_is_read);
2635
g_assert_cmpint((int)password.length, ==,
2636
sizeof(helper_directory)-1);
2637
g_assert_nonnull(password.data);
2638
g_assert_cmpint(memcmp(helper_directory, password.data,
2639
sizeof(helper_directory)-1), ==, 0);
2642
__attribute__((nonnull, warn_unused_result))
2643
static bool proc_status_sigblk_to_sigset(const char *const,
2646
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2647
__attribute__((unused))
2648
gconstpointer user_data){
2649
bool mandos_client_exited = false;
2650
bool quit_now = false;
2651
__attribute__((cleanup(cleanup_close)))
2652
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2653
g_assert_cmpint(epoll_fd, >=, 0);
2654
__attribute__((cleanup(cleanup_queue)))
2655
task_queue *queue = create_queue();
2656
g_assert_nonnull(queue);
2657
__attribute__((cleanup(cleanup_buffer)))
2658
buffer password = {};
2659
bool password_is_read = false;
2660
const char helper_directory[] = "/nonexistent";
2661
/* see proc(5) for format of /proc/self/status */
2662
const char *const argv[] = { "/usr/bin/awk",
2663
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2665
g_assert_true(start_mandos_client(queue, epoll_fd,
2666
&mandos_client_exited, &quit_now,
2667
&password, &password_is_read,
2668
&fixture->orig_sigaction,
2669
fixture->orig_sigmask,
2670
helper_directory, 0, 0, argv));
2672
struct timespec starttime, currtime;
2673
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2675
queue->next_run = 0;
2676
string_set cancelled_filenames = {};
2677
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2678
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2679
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2680
} while((not (mandos_client_exited and password_is_read))
2682
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2683
g_assert_true(mandos_client_exited);
2684
g_assert_true(password_is_read);
2686
sigset_t parsed_sigmask;
2687
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2690
for(int signum = 1; signum < NSIG; signum++){
2691
const bool has_signal = sigismember(&parsed_sigmask, signum);
2692
if(sigismember(&fixture->orig_sigmask, signum)){
2693
g_assert_true(has_signal);
2695
g_assert_false(has_signal);
2700
__attribute__((nonnull, warn_unused_result))
2701
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2702
sigset_t *const sigmask){
2703
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2704
uintmax_t scanned_sigmask;
2705
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2708
if(sigemptyset(sigmask) != 0){
2711
for(int signum = 1; signum < NSIG; signum++){
2712
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2713
if(sigaddset(sigmask, signum) != 0){
2721
static void run_task_with_stderr_to_dev_null(const task_context task,
2722
task_queue *const queue);
2725
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2726
test_fixture *fixture,
2727
__attribute__((unused))
2728
gconstpointer user_data){
2730
bool mandos_client_exited = false;
2731
bool quit_now = false;
2733
__attribute__((cleanup(cleanup_queue)))
2734
task_queue *queue = create_queue();
2735
g_assert_nonnull(queue);
2736
const task_context task = {
2737
.func=wait_for_mandos_client_exit,
2739
.mandos_client_exited=&mandos_client_exited,
2740
.quit_now=&quit_now,
2742
run_task_with_stderr_to_dev_null(task, queue);
2744
g_assert_false(mandos_client_exited);
2745
g_assert_true(quit_now);
2746
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2749
static void run_task_with_stderr_to_dev_null(const task_context task,
2750
task_queue *const queue){
2751
FILE *real_stderr = stderr;
2752
FILE *devnull = fopen("/dev/null", "we");
2753
g_assert_nonnull(devnull);
2756
task.func(task, queue);
2757
stderr = real_stderr;
2759
g_assert_cmpint(fclose(devnull), ==, 0);
2763
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2764
__attribute__((unused))
2765
gconstpointer user_data){
2766
bool mandos_client_exited = false;
2767
bool quit_now = false;
2769
pid_t create_eternal_process(void){
2770
const pid_t pid = fork();
2771
if(pid == 0){ /* Child */
2772
if(not restore_signal_handler(&fixture->orig_sigaction)){
2773
_exit(EXIT_FAILURE);
2775
if(not restore_sigmask(&fixture->orig_sigmask)){
2776
_exit(EXIT_FAILURE);
2784
pid_t pid = create_eternal_process();
2785
g_assert_true(pid != -1);
2787
__attribute__((cleanup(cleanup_queue)))
2788
task_queue *queue = create_queue();
2789
g_assert_nonnull(queue);
2790
const task_context task = {
2791
.func=wait_for_mandos_client_exit,
2793
.mandos_client_exited=&mandos_client_exited,
2794
.quit_now=&quit_now,
2796
task.func(task, queue);
2798
g_assert_false(mandos_client_exited);
2799
g_assert_false(quit_now);
2800
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2802
g_assert_nonnull(find_matching_task(queue, (task_context){
2803
.func=wait_for_mandos_client_exit,
2805
.mandos_client_exited=&mandos_client_exited,
2806
.quit_now=&quit_now,
2811
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2812
__attribute__((unused))
2815
bool mandos_client_exited = false;
2816
bool quit_now = false;
2818
pid_t create_successful_process(void){
2819
const pid_t pid = fork();
2820
if(pid == 0){ /* Child */
2821
if(not restore_signal_handler(&fixture->orig_sigaction)){
2822
_exit(EXIT_FAILURE);
2824
if(not restore_sigmask(&fixture->orig_sigmask)){
2825
_exit(EXIT_FAILURE);
2831
const pid_t pid = create_successful_process();
2832
g_assert_true(pid != -1);
2834
__attribute__((cleanup(cleanup_queue)))
2835
task_queue *queue = create_queue();
2836
g_assert_nonnull(queue);
2837
const task_context initial_task = {
2838
.func=wait_for_mandos_client_exit,
2840
.mandos_client_exited=&mandos_client_exited,
2841
.quit_now=&quit_now,
2843
g_assert_true(add_to_queue(queue, initial_task));
2845
struct timespec starttime, currtime;
2846
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2847
__attribute__((cleanup(cleanup_close)))
2848
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2850
queue->next_run = 0;
2851
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2852
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2853
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2854
} while((not mandos_client_exited)
2856
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2858
g_assert_true(mandos_client_exited);
2859
g_assert_false(quit_now);
2860
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2864
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2865
__attribute__((unused))
2868
bool mandos_client_exited = false;
2869
bool quit_now = false;
2871
pid_t create_failing_process(void){
2872
const pid_t pid = fork();
2873
if(pid == 0){ /* Child */
2874
if(not restore_signal_handler(&fixture->orig_sigaction)){
2875
_exit(EXIT_FAILURE);
2877
if(not restore_sigmask(&fixture->orig_sigmask)){
2878
_exit(EXIT_FAILURE);
2884
const pid_t pid = create_failing_process();
2885
g_assert_true(pid != -1);
2887
__attribute__((cleanup(string_set_clear)))
2888
string_set cancelled_filenames = {};
2889
__attribute__((cleanup(cleanup_close)))
2890
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2891
g_assert_cmpint(epoll_fd, >=, 0);
2892
__attribute__((cleanup(cleanup_queue)))
2893
task_queue *queue = create_queue();
2894
g_assert_nonnull(queue);
2895
g_assert_true(add_to_queue(queue, (task_context){
2896
.func=wait_for_mandos_client_exit,
2898
.mandos_client_exited=&mandos_client_exited,
2899
.quit_now=&quit_now,
2902
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2904
__attribute__((cleanup(cleanup_close)))
2905
const int devnull_fd = open("/dev/null",
2906
O_WRONLY | O_CLOEXEC);
2907
g_assert_cmpint(devnull_fd, >=, 0);
2908
__attribute__((cleanup(cleanup_close)))
2909
const int real_stderr_fd = dup(STDERR_FILENO);
2910
g_assert_cmpint(real_stderr_fd, >=, 0);
2912
struct timespec starttime, currtime;
2913
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2915
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2916
dup2(devnull_fd, STDERR_FILENO);
2917
const bool success = run_queue(&queue, &cancelled_filenames,
2919
dup2(real_stderr_fd, STDERR_FILENO);
2924
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2925
} while((not mandos_client_exited)
2927
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2929
g_assert_true(quit_now);
2930
g_assert_true(mandos_client_exited);
2931
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2935
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2936
__attribute__((unused))
2937
gconstpointer user_data){
2938
bool mandos_client_exited = false;
2939
bool quit_now = false;
2941
pid_t create_killed_process(void){
2942
const pid_t pid = fork();
2943
if(pid == 0){ /* Child */
2944
if(not restore_signal_handler(&fixture->orig_sigaction)){
2945
_exit(EXIT_FAILURE);
2947
if(not restore_sigmask(&fixture->orig_sigmask)){
2948
_exit(EXIT_FAILURE);
2957
const pid_t pid = create_killed_process();
2958
g_assert_true(pid != -1);
2960
__attribute__((cleanup(string_set_clear)))
2961
string_set cancelled_filenames = {};
2962
__attribute__((cleanup(cleanup_close)))
2963
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2964
g_assert_cmpint(epoll_fd, >=, 0);
2965
__attribute__((cleanup(cleanup_queue)))
2966
task_queue *queue = create_queue();
2967
g_assert_nonnull(queue);
2968
g_assert_true(add_to_queue(queue, (task_context){
2969
.func=wait_for_mandos_client_exit,
2971
.mandos_client_exited=&mandos_client_exited,
2972
.quit_now=&quit_now,
2975
__attribute__((cleanup(cleanup_close)))
2976
const int devnull_fd = open("/dev/null",
2977
O_WRONLY | O_CLOEXEC);
2978
g_assert_cmpint(devnull_fd, >=, 0);
2979
__attribute__((cleanup(cleanup_close)))
2980
const int real_stderr_fd = dup(STDERR_FILENO);
2981
g_assert_cmpint(real_stderr_fd, >=, 0);
2983
struct timespec starttime, currtime;
2984
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2986
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2987
dup2(devnull_fd, STDERR_FILENO);
2988
const bool success = run_queue(&queue, &cancelled_filenames,
2990
dup2(real_stderr_fd, STDERR_FILENO);
2995
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2996
} while((not mandos_client_exited)
2998
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3000
g_assert_true(mandos_client_exited);
3001
g_assert_true(quit_now);
3002
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3005
static bool epoll_set_does_not_contain(int, int);
3008
void test_read_mandos_client_output_readerror(__attribute__((unused))
3009
test_fixture *fixture,
3010
__attribute__((unused))
3013
__attribute__((cleanup(cleanup_close)))
3014
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3015
g_assert_cmpint(epoll_fd, >=, 0);
3017
__attribute__((cleanup(cleanup_buffer)))
3018
buffer password = {};
3020
/* Reading /proc/self/mem from offset 0 will always give EIO */
3021
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3023
bool password_is_read = false;
3024
bool quit_now = false;
3025
__attribute__((cleanup(cleanup_queue)))
3026
task_queue *queue = create_queue();
3027
g_assert_nonnull(queue);
3029
task_context task = {
3030
.func=read_mandos_client_output,
3033
.password=&password,
3034
.password_is_read=&password_is_read,
3035
.quit_now=&quit_now,
3037
run_task_with_stderr_to_dev_null(task, queue);
3038
g_assert_false(password_is_read);
3039
g_assert_cmpint((int)password.length, ==, 0);
3040
g_assert_true(quit_now);
3041
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3043
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3045
g_assert_cmpint(close(fd), ==, -1);
3048
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3049
return not epoll_set_contains(epoll_fd, fd, 0);
3053
void test_read_mandos_client_output_nodata(__attribute__((unused))
3054
test_fixture *fixture,
3055
__attribute__((unused))
3056
gconstpointer user_data){
3057
__attribute__((cleanup(cleanup_close)))
3058
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3059
g_assert_cmpint(epoll_fd, >=, 0);
3062
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3064
__attribute__((cleanup(cleanup_buffer)))
3065
buffer password = {};
3067
bool password_is_read = false;
3068
bool quit_now = false;
3069
__attribute__((cleanup(cleanup_queue)))
3070
task_queue *queue = create_queue();
3071
g_assert_nonnull(queue);
3073
task_context task = {
3074
.func=read_mandos_client_output,
3077
.password=&password,
3078
.password_is_read=&password_is_read,
3079
.quit_now=&quit_now,
3081
task.func(task, queue);
3082
g_assert_false(password_is_read);
3083
g_assert_cmpint((int)password.length, ==, 0);
3084
g_assert_false(quit_now);
3085
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3087
g_assert_nonnull(find_matching_task(queue, (task_context){
3088
.func=read_mandos_client_output,
3091
.password=&password,
3092
.password_is_read=&password_is_read,
3093
.quit_now=&quit_now,
3096
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3097
EPOLLIN | EPOLLRDHUP));
3099
g_assert_cmpint(close(pipefds[1]), ==, 0);
3102
static void test_read_mandos_client_output_eof(__attribute__((unused))
3103
test_fixture *fixture,
3104
__attribute__((unused))
3107
__attribute__((cleanup(cleanup_close)))
3108
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3109
g_assert_cmpint(epoll_fd, >=, 0);
3112
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3113
g_assert_cmpint(close(pipefds[1]), ==, 0);
3115
__attribute__((cleanup(cleanup_buffer)))
3116
buffer password = {};
3118
bool password_is_read = false;
3119
bool quit_now = false;
3120
__attribute__((cleanup(cleanup_queue)))
3121
task_queue *queue = create_queue();
3122
g_assert_nonnull(queue);
3124
task_context task = {
3125
.func=read_mandos_client_output,
3128
.password=&password,
3129
.password_is_read=&password_is_read,
3130
.quit_now=&quit_now,
3132
task.func(task, queue);
3133
g_assert_true(password_is_read);
3134
g_assert_cmpint((int)password.length, ==, 0);
3135
g_assert_false(quit_now);
3136
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3138
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3140
g_assert_cmpint(close(pipefds[0]), ==, -1);
3144
void test_read_mandos_client_output_once(__attribute__((unused))
3145
test_fixture *fixture,
3146
__attribute__((unused))
3147
gconstpointer user_data){
3148
__attribute__((cleanup(cleanup_close)))
3149
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3150
g_assert_cmpint(epoll_fd, >=, 0);
3153
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3155
const char dummy_test_password[] = "dummy test password";
3156
/* Start with a pre-allocated buffer */
3157
__attribute__((cleanup(cleanup_buffer)))
3159
.data=malloc(sizeof(dummy_test_password)),
3161
.allocated=sizeof(dummy_test_password),
3163
g_assert_nonnull(password.data);
3164
if(mlock(password.data, password.allocated) != 0){
3165
g_assert_true(errno == EPERM or errno == ENOMEM);
3168
bool password_is_read = false;
3169
bool quit_now = false;
3170
__attribute__((cleanup(cleanup_queue)))
3171
task_queue *queue = create_queue();
3172
g_assert_nonnull(queue);
3174
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3175
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3176
sizeof(dummy_test_password)),
3177
==, (int)sizeof(dummy_test_password));
3179
task_context task = {
3180
.func=read_mandos_client_output,
3183
.password=&password,
3184
.password_is_read=&password_is_read,
3185
.quit_now=&quit_now,
3187
task.func(task, queue);
3189
g_assert_false(password_is_read);
3190
g_assert_cmpint((int)password.length, ==,
3191
(int)sizeof(dummy_test_password));
3192
g_assert_nonnull(password.data);
3193
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3194
sizeof(dummy_test_password)), ==, 0);
3196
g_assert_false(quit_now);
3197
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3199
g_assert_nonnull(find_matching_task(queue, (task_context){
3200
.func=read_mandos_client_output,
3203
.password=&password,
3204
.password_is_read=&password_is_read,
3205
.quit_now=&quit_now,
3208
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3209
EPOLLIN | EPOLLRDHUP));
3211
g_assert_cmpint(close(pipefds[1]), ==, 0);
3215
void test_read_mandos_client_output_malloc(__attribute__((unused))
3216
test_fixture *fixture,
3217
__attribute__((unused))
3218
gconstpointer user_data){
3219
__attribute__((cleanup(cleanup_close)))
3220
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3221
g_assert_cmpint(epoll_fd, >=, 0);
3224
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3226
const char dummy_test_password[] = "dummy test password";
3227
/* Start with an empty buffer */
3228
__attribute__((cleanup(cleanup_buffer)))
3229
buffer password = {};
3231
bool password_is_read = false;
3232
bool quit_now = false;
3233
__attribute__((cleanup(cleanup_queue)))
3234
task_queue *queue = create_queue();
3235
g_assert_nonnull(queue);
3237
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3238
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3239
sizeof(dummy_test_password)),
3240
==, (int)sizeof(dummy_test_password));
3242
task_context task = {
3243
.func=read_mandos_client_output,
3246
.password=&password,
3247
.password_is_read=&password_is_read,
3248
.quit_now=&quit_now,
3250
task.func(task, queue);
3252
g_assert_false(password_is_read);
3253
g_assert_cmpint((int)password.length, ==,
3254
(int)sizeof(dummy_test_password));
3255
g_assert_nonnull(password.data);
3256
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3257
sizeof(dummy_test_password)), ==, 0);
3259
g_assert_false(quit_now);
3260
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3262
g_assert_nonnull(find_matching_task(queue, (task_context){
3263
.func=read_mandos_client_output,
3266
.password=&password,
3267
.password_is_read=&password_is_read,
3268
.quit_now=&quit_now,
3271
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3272
EPOLLIN | EPOLLRDHUP));
3274
g_assert_cmpint(close(pipefds[1]), ==, 0);
3278
void test_read_mandos_client_output_append(__attribute__((unused))
3279
test_fixture *fixture,
3280
__attribute__((unused))
3281
gconstpointer user_data){
3282
__attribute__((cleanup(cleanup_close)))
3283
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3284
g_assert_cmpint(epoll_fd, >=, 0);
3287
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3289
const char dummy_test_password[] = "dummy test password";
3290
__attribute__((cleanup(cleanup_buffer)))
3292
.data=malloc(PIPE_BUF),
3294
.allocated=PIPE_BUF,
3296
g_assert_nonnull(password.data);
3297
if(mlock(password.data, password.allocated) != 0){
3298
g_assert_true(errno == EPERM or errno == ENOMEM);
3301
memset(password.data, 'x', PIPE_BUF);
3302
char password_expected[PIPE_BUF];
3303
memcpy(password_expected, password.data, PIPE_BUF);
3305
bool password_is_read = false;
3306
bool quit_now = false;
3307
__attribute__((cleanup(cleanup_queue)))
3308
task_queue *queue = create_queue();
3309
g_assert_nonnull(queue);
3311
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3312
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3313
sizeof(dummy_test_password)),
3314
==, (int)sizeof(dummy_test_password));
3316
task_context task = {
3317
.func=read_mandos_client_output,
3320
.password=&password,
3321
.password_is_read=&password_is_read,
3322
.quit_now=&quit_now,
3324
task.func(task, queue);
3326
g_assert_false(password_is_read);
3327
g_assert_cmpint((int)password.length, ==,
3328
PIPE_BUF + sizeof(dummy_test_password));
3329
g_assert_nonnull(password.data);
3330
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3332
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3333
dummy_test_password,
3334
sizeof(dummy_test_password)), ==, 0);
3335
g_assert_false(quit_now);
3336
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3338
g_assert_nonnull(find_matching_task(queue, (task_context){
3339
.func=read_mandos_client_output,
3342
.password=&password,
3343
.password_is_read=&password_is_read,
3344
.quit_now=&quit_now,
3347
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3348
EPOLLIN | EPOLLRDHUP));
3351
static char *make_temporary_directory(void);
3353
static void test_add_inotify_dir_watch(__attribute__((unused))
3354
test_fixture *fixture,
3355
__attribute__((unused))
3356
gconstpointer user_data){
3357
__attribute__((cleanup(cleanup_close)))
3358
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3359
g_assert_cmpint(epoll_fd, >=, 0);
3360
__attribute__((cleanup(cleanup_queue)))
3361
task_queue *queue = create_queue();
3362
g_assert_nonnull(queue);
3363
__attribute__((cleanup(string_set_clear)))
3364
string_set cancelled_filenames = {};
3365
const mono_microsecs current_time = 0;
3367
bool quit_now = false;
3368
buffer password = {};
3369
bool mandos_client_exited = false;
3370
bool password_is_read = false;
3372
__attribute__((cleanup(cleanup_string)))
3373
char *tempdir = make_temporary_directory();
3374
g_assert_nonnull(tempdir);
3376
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3378
&cancelled_filenames,
3380
&mandos_client_exited,
3381
&password_is_read));
3383
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3385
const task_context *const added_read_task
3386
= find_matching_task(queue, (task_context){
3387
.func=read_inotify_event,
3389
.quit_now=&quit_now,
3390
.password=&password,
3392
.cancelled_filenames=&cancelled_filenames,
3393
.current_time=¤t_time,
3394
.mandos_client_exited=&mandos_client_exited,
3395
.password_is_read=&password_is_read,
3397
g_assert_nonnull(added_read_task);
3399
g_assert_cmpint(added_read_task->fd, >, 2);
3400
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3401
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3402
added_read_task->fd,
3403
EPOLLIN | EPOLLRDHUP));
3405
g_assert_cmpint(rmdir(tempdir), ==, 0);
3408
static char *make_temporary_directory(void){
3409
char *name = strdup("/tmp/mandosXXXXXX");
3410
g_assert_nonnull(name);
3411
char *result = mkdtemp(name);
3418
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3419
test_fixture *fixture,
3420
__attribute__((unused))
3421
gconstpointer user_data){
3422
__attribute__((cleanup(cleanup_close)))
3423
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3424
g_assert_cmpint(epoll_fd, >=, 0);
3425
__attribute__((cleanup(cleanup_queue)))
3426
task_queue *queue = create_queue();
3427
g_assert_nonnull(queue);
3428
__attribute__((cleanup(string_set_clear)))
3429
string_set cancelled_filenames = {};
3430
const mono_microsecs current_time = 0;
3432
bool quit_now = false;
3433
buffer password = {};
3434
bool mandos_client_exited = false;
3435
bool password_is_read = false;
3437
const char nonexistent_dir[] = "/nonexistent";
3439
FILE *real_stderr = stderr;
3440
FILE *devnull = fopen("/dev/null", "we");
3441
g_assert_nonnull(devnull);
3443
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3444
&password, nonexistent_dir,
3445
&cancelled_filenames,
3447
&mandos_client_exited,
3448
&password_is_read));
3449
stderr = real_stderr;
3450
g_assert_cmpint(fclose(devnull), ==, 0);
3452
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3456
test_fixture *fixture,
3457
__attribute__((unused))
3460
__attribute__((cleanup(cleanup_close)))
3461
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3462
g_assert_cmpint(epoll_fd, >=, 0);
3463
__attribute__((cleanup(cleanup_queue)))
3464
task_queue *queue = create_queue();
3465
g_assert_nonnull(queue);
3466
__attribute__((cleanup(string_set_clear)))
3467
string_set cancelled_filenames = {};
3468
const mono_microsecs current_time = 0;
3470
bool quit_now = false;
3471
buffer password = {};
3472
bool mandos_client_exited = false;
3473
bool password_is_read = false;
3475
__attribute__((cleanup(cleanup_string)))
3476
char *tempdir = make_temporary_directory();
3477
g_assert_nonnull(tempdir);
3479
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3481
&cancelled_filenames,
3483
&mandos_client_exited,
3484
&password_is_read));
3486
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3488
const task_context *const added_read_task
3489
= find_matching_task(queue,
3490
(task_context){ .func=read_inotify_event });
3491
g_assert_nonnull(added_read_task);
3493
g_assert_cmpint(added_read_task->fd, >, 2);
3494
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3496
/* "sufficient to read at least one event." - inotify(7) */
3497
const size_t ievent_size = (sizeof(struct inotify_event)
3499
struct inotify_event *ievent = malloc(ievent_size);
3500
g_assert_nonnull(ievent);
3502
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3504
g_assert_cmpint(errno, ==, EAGAIN);
3508
g_assert_cmpint(rmdir(tempdir), ==, 0);
3511
static char *make_temporary_file_in_directory(const char
3515
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3516
test_fixture *fixture,
3517
__attribute__((unused))
3520
__attribute__((cleanup(cleanup_close)))
3521
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3522
g_assert_cmpint(epoll_fd, >=, 0);
3523
__attribute__((cleanup(cleanup_queue)))
3524
task_queue *queue = create_queue();
3525
g_assert_nonnull(queue);
3526
__attribute__((cleanup(string_set_clear)))
3527
string_set cancelled_filenames = {};
3528
const mono_microsecs current_time = 0;
3530
bool quit_now = false;
3531
buffer password = {};
3532
bool mandos_client_exited = false;
3533
bool password_is_read = false;
3535
__attribute__((cleanup(cleanup_string)))
3536
char *tempdir = make_temporary_directory();
3537
g_assert_nonnull(tempdir);
3539
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3541
&cancelled_filenames,
3543
&mandos_client_exited,
3544
&password_is_read));
3546
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3548
const task_context *const added_read_task
3549
= find_matching_task(queue,
3550
(task_context){ .func=read_inotify_event });
3551
g_assert_nonnull(added_read_task);
3553
g_assert_cmpint(added_read_task->fd, >, 2);
3554
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3556
__attribute__((cleanup(cleanup_string)))
3557
char *filename = make_temporary_file_in_directory(tempdir);
3558
g_assert_nonnull(filename);
3560
/* "sufficient to read at least one event." - inotify(7) */
3561
const size_t ievent_size = (sizeof(struct inotify_event)
3563
struct inotify_event *ievent = malloc(ievent_size);
3564
g_assert_nonnull(ievent);
3566
ssize_t read_size = 0;
3567
read_size = read(added_read_task->fd, ievent, ievent_size);
3569
g_assert_cmpint((int)read_size, >, 0);
3570
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3571
g_assert_cmpstr(ievent->name, ==, basename(filename));
3575
g_assert_cmpint(unlink(filename), ==, 0);
3576
g_assert_cmpint(rmdir(tempdir), ==, 0);
3579
static char *make_temporary_prefixed_file_in_directory(const char
3583
char *filename = NULL;
3584
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3586
g_assert_nonnull(filename);
3587
const int fd = mkostemp(filename, O_CLOEXEC);
3588
g_assert_cmpint(fd, >=, 0);
3589
g_assert_cmpint(close(fd), ==, 0);
3593
static char *make_temporary_file_in_directory(const char
3595
return make_temporary_prefixed_file_in_directory("temp", dir);
3599
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3600
test_fixture *fixture,
3601
__attribute__((unused))
3602
gconstpointer user_data){
3603
__attribute__((cleanup(cleanup_close)))
3604
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3605
g_assert_cmpint(epoll_fd, >=, 0);
3606
__attribute__((cleanup(cleanup_queue)))
3607
task_queue *queue = create_queue();
3608
g_assert_nonnull(queue);
3609
__attribute__((cleanup(string_set_clear)))
3610
string_set cancelled_filenames = {};
3611
const mono_microsecs current_time = 0;
3613
bool quit_now = false;
3614
buffer password = {};
3615
bool mandos_client_exited = false;
3616
bool password_is_read = false;
3618
__attribute__((cleanup(cleanup_string)))
3619
char *watchdir = make_temporary_directory();
3620
g_assert_nonnull(watchdir);
3622
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3623
&password, watchdir,
3624
&cancelled_filenames,
3626
&mandos_client_exited,
3627
&password_is_read));
3629
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3631
const task_context *const added_read_task
3632
= find_matching_task(queue,
3633
(task_context){ .func=read_inotify_event });
3634
g_assert_nonnull(added_read_task);
3636
g_assert_cmpint(added_read_task->fd, >, 2);
3637
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3639
char *sourcedir = make_temporary_directory();
3640
g_assert_nonnull(sourcedir);
3642
__attribute__((cleanup(cleanup_string)))
3643
char *filename = make_temporary_file_in_directory(sourcedir);
3644
g_assert_nonnull(filename);
3646
__attribute__((cleanup(cleanup_string)))
3647
char *targetfilename = NULL;
3648
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3649
basename(filename)), >, 0);
3650
g_assert_nonnull(targetfilename);
3652
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3653
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3656
/* "sufficient to read at least one event." - inotify(7) */
3657
const size_t ievent_size = (sizeof(struct inotify_event)
3659
struct inotify_event *ievent = malloc(ievent_size);
3660
g_assert_nonnull(ievent);
3662
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3664
g_assert_cmpint((int)read_size, >, 0);
3665
g_assert_true(ievent->mask & IN_MOVED_TO);
3666
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3670
g_assert_cmpint(unlink(targetfilename), ==, 0);
3671
g_assert_cmpint(rmdir(watchdir), ==, 0);
3675
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3676
test_fixture *fixture,
3677
__attribute__((unused))
3680
__attribute__((cleanup(cleanup_close)))
3681
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3682
g_assert_cmpint(epoll_fd, >=, 0);
3683
__attribute__((cleanup(cleanup_queue)))
3684
task_queue *queue = create_queue();
3685
g_assert_nonnull(queue);
3686
__attribute__((cleanup(string_set_clear)))
3687
string_set cancelled_filenames = {};
3688
const mono_microsecs current_time = 0;
3690
bool quit_now = false;
3691
buffer password = {};
3692
bool mandos_client_exited = false;
3693
bool password_is_read = false;
3695
__attribute__((cleanup(cleanup_string)))
3696
char *tempdir = make_temporary_directory();
3697
g_assert_nonnull(tempdir);
3699
__attribute__((cleanup(cleanup_string)))
3700
char *tempfilename = make_temporary_file_in_directory(tempdir);
3701
g_assert_nonnull(tempfilename);
3703
__attribute__((cleanup(cleanup_string)))
3704
char *targetdir = make_temporary_directory();
3705
g_assert_nonnull(targetdir);
3707
__attribute__((cleanup(cleanup_string)))
3708
char *targetfilename = NULL;
3709
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3710
basename(tempfilename)), >, 0);
3711
g_assert_nonnull(targetfilename);
3713
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3715
&cancelled_filenames,
3717
&mandos_client_exited,
3718
&password_is_read));
3720
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3722
const task_context *const added_read_task
3723
= find_matching_task(queue,
3724
(task_context){ .func=read_inotify_event });
3725
g_assert_nonnull(added_read_task);
3727
/* "sufficient to read at least one event." - inotify(7) */
3728
const size_t ievent_size = (sizeof(struct inotify_event)
3730
struct inotify_event *ievent = malloc(ievent_size);
3731
g_assert_nonnull(ievent);
3733
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3735
g_assert_cmpint((int)read_size, >, 0);
3736
g_assert_true(ievent->mask & IN_MOVED_FROM);
3737
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3741
g_assert_cmpint(unlink(targetfilename), ==, 0);
3742
g_assert_cmpint(rmdir(targetdir), ==, 0);
3743
g_assert_cmpint(rmdir(tempdir), ==, 0);
3747
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3748
test_fixture *fixture,
3749
__attribute__((unused))
3750
gconstpointer user_data){
3751
__attribute__((cleanup(cleanup_close)))
3752
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3753
g_assert_cmpint(epoll_fd, >=, 0);
3754
__attribute__((cleanup(cleanup_queue)))
3755
task_queue *queue = create_queue();
3756
g_assert_nonnull(queue);
3757
__attribute__((cleanup(string_set_clear)))
3758
string_set cancelled_filenames = {};
3759
const mono_microsecs current_time = 0;
3761
bool quit_now = false;
3762
buffer password = {};
3763
bool mandos_client_exited = false;
3764
bool password_is_read = false;
3766
__attribute__((cleanup(cleanup_string)))
3767
char *tempdir = make_temporary_directory();
3768
g_assert_nonnull(tempdir);
3770
__attribute__((cleanup(cleanup_string)))
3771
char *tempfile = make_temporary_file_in_directory(tempdir);
3772
g_assert_nonnull(tempfile);
3774
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3776
&cancelled_filenames,
3778
&mandos_client_exited,
3779
&password_is_read));
3780
g_assert_cmpint(unlink(tempfile), ==, 0);
3782
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3784
const task_context *const added_read_task
3785
= find_matching_task(queue,
3786
(task_context){ .func=read_inotify_event });
3787
g_assert_nonnull(added_read_task);
3789
g_assert_cmpint(added_read_task->fd, >, 2);
3790
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3792
/* "sufficient to read at least one event." - inotify(7) */
3793
const size_t ievent_size = (sizeof(struct inotify_event)
3795
struct inotify_event *ievent = malloc(ievent_size);
3796
g_assert_nonnull(ievent);
3798
ssize_t read_size = 0;
3799
read_size = read(added_read_task->fd, ievent, ievent_size);
3801
g_assert_cmpint((int)read_size, >, 0);
3802
g_assert_true(ievent->mask & IN_DELETE);
3803
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3807
g_assert_cmpint(rmdir(tempdir), ==, 0);
3811
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3812
test_fixture *fixture,
3813
__attribute__((unused))
3816
__attribute__((cleanup(cleanup_close)))
3817
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3818
g_assert_cmpint(epoll_fd, >=, 0);
3819
__attribute__((cleanup(cleanup_queue)))
3820
task_queue *queue = create_queue();
3821
g_assert_nonnull(queue);
3822
__attribute__((cleanup(string_set_clear)))
3823
string_set cancelled_filenames = {};
3824
const mono_microsecs current_time = 0;
3826
bool quit_now = false;
3827
buffer password = {};
3828
bool mandos_client_exited = false;
3829
bool password_is_read = false;
3831
__attribute__((cleanup(cleanup_string)))
3832
char *tempdir = make_temporary_directory();
3833
g_assert_nonnull(tempdir);
3835
__attribute__((cleanup(cleanup_string)))
3836
char *tempfile = make_temporary_file_in_directory(tempdir);
3837
g_assert_nonnull(tempfile);
3838
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3840
g_assert_cmpint(tempfile_fd, >, 2);
3842
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3844
&cancelled_filenames,
3846
&mandos_client_exited,
3847
&password_is_read));
3848
g_assert_cmpint(unlink(tempfile), ==, 0);
3850
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3852
const task_context *const added_read_task
3853
= find_matching_task(queue,
3854
(task_context){ .func=read_inotify_event });
3855
g_assert_nonnull(added_read_task);
3857
g_assert_cmpint(added_read_task->fd, >, 2);
3858
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3860
/* "sufficient to read at least one event." - inotify(7) */
3861
const size_t ievent_size = (sizeof(struct inotify_event)
3863
struct inotify_event *ievent = malloc(ievent_size);
3864
g_assert_nonnull(ievent);
3866
ssize_t read_size = 0;
3867
read_size = read(added_read_task->fd, ievent, ievent_size);
3869
g_assert_cmpint((int)read_size, >, 0);
3870
g_assert_true(ievent->mask & IN_DELETE);
3871
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3873
g_assert_cmpint(close(tempfile_fd), ==, 0);
3875
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3876
file not appear as an ievent, so we should not see it now. */
3877
read_size = read(added_read_task->fd, ievent, ievent_size);
3878
g_assert_cmpint((int)read_size, ==, -1);
3879
g_assert_true(errno == EAGAIN);
3883
g_assert_cmpint(rmdir(tempdir), ==, 0);
3886
static void test_read_inotify_event_readerror(__attribute__((unused))
3887
test_fixture *fixture,
3888
__attribute__((unused))
3891
__attribute__((cleanup(cleanup_close)))
3892
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3893
g_assert_cmpint(epoll_fd, >=, 0);
3894
const mono_microsecs current_time = 0;
3896
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3897
const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3899
bool quit_now = false;
3900
__attribute__((cleanup(cleanup_queue)))
3901
task_queue *queue = create_queue();
3902
g_assert_nonnull(queue);
3904
task_context task = {
3905
.func=read_inotify_event,
3908
.quit_now=&quit_now,
3909
.filename=strdup("/nonexistent"),
3910
.cancelled_filenames = &(string_set){},
3912
.current_time=¤t_time,
3914
g_assert_nonnull(task.filename);
3915
run_task_with_stderr_to_dev_null(task, queue);
3916
g_assert_true(quit_now);
3917
g_assert_true(queue->next_run == 0);
3918
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3920
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3922
g_assert_cmpint(close(fd), ==, -1);
3925
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3926
test_fixture *fixture,
3927
__attribute__((unused))
3930
const mono_microsecs current_time = 17;
3933
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3934
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3936
bool quit_now = false;
3937
buffer password = {};
3938
bool mandos_client_exited = false;
3939
bool password_is_read = false;
3940
__attribute__((cleanup(cleanup_queue)))
3941
task_queue *queue = create_queue();
3942
g_assert_nonnull(queue);
3944
task_context task = {
3945
.func=read_inotify_event,
3948
.quit_now=&quit_now,
3949
.password=&password,
3950
.filename=strdup("/nonexistent"),
3951
.cancelled_filenames = &(string_set){},
3953
.current_time=¤t_time,
3954
.mandos_client_exited=&mandos_client_exited,
3955
.password_is_read=&password_is_read,
3957
g_assert_nonnull(task.filename);
3958
run_task_with_stderr_to_dev_null(task, queue);
3960
g_assert_nonnull(find_matching_task(queue, task));
3961
g_assert_true(queue->next_run == 1000000 + current_time);
3963
g_assert_cmpint(close(pipefds[0]), ==, 0);
3964
g_assert_cmpint(close(pipefds[1]), ==, 0);
3967
static void test_read_inotify_event_nodata(__attribute__((unused))
3968
test_fixture *fixture,
3969
__attribute__((unused))
3970
gconstpointer user_data){
3971
__attribute__((cleanup(cleanup_close)))
3972
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3973
g_assert_cmpint(epoll_fd, >=, 0);
3974
const mono_microsecs current_time = 0;
3977
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3979
bool quit_now = false;
3980
buffer password = {};
3981
bool mandos_client_exited = false;
3982
bool password_is_read = false;
3983
__attribute__((cleanup(cleanup_queue)))
3984
task_queue *queue = create_queue();
3985
g_assert_nonnull(queue);
3987
task_context task = {
3988
.func=read_inotify_event,
3991
.quit_now=&quit_now,
3992
.password=&password,
3993
.filename=strdup("/nonexistent"),
3994
.cancelled_filenames = &(string_set){},
3996
.current_time=¤t_time,
3997
.mandos_client_exited=&mandos_client_exited,
3998
.password_is_read=&password_is_read,
4000
g_assert_nonnull(task.filename);
4001
task.func(task, queue);
4002
g_assert_false(quit_now);
4003
g_assert_true(queue->next_run == 0);
4004
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4006
g_assert_nonnull(find_matching_task(queue, (task_context){
4007
.func=read_inotify_event,
4010
.quit_now=&quit_now,
4011
.password=&password,
4012
.filename=task.filename,
4013
.cancelled_filenames=task.cancelled_filenames,
4014
.current_time=¤t_time,
4015
.mandos_client_exited=&mandos_client_exited,
4016
.password_is_read=&password_is_read,
4019
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4020
EPOLLIN | EPOLLRDHUP));
4022
g_assert_cmpint(close(pipefds[1]), ==, 0);
4025
static void test_read_inotify_event_eof(__attribute__((unused))
4026
test_fixture *fixture,
4027
__attribute__((unused))
4028
gconstpointer user_data){
4029
__attribute__((cleanup(cleanup_close)))
4030
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4031
g_assert_cmpint(epoll_fd, >=, 0);
4032
const mono_microsecs current_time = 0;
4035
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4036
g_assert_cmpint(close(pipefds[1]), ==, 0);
4038
bool quit_now = false;
4039
buffer password = {};
4040
__attribute__((cleanup(cleanup_queue)))
4041
task_queue *queue = create_queue();
4042
g_assert_nonnull(queue);
4044
task_context task = {
4045
.func=read_inotify_event,
4048
.quit_now=&quit_now,
4049
.password=&password,
4050
.filename=strdup("/nonexistent"),
4051
.cancelled_filenames = &(string_set){},
4053
.current_time=¤t_time,
4055
run_task_with_stderr_to_dev_null(task, queue);
4056
g_assert_true(quit_now);
4057
g_assert_true(queue->next_run == 0);
4058
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4060
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4062
g_assert_cmpint(close(pipefds[0]), ==, -1);
4066
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4067
test_fixture *fixture,
4068
__attribute__((unused))
4069
gconstpointer user_data){
4070
__attribute__((cleanup(cleanup_close)))
4071
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4072
g_assert_cmpint(epoll_fd, >=, 0);
4073
const mono_microsecs current_time = 0;
4076
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4078
/* "sufficient to read at least one event." - inotify(7) */
4079
const size_t ievent_max_size = (sizeof(struct inotify_event)
4081
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4083
struct inotify_event event;
4084
char name_buffer[NAME_MAX + 1];
4086
struct inotify_event *const ievent = &ievent_buffer.event;
4088
const char dummy_file_name[] = "ask.dummy_file_name";
4089
ievent->mask = IN_CLOSE_WRITE;
4090
ievent->len = sizeof(dummy_file_name);
4091
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4092
const size_t ievent_size = (sizeof(struct inotify_event)
4093
+ sizeof(dummy_file_name));
4094
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4096
g_assert_cmpint(close(pipefds[1]), ==, 0);
4098
bool quit_now = false;
4099
buffer password = {};
4100
bool mandos_client_exited = false;
4101
bool password_is_read = false;
4102
__attribute__((cleanup(cleanup_queue)))
4103
task_queue *queue = create_queue();
4104
g_assert_nonnull(queue);
4106
task_context task = {
4107
.func=read_inotify_event,
4110
.quit_now=&quit_now,
4111
.password=&password,
4112
.filename=strdup("/nonexistent"),
4113
.cancelled_filenames = &(string_set){},
4115
.current_time=¤t_time,
4116
.mandos_client_exited=&mandos_client_exited,
4117
.password_is_read=&password_is_read,
4119
task.func(task, queue);
4120
g_assert_false(quit_now);
4121
g_assert_true(queue->next_run != 0);
4122
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4124
g_assert_nonnull(find_matching_task(queue, (task_context){
4125
.func=read_inotify_event,
4128
.quit_now=&quit_now,
4129
.password=&password,
4130
.filename=task.filename,
4131
.cancelled_filenames=task.cancelled_filenames,
4132
.current_time=¤t_time,
4133
.mandos_client_exited=&mandos_client_exited,
4134
.password_is_read=&password_is_read,
4137
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4138
EPOLLIN | EPOLLRDHUP));
4140
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4142
__attribute__((cleanup(cleanup_string)))
4143
char *filename = NULL;
4144
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4145
dummy_file_name), >, 0);
4146
g_assert_nonnull(filename);
4147
g_assert_nonnull(find_matching_task(queue, (task_context){
4148
.func=open_and_parse_question,
4151
.question_filename=filename,
4152
.password=&password,
4153
.cancelled_filenames=task.cancelled_filenames,
4154
.current_time=¤t_time,
4155
.mandos_client_exited=&mandos_client_exited,
4156
.password_is_read=&password_is_read,
4161
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4162
test_fixture *fixture,
4163
__attribute__((unused))
4164
gconstpointer user_data){
4165
__attribute__((cleanup(cleanup_close)))
4166
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4167
g_assert_cmpint(epoll_fd, >=, 0);
4168
const mono_microsecs current_time = 0;
4171
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4173
/* "sufficient to read at least one event." - inotify(7) */
4174
const size_t ievent_max_size = (sizeof(struct inotify_event)
4176
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4178
struct inotify_event event;
4179
char name_buffer[NAME_MAX + 1];
4181
struct inotify_event *const ievent = &ievent_buffer.event;
4183
const char dummy_file_name[] = "ask.dummy_file_name";
4184
ievent->mask = IN_MOVED_TO;
4185
ievent->len = sizeof(dummy_file_name);
4186
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4187
const size_t ievent_size = (sizeof(struct inotify_event)
4188
+ sizeof(dummy_file_name));
4189
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4191
g_assert_cmpint(close(pipefds[1]), ==, 0);
4193
bool quit_now = false;
4194
buffer password = {};
4195
bool mandos_client_exited = false;
4196
bool password_is_read = false;
4197
__attribute__((cleanup(cleanup_queue)))
4198
task_queue *queue = create_queue();
4199
g_assert_nonnull(queue);
4201
task_context task = {
4202
.func=read_inotify_event,
4205
.quit_now=&quit_now,
4206
.password=&password,
4207
.filename=strdup("/nonexistent"),
4208
.cancelled_filenames = &(string_set){},
4210
.current_time=¤t_time,
4211
.mandos_client_exited=&mandos_client_exited,
4212
.password_is_read=&password_is_read,
4214
task.func(task, queue);
4215
g_assert_false(quit_now);
4216
g_assert_true(queue->next_run != 0);
4217
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4219
g_assert_nonnull(find_matching_task(queue, (task_context){
4220
.func=read_inotify_event,
4223
.quit_now=&quit_now,
4224
.password=&password,
4225
.filename=task.filename,
4226
.cancelled_filenames=task.cancelled_filenames,
4227
.current_time=¤t_time,
4228
.mandos_client_exited=&mandos_client_exited,
4229
.password_is_read=&password_is_read,
4232
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4233
EPOLLIN | EPOLLRDHUP));
4235
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4237
__attribute__((cleanup(cleanup_string)))
4238
char *filename = NULL;
4239
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4240
dummy_file_name), >, 0);
4241
g_assert_nonnull(filename);
4242
g_assert_nonnull(find_matching_task(queue, (task_context){
4243
.func=open_and_parse_question,
4246
.question_filename=filename,
4247
.password=&password,
4248
.cancelled_filenames=task.cancelled_filenames,
4249
.current_time=¤t_time,
4250
.mandos_client_exited=&mandos_client_exited,
4251
.password_is_read=&password_is_read,
4256
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4257
test_fixture *fixture,
4258
__attribute__((unused))
4259
gconstpointer user_data){
4260
__attribute__((cleanup(cleanup_close)))
4261
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4262
g_assert_cmpint(epoll_fd, >=, 0);
4263
__attribute__((cleanup(string_set_clear)))
4264
string_set cancelled_filenames = {};
4265
const mono_microsecs current_time = 0;
4268
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4270
/* "sufficient to read at least one event." - inotify(7) */
4271
const size_t ievent_max_size = (sizeof(struct inotify_event)
4273
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4275
struct inotify_event event;
4276
char name_buffer[NAME_MAX + 1];
4278
struct inotify_event *const ievent = &ievent_buffer.event;
4280
const char dummy_file_name[] = "ask.dummy_file_name";
4281
ievent->mask = IN_MOVED_FROM;
4282
ievent->len = sizeof(dummy_file_name);
4283
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4284
const size_t ievent_size = (sizeof(struct inotify_event)
4285
+ sizeof(dummy_file_name));
4286
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4288
g_assert_cmpint(close(pipefds[1]), ==, 0);
4290
bool quit_now = false;
4291
buffer password = {};
4292
bool mandos_client_exited = false;
4293
bool password_is_read = false;
4294
__attribute__((cleanup(cleanup_queue)))
4295
task_queue *queue = create_queue();
4296
g_assert_nonnull(queue);
4298
task_context task = {
4299
.func=read_inotify_event,
4302
.quit_now=&quit_now,
4303
.password=&password,
4304
.filename=strdup("/nonexistent"),
4305
.cancelled_filenames=&cancelled_filenames,
4306
.current_time=¤t_time,
4307
.mandos_client_exited=&mandos_client_exited,
4308
.password_is_read=&password_is_read,
4310
task.func(task, queue);
4311
g_assert_false(quit_now);
4312
g_assert_true(queue->next_run == 0);
4313
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4315
g_assert_nonnull(find_matching_task(queue, (task_context){
4316
.func=read_inotify_event,
4319
.quit_now=&quit_now,
4320
.password=&password,
4321
.filename=task.filename,
4322
.cancelled_filenames=&cancelled_filenames,
4323
.current_time=¤t_time,
4324
.mandos_client_exited=&mandos_client_exited,
4325
.password_is_read=&password_is_read,
4328
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4329
EPOLLIN | EPOLLRDHUP));
4331
__attribute__((cleanup(cleanup_string)))
4332
char *filename = NULL;
4333
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4334
dummy_file_name), >, 0);
4335
g_assert_nonnull(filename);
4336
g_assert_true(string_set_contains(*task.cancelled_filenames,
4340
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4341
test_fixture *fixture,
4342
__attribute__((unused))
4345
__attribute__((cleanup(cleanup_close)))
4346
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4347
g_assert_cmpint(epoll_fd, >=, 0);
4348
__attribute__((cleanup(string_set_clear)))
4349
string_set cancelled_filenames = {};
4350
const mono_microsecs current_time = 0;
4353
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4355
/* "sufficient to read at least one event." - inotify(7) */
4356
const size_t ievent_max_size = (sizeof(struct inotify_event)
4358
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4360
struct inotify_event event;
4361
char name_buffer[NAME_MAX + 1];
4363
struct inotify_event *const ievent = &ievent_buffer.event;
4365
const char dummy_file_name[] = "ask.dummy_file_name";
4366
ievent->mask = IN_DELETE;
4367
ievent->len = sizeof(dummy_file_name);
4368
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4369
const size_t ievent_size = (sizeof(struct inotify_event)
4370
+ sizeof(dummy_file_name));
4371
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4373
g_assert_cmpint(close(pipefds[1]), ==, 0);
4375
bool quit_now = false;
4376
buffer password = {};
4377
bool mandos_client_exited = false;
4378
bool password_is_read = false;
4379
__attribute__((cleanup(cleanup_queue)))
4380
task_queue *queue = create_queue();
4381
g_assert_nonnull(queue);
4383
task_context task = {
4384
.func=read_inotify_event,
4387
.quit_now=&quit_now,
4388
.password=&password,
4389
.filename=strdup("/nonexistent"),
4390
.cancelled_filenames=&cancelled_filenames,
4391
.current_time=¤t_time,
4392
.mandos_client_exited=&mandos_client_exited,
4393
.password_is_read=&password_is_read,
4395
task.func(task, queue);
4396
g_assert_false(quit_now);
4397
g_assert_true(queue->next_run == 0);
4398
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4400
g_assert_nonnull(find_matching_task(queue, (task_context){
4401
.func=read_inotify_event,
4404
.quit_now=&quit_now,
4405
.password=&password,
4406
.filename=task.filename,
4407
.cancelled_filenames=&cancelled_filenames,
4408
.current_time=¤t_time,
4409
.mandos_client_exited=&mandos_client_exited,
4410
.password_is_read=&password_is_read,
4413
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4414
EPOLLIN | EPOLLRDHUP));
4416
__attribute__((cleanup(cleanup_string)))
4417
char *filename = NULL;
4418
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4419
dummy_file_name), >, 0);
4420
g_assert_nonnull(filename);
4421
g_assert_true(string_set_contains(*task.cancelled_filenames,
4426
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4427
test_fixture *fixture,
4428
__attribute__((unused))
4431
__attribute__((cleanup(cleanup_close)))
4432
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4433
g_assert_cmpint(epoll_fd, >=, 0);
4434
const mono_microsecs current_time = 0;
4437
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4439
/* "sufficient to read at least one event." - inotify(7) */
4440
const size_t ievent_max_size = (sizeof(struct inotify_event)
4442
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4444
struct inotify_event event;
4445
char name_buffer[NAME_MAX + 1];
4447
struct inotify_event *const ievent = &ievent_buffer.event;
4449
const char dummy_file_name[] = "ignored.dummy_file_name";
4450
ievent->mask = IN_CLOSE_WRITE;
4451
ievent->len = sizeof(dummy_file_name);
4452
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4453
const size_t ievent_size = (sizeof(struct inotify_event)
4454
+ sizeof(dummy_file_name));
4455
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4457
g_assert_cmpint(close(pipefds[1]), ==, 0);
4459
bool quit_now = false;
4460
buffer password = {};
4461
bool mandos_client_exited = false;
4462
bool password_is_read = false;
4463
__attribute__((cleanup(cleanup_queue)))
4464
task_queue *queue = create_queue();
4465
g_assert_nonnull(queue);
4467
task_context task = {
4468
.func=read_inotify_event,
4471
.quit_now=&quit_now,
4472
.password=&password,
4473
.filename=strdup("/nonexistent"),
4474
.cancelled_filenames = &(string_set){},
4476
.current_time=¤t_time,
4477
.mandos_client_exited=&mandos_client_exited,
4478
.password_is_read=&password_is_read,
4480
task.func(task, queue);
4481
g_assert_false(quit_now);
4482
g_assert_true(queue->next_run == 0);
4483
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4485
g_assert_nonnull(find_matching_task(queue, (task_context){
4486
.func=read_inotify_event,
4489
.quit_now=&quit_now,
4490
.password=&password,
4491
.filename=task.filename,
4492
.cancelled_filenames=task.cancelled_filenames,
4493
.current_time=¤t_time,
4494
.mandos_client_exited=&mandos_client_exited,
4495
.password_is_read=&password_is_read,
4498
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4499
EPOLLIN | EPOLLRDHUP));
4503
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4504
test_fixture *fixture,
4505
__attribute__((unused))
4506
gconstpointer user_data){
4507
__attribute__((cleanup(cleanup_close)))
4508
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4509
g_assert_cmpint(epoll_fd, >=, 0);
4510
const mono_microsecs current_time = 0;
4513
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4515
/* "sufficient to read at least one event." - inotify(7) */
4516
const size_t ievent_max_size = (sizeof(struct inotify_event)
4518
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4520
struct inotify_event event;
4521
char name_buffer[NAME_MAX + 1];
4523
struct inotify_event *const ievent = &ievent_buffer.event;
4525
const char dummy_file_name[] = "ignored.dummy_file_name";
4526
ievent->mask = IN_MOVED_TO;
4527
ievent->len = sizeof(dummy_file_name);
4528
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4529
const size_t ievent_size = (sizeof(struct inotify_event)
4530
+ sizeof(dummy_file_name));
4531
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4533
g_assert_cmpint(close(pipefds[1]), ==, 0);
4535
bool quit_now = false;
4536
buffer password = {};
4537
bool mandos_client_exited = false;
4538
bool password_is_read = false;
4539
__attribute__((cleanup(cleanup_queue)))
4540
task_queue *queue = create_queue();
4541
g_assert_nonnull(queue);
4543
task_context task = {
4544
.func=read_inotify_event,
4547
.quit_now=&quit_now,
4548
.password=&password,
4549
.filename=strdup("/nonexistent"),
4550
.cancelled_filenames = &(string_set){},
4552
.current_time=¤t_time,
4553
.mandos_client_exited=&mandos_client_exited,
4554
.password_is_read=&password_is_read,
4556
task.func(task, queue);
4557
g_assert_false(quit_now);
4558
g_assert_true(queue->next_run == 0);
4559
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4561
g_assert_nonnull(find_matching_task(queue, (task_context){
4562
.func=read_inotify_event,
4565
.quit_now=&quit_now,
4566
.password=&password,
4567
.filename=task.filename,
4568
.cancelled_filenames=task.cancelled_filenames,
4569
.current_time=¤t_time,
4570
.mandos_client_exited=&mandos_client_exited,
4571
.password_is_read=&password_is_read,
4574
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4575
EPOLLIN | EPOLLRDHUP));
4579
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4580
test_fixture *fixture,
4581
__attribute__((unused))
4584
__attribute__((cleanup(cleanup_close)))
4585
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4586
g_assert_cmpint(epoll_fd, >=, 0);
4587
__attribute__((cleanup(string_set_clear)))
4588
string_set cancelled_filenames = {};
4589
const mono_microsecs current_time = 0;
4592
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4594
/* "sufficient to read at least one event." - inotify(7) */
4595
const size_t ievent_max_size = (sizeof(struct inotify_event)
4597
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4599
struct inotify_event event;
4600
char name_buffer[NAME_MAX + 1];
4602
struct inotify_event *const ievent = &ievent_buffer.event;
4604
const char dummy_file_name[] = "ignored.dummy_file_name";
4605
ievent->mask = IN_MOVED_FROM;
4606
ievent->len = sizeof(dummy_file_name);
4607
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4608
const size_t ievent_size = (sizeof(struct inotify_event)
4609
+ sizeof(dummy_file_name));
4610
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4612
g_assert_cmpint(close(pipefds[1]), ==, 0);
4614
bool quit_now = false;
4615
buffer password = {};
4616
bool mandos_client_exited = false;
4617
bool password_is_read = false;
4618
__attribute__((cleanup(cleanup_queue)))
4619
task_queue *queue = create_queue();
4620
g_assert_nonnull(queue);
4622
task_context task = {
4623
.func=read_inotify_event,
4626
.quit_now=&quit_now,
4627
.password=&password,
4628
.filename=strdup("/nonexistent"),
4629
.cancelled_filenames=&cancelled_filenames,
4630
.current_time=¤t_time,
4631
.mandos_client_exited=&mandos_client_exited,
4632
.password_is_read=&password_is_read,
4634
task.func(task, queue);
4635
g_assert_false(quit_now);
4636
g_assert_true(queue->next_run == 0);
4637
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4639
g_assert_nonnull(find_matching_task(queue, (task_context){
4640
.func=read_inotify_event,
4643
.quit_now=&quit_now,
4644
.password=&password,
4645
.filename=task.filename,
4646
.cancelled_filenames=&cancelled_filenames,
4647
.current_time=¤t_time,
4648
.mandos_client_exited=&mandos_client_exited,
4649
.password_is_read=&password_is_read,
4652
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4653
EPOLLIN | EPOLLRDHUP));
4655
__attribute__((cleanup(cleanup_string)))
4656
char *filename = NULL;
4657
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4658
dummy_file_name), >, 0);
4659
g_assert_nonnull(filename);
4660
g_assert_false(string_set_contains(cancelled_filenames, filename));
4664
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4665
test_fixture *fixture,
4666
__attribute__((unused))
4669
__attribute__((cleanup(cleanup_close)))
4670
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4671
g_assert_cmpint(epoll_fd, >=, 0);
4672
__attribute__((cleanup(string_set_clear)))
4673
string_set cancelled_filenames = {};
4674
const mono_microsecs current_time = 0;
4677
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4679
/* "sufficient to read at least one event." - inotify(7) */
4680
const size_t ievent_max_size = (sizeof(struct inotify_event)
4682
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4684
struct inotify_event event;
4685
char name_buffer[NAME_MAX + 1];
4687
struct inotify_event *const ievent = &ievent_buffer.event;
4689
const char dummy_file_name[] = "ignored.dummy_file_name";
4690
ievent->mask = IN_DELETE;
4691
ievent->len = sizeof(dummy_file_name);
4692
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4693
const size_t ievent_size = (sizeof(struct inotify_event)
4694
+ sizeof(dummy_file_name));
4695
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4697
g_assert_cmpint(close(pipefds[1]), ==, 0);
4699
bool quit_now = false;
4700
buffer password = {};
4701
bool mandos_client_exited = false;
4702
bool password_is_read = false;
4703
__attribute__((cleanup(cleanup_queue)))
4704
task_queue *queue = create_queue();
4705
g_assert_nonnull(queue);
4707
task_context task = {
4708
.func=read_inotify_event,
4711
.quit_now=&quit_now,
4712
.password=&password,
4713
.filename=strdup("/nonexistent"),
4714
.cancelled_filenames=&cancelled_filenames,
4715
.current_time=¤t_time,
4716
.mandos_client_exited=&mandos_client_exited,
4717
.password_is_read=&password_is_read,
4719
task.func(task, queue);
4720
g_assert_false(quit_now);
4721
g_assert_true(queue->next_run == 0);
4722
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4724
g_assert_nonnull(find_matching_task(queue, (task_context){
4725
.func=read_inotify_event,
4728
.quit_now=&quit_now,
4729
.password=&password,
4730
.filename=task.filename,
4731
.cancelled_filenames=&cancelled_filenames,
4732
.current_time=¤t_time,
4733
.mandos_client_exited=&mandos_client_exited,
4734
.password_is_read=&password_is_read,
4737
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4738
EPOLLIN | EPOLLRDHUP));
4740
__attribute__((cleanup(cleanup_string)))
4741
char *filename = NULL;
4742
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4743
dummy_file_name), >, 0);
4744
g_assert_nonnull(filename);
4745
g_assert_false(string_set_contains(cancelled_filenames, filename));
4749
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4750
test_fixture *fixture,
4751
__attribute__((unused))
4752
gconstpointer user_data){
4753
__attribute__((cleanup(cleanup_close)))
4754
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4755
g_assert_cmpint(epoll_fd, >=, 0);
4756
__attribute__((cleanup(string_set_clear)))
4757
string_set cancelled_filenames = {};
4758
bool mandos_client_exited = false;
4759
bool password_is_read = false;
4760
__attribute__((cleanup(cleanup_queue)))
4761
task_queue *queue = create_queue();
4762
g_assert_nonnull(queue);
4764
char *const filename = strdup("/nonexistent");
4765
g_assert_nonnull(filename);
4766
task_context task = {
4767
.func=open_and_parse_question,
4768
.question_filename=filename,
4770
.password=(buffer[]){{}},
4772
.cancelled_filenames=&cancelled_filenames,
4773
.current_time=(mono_microsecs[]){0},
4774
.mandos_client_exited=&mandos_client_exited,
4775
.password_is_read=&password_is_read,
4777
task.func(task, queue);
4778
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4781
static void test_open_and_parse_question_EIO(__attribute__((unused))
4782
test_fixture *fixture,
4783
__attribute__((unused))
4784
gconstpointer user_data){
4785
__attribute__((cleanup(cleanup_close)))
4786
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4787
g_assert_cmpint(epoll_fd, >=, 0);
4788
__attribute__((cleanup(string_set_clear)))
4789
string_set cancelled_filenames = {};
4790
buffer password = {};
4791
bool mandos_client_exited = false;
4792
bool password_is_read = false;
4793
__attribute__((cleanup(cleanup_queue)))
4794
task_queue *queue = create_queue();
4795
g_assert_nonnull(queue);
4796
const mono_microsecs current_time = 0;
4798
char *filename = strdup("/proc/self/mem");
4799
task_context task = {
4800
.func=open_and_parse_question,
4801
.question_filename=filename,
4803
.password=&password,
4805
.cancelled_filenames=&cancelled_filenames,
4806
.current_time=¤t_time,
4807
.mandos_client_exited=&mandos_client_exited,
4808
.password_is_read=&password_is_read,
4810
run_task_with_stderr_to_dev_null(task, queue);
4811
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4815
test_open_and_parse_question_parse_error(__attribute__((unused))
4816
test_fixture *fixture,
4817
__attribute__((unused))
4818
gconstpointer user_data){
4819
__attribute__((cleanup(cleanup_close)))
4820
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4821
g_assert_cmpint(epoll_fd, >=, 0);
4822
__attribute__((cleanup(string_set_clear)))
4823
string_set cancelled_filenames = {};
4824
__attribute__((cleanup(cleanup_queue)))
4825
task_queue *queue = create_queue();
4826
g_assert_nonnull(queue);
4828
__attribute__((cleanup(cleanup_string)))
4829
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4830
g_assert_nonnull(tempfilename);
4831
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4832
g_assert_cmpint(tempfile, >, 0);
4833
const char bad_data[] = "this is bad syntax\n";
4834
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4835
==, sizeof(bad_data));
4836
g_assert_cmpint(close(tempfile), ==, 0);
4838
char *const filename = strdup(tempfilename);
4839
g_assert_nonnull(filename);
4840
task_context task = {
4841
.func=open_and_parse_question,
4842
.question_filename=filename,
4844
.password=(buffer[]){{}},
4846
.cancelled_filenames=&cancelled_filenames,
4847
.current_time=(mono_microsecs[]){0},
4848
.mandos_client_exited=(bool[]){false},
4849
.password_is_read=(bool[]){false},
4851
run_task_with_stderr_to_dev_null(task, queue);
4853
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4855
g_assert_cmpint(unlink(tempfilename), ==, 0);
4859
void test_open_and_parse_question_nosocket(__attribute__((unused))
4860
test_fixture *fixture,
4861
__attribute__((unused))
4862
gconstpointer user_data){
4863
__attribute__((cleanup(cleanup_close)))
4864
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4865
g_assert_cmpint(epoll_fd, >=, 0);
4866
__attribute__((cleanup(string_set_clear)))
4867
string_set cancelled_filenames = {};
4868
__attribute__((cleanup(cleanup_queue)))
4869
task_queue *queue = create_queue();
4870
g_assert_nonnull(queue);
4872
__attribute__((cleanup(cleanup_string)))
4873
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4874
g_assert_nonnull(tempfilename);
4875
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4876
g_assert_cmpint(questionfile, >, 0);
4877
FILE *qf = fdopen(questionfile, "w");
4878
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4879
g_assert_cmpint(fclose(qf), ==, 0);
4881
char *const filename = strdup(tempfilename);
4882
g_assert_nonnull(filename);
4883
task_context task = {
4884
.func=open_and_parse_question,
4885
.question_filename=filename,
4887
.password=(buffer[]){{}},
4889
.cancelled_filenames=&cancelled_filenames,
4890
.current_time=(mono_microsecs[]){0},
4891
.mandos_client_exited=(bool[]){false},
4892
.password_is_read=(bool[]){false},
4894
run_task_with_stderr_to_dev_null(task, queue);
4895
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4897
g_assert_cmpint(unlink(tempfilename), ==, 0);
4901
void test_open_and_parse_question_badsocket(__attribute__((unused))
4902
test_fixture *fixture,
4903
__attribute__((unused))
4904
gconstpointer user_data){
4905
__attribute__((cleanup(cleanup_close)))
4906
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4907
g_assert_cmpint(epoll_fd, >=, 0);
4908
__attribute__((cleanup(string_set_clear)))
4909
string_set cancelled_filenames = {};
4910
__attribute__((cleanup(cleanup_queue)))
4911
task_queue *queue = create_queue();
4912
g_assert_nonnull(queue);
4914
__attribute__((cleanup(cleanup_string)))
4915
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4916
g_assert_nonnull(tempfilename);
4917
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4918
g_assert_cmpint(questionfile, >, 0);
4919
FILE *qf = fdopen(questionfile, "w");
4920
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4921
g_assert_cmpint(fclose(qf), ==, 0);
4923
char *const filename = strdup(tempfilename);
4924
g_assert_nonnull(filename);
4925
task_context task = {
4926
.func=open_and_parse_question,
4927
.question_filename=filename,
4929
.password=(buffer[]){{}},
4931
.cancelled_filenames=&cancelled_filenames,
4932
.current_time=(mono_microsecs[]){0},
4933
.mandos_client_exited=(bool[]){false},
4934
.password_is_read=(bool[]){false},
4936
run_task_with_stderr_to_dev_null(task, queue);
4937
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4939
g_assert_cmpint(unlink(tempfilename), ==, 0);
4943
void test_open_and_parse_question_nopid(__attribute__((unused))
4944
test_fixture *fixture,
4945
__attribute__((unused))
4946
gconstpointer user_data){
4947
__attribute__((cleanup(cleanup_close)))
4948
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4949
g_assert_cmpint(epoll_fd, >=, 0);
4950
__attribute__((cleanup(string_set_clear)))
4951
string_set cancelled_filenames = {};
4952
__attribute__((cleanup(cleanup_queue)))
4953
task_queue *queue = create_queue();
4954
g_assert_nonnull(queue);
4956
__attribute__((cleanup(cleanup_string)))
4957
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4958
g_assert_nonnull(tempfilename);
4959
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4960
g_assert_cmpint(questionfile, >, 0);
4961
FILE *qf = fdopen(questionfile, "w");
4962
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
4963
g_assert_cmpint(fclose(qf), ==, 0);
4965
char *const filename = strdup(tempfilename);
4966
g_assert_nonnull(filename);
4967
task_context task = {
4968
.func=open_and_parse_question,
4969
.question_filename=filename,
4971
.password=(buffer[]){{}},
4973
.cancelled_filenames=&cancelled_filenames,
4974
.current_time=(mono_microsecs[]){0},
4975
.mandos_client_exited=(bool[]){false},
4976
.password_is_read=(bool[]){false},
4978
run_task_with_stderr_to_dev_null(task, queue);
4979
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4981
g_assert_cmpint(unlink(tempfilename), ==, 0);
4985
void test_open_and_parse_question_badpid(__attribute__((unused))
4986
test_fixture *fixture,
4987
__attribute__((unused))
4988
gconstpointer user_data){
4989
__attribute__((cleanup(cleanup_close)))
4990
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4991
g_assert_cmpint(epoll_fd, >=, 0);
4992
__attribute__((cleanup(string_set_clear)))
4993
string_set cancelled_filenames = {};
4994
__attribute__((cleanup(cleanup_queue)))
4995
task_queue *queue = create_queue();
4996
g_assert_nonnull(queue);
4998
__attribute__((cleanup(cleanup_string)))
4999
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5000
g_assert_nonnull(tempfilename);
5001
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5002
g_assert_cmpint(questionfile, >, 0);
5003
FILE *qf = fdopen(questionfile, "w");
5004
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5006
g_assert_cmpint(fclose(qf), ==, 0);
5008
char *const filename = strdup(tempfilename);
5009
g_assert_nonnull(filename);
5010
task_context task = {
5011
.func=open_and_parse_question,
5012
.question_filename=filename,
5014
.password=(buffer[]){{}},
5016
.cancelled_filenames=&cancelled_filenames,
5017
.current_time=(mono_microsecs[]){0},
5018
.mandos_client_exited=(bool[]){false},
5019
.password_is_read=(bool[]){false},
5021
run_task_with_stderr_to_dev_null(task, queue);
5022
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5024
g_assert_cmpint(unlink(tempfilename), ==, 0);
5028
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5029
test_fixture *fixture,
5030
__attribute__((unused))
5031
gconstpointer user_data){
5032
__attribute__((cleanup(cleanup_close)))
5033
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5034
g_assert_cmpint(epoll_fd, >=, 0);
5035
__attribute__((cleanup(string_set_clear)))
5036
string_set cancelled_filenames = {};
5037
buffer password = {};
5038
bool mandos_client_exited = false;
5039
bool password_is_read = false;
5040
__attribute__((cleanup(cleanup_queue)))
5041
task_queue *queue = create_queue();
5042
g_assert_nonnull(queue);
5043
const mono_microsecs current_time = 0;
5045
/* Find value of sysctl kernel.pid_max */
5046
uintmax_t pid_max = 0;
5047
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5048
g_assert_nonnull(sysctl_pid_max);
5049
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5051
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5053
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5054
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5056
__attribute__((cleanup(cleanup_string)))
5057
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5058
g_assert_nonnull(tempfilename);
5059
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5060
g_assert_cmpint(questionfile, >, 0);
5061
FILE *qf = fdopen(questionfile, "w");
5062
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5063
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5065
g_assert_cmpint(fclose(qf), ==, 0);
5067
char *const question_filename = strdup(tempfilename);
5068
g_assert_nonnull(question_filename);
5069
task_context task = {
5070
.func=open_and_parse_question,
5071
.question_filename=question_filename,
5073
.password=&password,
5074
.filename=question_filename,
5075
.cancelled_filenames=&cancelled_filenames,
5076
.current_time=¤t_time,
5077
.mandos_client_exited=&mandos_client_exited,
5078
.password_is_read=&password_is_read,
5080
run_task_with_stderr_to_dev_null(task, queue);
5081
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5083
g_assert_cmpint(unlink(tempfilename), ==, 0);
5087
test_open_and_parse_question_no_notafter(__attribute__((unused))
5088
test_fixture *fixture,
5089
__attribute__((unused))
5090
gconstpointer user_data){
5091
__attribute__((cleanup(cleanup_close)))
5092
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5093
g_assert_cmpint(epoll_fd, >=, 0);
5094
__attribute__((cleanup(string_set_clear)))
5095
string_set cancelled_filenames = {};
5096
buffer password = {};
5097
bool mandos_client_exited = false;
5098
bool password_is_read = false;
5099
__attribute__((cleanup(cleanup_queue)))
5100
task_queue *queue = create_queue();
5101
g_assert_nonnull(queue);
5102
const mono_microsecs current_time = 0;
5104
__attribute__((cleanup(cleanup_string)))
5105
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5106
g_assert_nonnull(tempfilename);
5107
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5108
g_assert_cmpint(questionfile, >, 0);
5109
FILE *qf = fdopen(questionfile, "w");
5110
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5111
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5112
g_assert_cmpint(fclose(qf), ==, 0);
5114
char *const filename = strdup(tempfilename);
5115
g_assert_nonnull(filename);
5116
task_context task = {
5117
.func=open_and_parse_question,
5118
.question_filename=filename,
5120
.password=&password,
5122
.cancelled_filenames=&cancelled_filenames,
5123
.current_time=¤t_time,
5124
.mandos_client_exited=&mandos_client_exited,
5125
.password_is_read=&password_is_read,
5127
task.func(task, queue);
5128
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5130
__attribute__((cleanup(cleanup_string)))
5131
char *socket_filename = strdup("/nonexistent");
5132
g_assert_nonnull(socket_filename);
5133
g_assert_nonnull(find_matching_task(queue, (task_context){
5134
.func=connect_question_socket,
5135
.question_filename=tempfilename,
5136
.filename=socket_filename,
5138
.password=&password,
5139
.current_time=¤t_time,
5140
.mandos_client_exited=&mandos_client_exited,
5141
.password_is_read=&password_is_read,
5144
g_assert_true(queue->next_run != 0);
5146
g_assert_cmpint(unlink(tempfilename), ==, 0);
5150
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5151
test_fixture *fixture,
5152
__attribute__((unused))
5153
gconstpointer user_data){
5154
__attribute__((cleanup(cleanup_close)))
5155
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5156
g_assert_cmpint(epoll_fd, >=, 0);
5157
__attribute__((cleanup(string_set_clear)))
5158
string_set cancelled_filenames = {};
5159
buffer password = {};
5160
bool mandos_client_exited = false;
5161
bool password_is_read = false;
5162
__attribute__((cleanup(cleanup_queue)))
5163
task_queue *queue = create_queue();
5164
g_assert_nonnull(queue);
5165
const mono_microsecs current_time = 0;
5167
__attribute__((cleanup(cleanup_string)))
5168
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5169
g_assert_nonnull(tempfilename);
5170
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5171
g_assert_cmpint(questionfile, >, 0);
5172
FILE *qf = fdopen(questionfile, "w");
5173
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5174
PRIuMAX "\nNotAfter=\n",
5175
(uintmax_t)getpid()), >, 0);
5176
g_assert_cmpint(fclose(qf), ==, 0);
5178
char *const filename = strdup(tempfilename);
5179
g_assert_nonnull(filename);
5180
task_context task = {
5181
.func=open_and_parse_question,
5182
.question_filename=filename,
5184
.password=&password,
5186
.cancelled_filenames=&cancelled_filenames,
5187
.current_time=¤t_time,
5188
.mandos_client_exited=&mandos_client_exited,
5189
.password_is_read=&password_is_read,
5191
run_task_with_stderr_to_dev_null(task, queue);
5192
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5194
__attribute__((cleanup(cleanup_string)))
5195
char *socket_filename = strdup("/nonexistent");
5196
g_assert_nonnull(find_matching_task(queue, (task_context){
5197
.func=connect_question_socket,
5198
.question_filename=tempfilename,
5199
.filename=socket_filename,
5201
.password=&password,
5202
.current_time=¤t_time,
5203
.mandos_client_exited=&mandos_client_exited,
5204
.password_is_read=&password_is_read,
5206
g_assert_true(queue->next_run != 0);
5208
g_assert_cmpint(unlink(tempfilename), ==, 0);
5212
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5214
const mono_microsecs
5216
const mono_microsecs
5218
__attribute__((cleanup(cleanup_close)))
5219
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5220
g_assert_cmpint(epoll_fd, >=, 0);
5221
__attribute__((cleanup(string_set_clear)))
5222
string_set cancelled_filenames = {};
5223
buffer password = {};
5224
bool mandos_client_exited = false;
5225
bool password_is_read = false;
5226
__attribute__((cleanup(cleanup_queue)))
5227
task_queue *queue = create_queue();
5228
g_assert_nonnull(queue);
5229
queue->next_run = next_queue_run;
5231
__attribute__((cleanup(cleanup_string)))
5232
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5233
g_assert_nonnull(tempfilename);
5234
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5235
g_assert_cmpint(questionfile, >, 0);
5236
FILE *qf = fdopen(questionfile, "w");
5237
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5238
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5239
(uintmax_t)getpid(), notafter), >, 0);
5240
g_assert_cmpint(fclose(qf), ==, 0);
5242
char *const filename = strdup(tempfilename);
5243
g_assert_nonnull(filename);
5244
task_context task = {
5245
.func=open_and_parse_question,
5246
.question_filename=filename,
5248
.password=&password,
5250
.cancelled_filenames=&cancelled_filenames,
5251
.current_time=¤t_time,
5252
.mandos_client_exited=&mandos_client_exited,
5253
.password_is_read=&password_is_read,
5255
task.func(task, queue);
5257
if(queue->length >= 1){
5258
__attribute__((cleanup(cleanup_string)))
5259
char *socket_filename = strdup("/nonexistent");
5260
g_assert_nonnull(find_matching_task(queue, (task_context){
5261
.func=connect_question_socket,
5262
.filename=socket_filename,
5264
.password=&password,
5265
.current_time=¤t_time,
5266
.cancelled_filenames=&cancelled_filenames,
5267
.mandos_client_exited=&mandos_client_exited,
5268
.password_is_read=&password_is_read,
5270
g_assert_true(queue->next_run != 0);
5274
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5275
} else if(current_time >= notafter) {
5276
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5278
g_assert_nonnull(find_matching_task(queue, (task_context){
5279
.func=cancel_old_question,
5280
.question_filename=tempfilename,
5281
.filename=tempfilename,
5283
.cancelled_filenames=&cancelled_filenames,
5284
.current_time=¤t_time,
5287
g_assert_true(queue->next_run == 1);
5289
g_assert_cmpint(unlink(tempfilename), ==, 0);
5293
test_open_and_parse_question_notafter_0(__attribute__((unused))
5294
test_fixture *fixture,
5295
__attribute__((unused))
5296
gconstpointer user_data){
5297
/* current_time, notafter, next_queue_run */
5298
assert_open_and_parse_question_with_notafter(0, 0, 0);
5302
test_open_and_parse_question_notafter_1(__attribute__((unused))
5303
test_fixture *fixture,
5304
__attribute__((unused))
5305
gconstpointer user_data){
5306
/* current_time, notafter, next_queue_run */
5307
assert_open_and_parse_question_with_notafter(0, 1, 0);
5311
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5312
test_fixture *fixture,
5313
__attribute__((unused))
5314
gconstpointer user_data){
5315
/* current_time, notafter, next_queue_run */
5316
assert_open_and_parse_question_with_notafter(0, 1, 1);
5320
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5321
test_fixture *fixture,
5322
__attribute__((unused))
5323
gconstpointer user_data){
5324
/* current_time, notafter, next_queue_run */
5325
assert_open_and_parse_question_with_notafter(0, 1, 2);
5329
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5330
test_fixture *fixture,
5331
__attribute__((unused))
5332
gconstpointer user_data){
5333
/* current_time, notafter, next_queue_run */
5334
assert_open_and_parse_question_with_notafter(1, 1, 0);
5338
test_open_and_parse_question_late_notafter(__attribute__((unused))
5339
test_fixture *fixture,
5340
__attribute__((unused))
5341
gconstpointer user_data){
5342
/* current_time, notafter, next_queue_run */
5343
assert_open_and_parse_question_with_notafter(2, 1, 0);
5346
static void assert_cancel_old_question_param(const mono_microsecs
5348
const mono_microsecs
5350
const mono_microsecs
5352
const mono_microsecs
5354
__attribute__((cleanup(string_set_clear)))
5355
string_set cancelled_filenames = {};
5356
__attribute__((cleanup(cleanup_queue)))
5357
task_queue *queue = create_queue();
5358
g_assert_nonnull(queue);
5359
queue->next_run = next_queue_run;
5361
char *const question_filename = strdup("/nonexistent");
5362
g_assert_nonnull(question_filename);
5363
task_context task = {
5364
.func=cancel_old_question,
5365
.question_filename=question_filename,
5366
.filename=question_filename,
5368
.cancelled_filenames=&cancelled_filenames,
5369
.current_time=¤t_time,
5371
task.func(task, queue);
5373
if(current_time >= notafter){
5374
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5375
g_assert_true(string_set_contains(cancelled_filenames,
5378
g_assert_nonnull(find_matching_task(queue, (task_context){
5379
.func=cancel_old_question,
5380
.question_filename=question_filename,
5381
.filename=question_filename,
5383
.cancelled_filenames=&cancelled_filenames,
5384
.current_time=¤t_time,
5387
g_assert_false(string_set_contains(cancelled_filenames,
5388
question_filename));
5390
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5391
(unsigned int)next_set_to);
5394
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5395
test_fixture *fixture,
5396
__attribute__((unused))
5397
gconstpointer user_data){
5398
/* next_queue_run unset,
5399
cancellation should happen because time has come,
5400
next_queue_run should be unchanged */
5401
/* next_queue_run, notafter, current_time, next_set_to */
5402
assert_cancel_old_question_param(0, 1, 2, 0);
5405
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5406
test_fixture *fixture,
5407
__attribute__((unused))
5408
gconstpointer user_data){
5409
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5410
and current_time is not yet notafter or greater,
5411
update value of next_queue_run to value of notafter */
5412
/* next_queue_run, notafter, current_time, next_set_to */
5413
assert_cancel_old_question_param(0, 2, 1, 2);
5416
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5417
test_fixture *fixture,
5418
__attribute__((unused))
5419
gconstpointer user_data){
5420
/* next_queue_run 1,
5421
cancellation should happen because time has come,
5422
next_queue_run should be unchanged */
5423
/* next_queue_run, notafter, current_time, next_set_to */
5424
assert_cancel_old_question_param(1, 2, 3, 1);
5427
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5428
test_fixture *fixture,
5429
__attribute__((unused))
5430
gconstpointer user_data){
5431
/* If next_queue_run is set,
5432
and current_time is not yet notafter or greater,
5433
and notafter is larger than next_queue_run
5434
next_queue_run should be unchanged */
5435
/* next_queue_run, notafter, current_time, next_set_to */
5436
assert_cancel_old_question_param(1, 3, 2, 1);
5439
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5440
test_fixture *fixture,
5441
__attribute__((unused))
5442
gconstpointer user_data){
5443
/* next_queue_run 2,
5444
cancellation should happen because time has come,
5445
next_queue_run should be unchanged */
5446
/* next_queue_run, notafter, current_time, next_set_to */
5447
assert_cancel_old_question_param(2, 1, 3, 2);
5450
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5451
test_fixture *fixture,
5452
__attribute__((unused))
5453
gconstpointer user_data){
5454
/* If next_queue_run is set,
5455
and current_time is not yet notafter or greater,
5456
and notafter is larger than next_queue_run
5457
next_queue_run should be unchanged */
5458
/* next_queue_run, notafter, current_time, next_set_to */
5459
assert_cancel_old_question_param(2, 3, 1, 2);
5462
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5463
test_fixture *fixture,
5464
__attribute__((unused))
5465
gconstpointer user_data){
5466
/* next_queue_run 3,
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(3, 1, 2, 3);
5473
static void test_cancel_old_question_3_2_1(__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 smaller than next_queue_run
5480
update value of next_queue_run to value of notafter */
5481
/* next_queue_run, notafter, current_time, next_set_to */
5482
assert_cancel_old_question_param(3, 2, 1, 2);
5486
test_connect_question_socket_name_too_long(__attribute__((unused))
5487
test_fixture *fixture,
5488
__attribute__((unused))
5489
gconstpointer user_data){
5490
__attribute__((cleanup(cleanup_close)))
5491
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5492
g_assert_cmpint(epoll_fd, >=, 0);
5493
const char question_filename[] = "/nonexistent/question";
5494
__attribute__((cleanup(string_set_clear)))
5495
string_set cancelled_filenames = {};
5496
__attribute__((cleanup(cleanup_queue)))
5497
task_queue *queue = create_queue();
5498
g_assert_nonnull(queue);
5499
__attribute__((cleanup(cleanup_string)))
5500
char *tempdir = make_temporary_directory();
5501
g_assert_nonnull(tempdir);
5502
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5503
char socket_name[sizeof(unix_socket.sun_path)];
5504
memset(socket_name, 'x', sizeof(socket_name));
5505
socket_name[sizeof(socket_name)-1] = '\0';
5506
char *filename = NULL;
5507
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5509
g_assert_nonnull(filename);
5511
task_context task = {
5512
.func=connect_question_socket,
5513
.question_filename=strdup(question_filename),
5515
.password=(buffer[]){{}},
5517
.cancelled_filenames=&cancelled_filenames,
5518
.mandos_client_exited=(bool[]){false},
5519
.password_is_read=(bool[]){false},
5520
.current_time=(mono_microsecs[]){0},
5522
g_assert_nonnull(task.question_filename);
5523
run_task_with_stderr_to_dev_null(task, queue);
5525
g_assert_true(string_set_contains(cancelled_filenames,
5526
question_filename));
5527
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5528
g_assert_true(queue->next_run == 0);
5530
g_assert_cmpint(rmdir(tempdir), ==, 0);
5534
void test_connect_question_socket_connect_fail(__attribute__((unused))
5535
test_fixture *fixture,
5536
__attribute__((unused))
5539
__attribute__((cleanup(cleanup_close)))
5540
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5541
g_assert_cmpint(epoll_fd, >=, 0);
5542
const char question_filename[] = "/nonexistent/question";
5543
__attribute__((cleanup(string_set_clear)))
5544
string_set cancelled_filenames = {};
5545
const mono_microsecs current_time = 3;
5546
__attribute__((cleanup(cleanup_queue)))
5547
task_queue *queue = create_queue();
5548
g_assert_nonnull(queue);
5549
__attribute__((cleanup(cleanup_string)))
5550
char *tempdir = make_temporary_directory();
5551
g_assert_nonnull(tempdir);
5552
char socket_name[] = "nonexistent";
5553
char *filename = NULL;
5554
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5556
g_assert_nonnull(filename);
5558
task_context task = {
5559
.func=connect_question_socket,
5560
.question_filename=strdup(question_filename),
5562
.password=(buffer[]){{}},
5564
.cancelled_filenames=&cancelled_filenames,
5565
.mandos_client_exited=(bool[]){false},
5566
.password_is_read=(bool[]){false},
5567
.current_time=¤t_time,
5569
g_assert_nonnull(task.question_filename);
5570
run_task_with_stderr_to_dev_null(task, queue);
5572
g_assert_nonnull(find_matching_task(queue, task));
5574
g_assert_false(string_set_contains(cancelled_filenames,
5575
question_filename));
5576
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5577
g_assert_true(queue->next_run == 1000000 + current_time);
5579
g_assert_cmpint(rmdir(tempdir), ==, 0);
5583
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5584
test_fixture *fixture,
5585
__attribute__((unused))
5586
gconstpointer user_data){
5587
__attribute__((cleanup(cleanup_close)))
5588
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5589
__attribute__((cleanup(cleanup_string)))
5590
char *const question_filename = strdup("/nonexistent/question");
5591
g_assert_nonnull(question_filename);
5592
__attribute__((cleanup(string_set_clear)))
5593
string_set cancelled_filenames = {};
5594
const mono_microsecs current_time = 5;
5595
__attribute__((cleanup(cleanup_queue)))
5596
task_queue *queue = create_queue();
5597
g_assert_nonnull(queue);
5598
__attribute__((cleanup(cleanup_string)))
5599
char *tempdir = make_temporary_directory();
5600
g_assert_nonnull(tempdir);
5601
__attribute__((cleanup(cleanup_close)))
5602
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5603
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5604
g_assert_cmpint(sock_fd, >=, 0);
5605
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5606
const char socket_name[] = "socket_name";
5607
__attribute__((cleanup(cleanup_string)))
5608
char *filename = NULL;
5609
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5611
g_assert_nonnull(filename);
5612
g_assert_cmpint((int)strlen(filename), <,
5613
(int)sizeof(sock_name.sun_path));
5614
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5615
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5616
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5617
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5618
task_context task = {
5619
.func=connect_question_socket,
5620
.question_filename=strdup(question_filename),
5622
.password=(buffer[]){{}},
5623
.filename=strdup(filename),
5624
.cancelled_filenames=&cancelled_filenames,
5625
.mandos_client_exited=(bool[]){false},
5626
.password_is_read=(bool[]){false},
5627
.current_time=¤t_time,
5629
g_assert_nonnull(task.question_filename);
5630
run_task_with_stderr_to_dev_null(task, queue);
5632
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5633
const task_context *const added_task
5634
= find_matching_task(queue, task);
5635
g_assert_nonnull(added_task);
5636
g_assert_true(queue->next_run == 1000000 + current_time);
5638
g_assert_cmpint(unlink(filename), ==, 0);
5639
g_assert_cmpint(rmdir(tempdir), ==, 0);
5643
void test_connect_question_socket_usable(__attribute__((unused))
5644
test_fixture *fixture,
5645
__attribute__((unused))
5646
gconstpointer user_data){
5647
__attribute__((cleanup(cleanup_close)))
5648
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5649
g_assert_cmpint(epoll_fd, >=, 0);
5650
__attribute__((cleanup(cleanup_string)))
5651
char *const question_filename = strdup("/nonexistent/question");
5652
g_assert_nonnull(question_filename);
5653
__attribute__((cleanup(string_set_clear)))
5654
string_set cancelled_filenames = {};
5655
buffer password = {};
5656
bool mandos_client_exited = false;
5657
bool password_is_read = false;
5658
const mono_microsecs current_time = 0;
5659
__attribute__((cleanup(cleanup_queue)))
5660
task_queue *queue = create_queue();
5661
g_assert_nonnull(queue);
5662
__attribute__((cleanup(cleanup_string)))
5663
char *tempdir = make_temporary_directory();
5664
g_assert_nonnull(tempdir);
5665
__attribute__((cleanup(cleanup_close)))
5666
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5667
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5668
g_assert_cmpint(sock_fd, >=, 0);
5669
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5670
const char socket_name[] = "socket_name";
5671
__attribute__((cleanup(cleanup_string)))
5672
char *filename = NULL;
5673
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5675
g_assert_nonnull(filename);
5676
g_assert_cmpint((int)strlen(filename), <,
5677
(int)sizeof(sock_name.sun_path));
5678
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5679
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5680
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5681
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5682
task_context task = {
5683
.func=connect_question_socket,
5684
.question_filename=strdup(question_filename),
5686
.password=&password,
5687
.filename=strdup(filename),
5688
.cancelled_filenames=&cancelled_filenames,
5689
.mandos_client_exited=&mandos_client_exited,
5690
.password_is_read=&password_is_read,
5691
.current_time=¤t_time,
5693
g_assert_nonnull(task.question_filename);
5694
task.func(task, queue);
5696
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5697
const task_context *const added_task
5698
= find_matching_task(queue, (task_context){
5699
.func=send_password_to_socket,
5700
.question_filename=question_filename,
5703
.password=&password,
5704
.cancelled_filenames=&cancelled_filenames,
5705
.mandos_client_exited=&mandos_client_exited,
5706
.password_is_read=&password_is_read,
5707
.current_time=¤t_time,
5709
g_assert_nonnull(added_task);
5710
g_assert_cmpint(added_task->fd, >, 0);
5712
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5715
const int fd = added_task->fd;
5716
g_assert_cmpint(fd, >, 0);
5717
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5720
char write_data[PIPE_BUF];
5722
/* Construct test password buffer */
5723
/* Start with + since that is what the real procotol uses */
5724
write_data[0] = '+';
5725
/* Set a special character at string end just to mark the end */
5726
write_data[sizeof(write_data)-2] = 'y';
5727
/* Set NUL at buffer end, as suggested by the protocol */
5728
write_data[sizeof(write_data)-1] = '\0';
5729
/* Fill rest of password with 'x' */
5730
memset(write_data+1, 'x', sizeof(write_data)-3);
5731
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5732
MSG_NOSIGNAL), ==, sizeof(write_data));
5735
/* read from sock_fd */
5736
char read_data[sizeof(write_data)];
5737
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5738
==, sizeof(read_data));
5740
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5743
/* writing to sock_fd should fail */
5744
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5745
MSG_NOSIGNAL), <, 0);
5747
/* reading from fd should fail */
5748
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5749
MSG_NOSIGNAL), <, 0);
5751
g_assert_cmpint(unlink(filename), ==, 0);
5752
g_assert_cmpint(rmdir(tempdir), ==, 0);
5756
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5757
test_fixture *fixture,
5758
__attribute__((unused))
5761
__attribute__((cleanup(cleanup_close)))
5762
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5763
g_assert_cmpint(epoll_fd, >=, 0);
5764
__attribute__((cleanup(cleanup_string)))
5765
char *const question_filename = strdup("/nonexistent/question");
5766
g_assert_nonnull(question_filename);
5767
__attribute__((cleanup(cleanup_string)))
5768
char *const filename = strdup("/nonexistent/socket");
5769
g_assert_nonnull(filename);
5770
__attribute__((cleanup(string_set_clear)))
5771
string_set cancelled_filenames = {};
5772
buffer password = {};
5773
bool password_is_read = true;
5774
__attribute__((cleanup(cleanup_queue)))
5775
task_queue *queue = create_queue();
5776
g_assert_nonnull(queue);
5778
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5779
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5781
__attribute__((cleanup(cleanup_close)))
5782
const int read_socket = socketfds[0];
5783
const int write_socket = socketfds[1];
5784
task_context task = {
5785
.func=send_password_to_socket,
5786
.question_filename=strdup(question_filename),
5787
.filename=strdup(filename),
5790
.password=&password,
5791
.cancelled_filenames=&cancelled_filenames,
5792
.mandos_client_exited=(bool[]){false},
5793
.password_is_read=&password_is_read,
5794
.current_time=(mono_microsecs[]){0},
5796
g_assert_nonnull(task.question_filename);
5798
task.func(task, queue);
5800
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5802
const task_context *const added_task
5803
= find_matching_task(queue, task);
5804
g_assert_nonnull(added_task);
5805
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5806
g_assert_true(password_is_read);
5808
g_assert_cmpint(added_task->fd, >, 0);
5809
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5814
test_send_password_to_socket_password_not_read(__attribute__((unused))
5815
test_fixture *fixture,
5816
__attribute__((unused))
5819
__attribute__((cleanup(cleanup_close)))
5820
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5821
g_assert_cmpint(epoll_fd, >=, 0);
5822
__attribute__((cleanup(cleanup_string)))
5823
char *const question_filename = strdup("/nonexistent/question");
5824
g_assert_nonnull(question_filename);
5825
__attribute__((cleanup(cleanup_string)))
5826
char *const filename = strdup("/nonexistent/socket");
5827
__attribute__((cleanup(string_set_clear)))
5828
string_set cancelled_filenames = {};
5829
buffer password = {};
5830
__attribute__((cleanup(cleanup_queue)))
5831
task_queue *queue = create_queue();
5832
g_assert_nonnull(queue);
5834
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5835
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5837
__attribute__((cleanup(cleanup_close)))
5838
const int read_socket = socketfds[0];
5839
const int write_socket = socketfds[1];
5840
task_context task = {
5841
.func=send_password_to_socket,
5842
.question_filename=strdup(question_filename),
5843
.filename=strdup(filename),
5846
.password=&password,
5847
.cancelled_filenames=&cancelled_filenames,
5848
.mandos_client_exited=(bool[]){false},
5849
.password_is_read=(bool[]){false},
5850
.current_time=(mono_microsecs[]){0},
5852
g_assert_nonnull(task.question_filename);
5854
task.func(task, queue);
5856
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5858
const task_context *const added_task = find_matching_task(queue,
5860
g_assert_nonnull(added_task);
5861
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5862
g_assert_true(queue->next_run == 0);
5864
g_assert_cmpint(added_task->fd, >, 0);
5865
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5870
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5871
test_fixture *fixture,
5872
__attribute__((unused))
5873
gconstpointer user_data){
5874
__attribute__((cleanup(cleanup_close)))
5875
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5876
g_assert_cmpint(epoll_fd, >=, 0);
5877
const char question_filename[] = "/nonexistent/question";
5878
char *const filename = strdup("/nonexistent/socket");
5879
__attribute__((cleanup(string_set_clear)))
5880
string_set cancelled_filenames = {};
5881
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5882
__attribute__((cleanup(cleanup_buffer)))
5884
.data=malloc(oversized),
5886
.allocated=oversized,
5888
g_assert_nonnull(password.data);
5889
if(mlock(password.data, password.allocated) != 0){
5890
g_assert_true(errno == EPERM or errno == ENOMEM);
5892
/* Construct test password buffer */
5893
/* Start with + since that is what the real procotol uses */
5894
password.data[0] = '+';
5895
/* Set a special character at string end just to mark the end */
5896
password.data[oversized-3] = 'y';
5897
/* Set NUL at buffer end, as suggested by the protocol */
5898
password.data[oversized-2] = '\0';
5899
/* Fill rest of password with 'x' */
5900
memset(password.data+1, 'x', oversized-3);
5902
__attribute__((cleanup(cleanup_queue)))
5903
task_queue *queue = create_queue();
5904
g_assert_nonnull(queue);
5906
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5907
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5909
__attribute__((cleanup(cleanup_close)))
5910
const int read_socket = socketfds[0];
5911
__attribute__((cleanup(cleanup_close)))
5912
const int write_socket = socketfds[1];
5913
task_context task = {
5914
.func=send_password_to_socket,
5915
.question_filename=strdup(question_filename),
5919
.password=&password,
5920
.cancelled_filenames=&cancelled_filenames,
5921
.mandos_client_exited=(bool[]){true},
5922
.password_is_read=(bool[]){true},
5923
.current_time=(mono_microsecs[]){0},
5925
g_assert_nonnull(task.question_filename);
5927
run_task_with_stderr_to_dev_null(task, queue);
5929
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5930
g_assert_true(string_set_contains(cancelled_filenames,
5931
question_filename));
5934
static void test_send_password_to_socket_retry(__attribute__((unused))
5935
test_fixture *fixture,
5936
__attribute__((unused))
5939
__attribute__((cleanup(cleanup_close)))
5940
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5941
g_assert_cmpint(epoll_fd, >=, 0);
5942
__attribute__((cleanup(cleanup_string)))
5943
char *const question_filename = strdup("/nonexistent/question");
5944
g_assert_nonnull(question_filename);
5945
__attribute__((cleanup(cleanup_string)))
5946
char *const filename = strdup("/nonexistent/socket");
5947
g_assert_nonnull(filename);
5948
__attribute__((cleanup(string_set_clear)))
5949
string_set cancelled_filenames = {};
5950
__attribute__((cleanup(cleanup_buffer)))
5951
buffer password = {};
5953
__attribute__((cleanup(cleanup_queue)))
5954
task_queue *queue = create_queue();
5955
g_assert_nonnull(queue);
5957
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5958
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5960
__attribute__((cleanup(cleanup_close)))
5961
const int read_socket = socketfds[0];
5962
const int write_socket = socketfds[1];
5963
/* Close the server side socket to force ECONNRESET on client */
5964
g_assert_cmpint(close(read_socket), ==, 0);
5965
task_context task = {
5966
.func=send_password_to_socket,
5967
.question_filename=strdup(question_filename),
5968
.filename=strdup(filename),
5971
.password=&password,
5972
.cancelled_filenames=&cancelled_filenames,
5973
.mandos_client_exited=(bool[]){true},
5974
.password_is_read=(bool[]){true},
5975
.current_time=(mono_microsecs[]){0},
5977
g_assert_nonnull(task.question_filename);
5979
task.func(task, queue);
5981
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5983
const task_context *const added_task = find_matching_task(queue,
5985
g_assert_nonnull(added_task);
5986
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5988
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5993
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
5994
test_fixture *fixture,
5995
__attribute__((unused))
5996
gconstpointer user_data){
5997
__attribute__((cleanup(cleanup_close)))
5998
const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5999
__attribute__((cleanup(cleanup_string)))
6000
char *const question_filename = strdup("/nonexistent/question");
6001
g_assert_nonnull(question_filename);
6002
__attribute__((cleanup(cleanup_string)))
6003
char *const filename = strdup("/nonexistent/socket");
6004
g_assert_nonnull(filename);
6005
__attribute__((cleanup(string_set_clear)))
6006
string_set cancelled_filenames = {};
6007
__attribute__((cleanup(cleanup_buffer)))
6008
buffer password = {};
6010
const mono_microsecs current_time = 11;
6011
__attribute__((cleanup(cleanup_queue)))
6012
task_queue *queue = create_queue();
6013
g_assert_nonnull(queue);
6015
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6016
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6018
__attribute__((cleanup(cleanup_close)))
6019
const int read_socket = socketfds[0];
6020
const int write_socket = socketfds[1];
6021
/* Close the server side socket to force ECONNRESET on client */
6022
g_assert_cmpint(close(read_socket), ==, 0);
6023
task_context task = {
6024
.func=send_password_to_socket,
6025
.question_filename=strdup(question_filename),
6026
.filename=strdup(filename),
6029
.password=&password,
6030
.cancelled_filenames=&cancelled_filenames,
6031
.mandos_client_exited=(bool[]){true},
6032
.password_is_read=(bool[]){true},
6033
.current_time=¤t_time,
6035
g_assert_nonnull(task.question_filename);
6037
run_task_with_stderr_to_dev_null(task, queue);
6039
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6041
const task_context *const added_task = find_matching_task(queue,
6043
g_assert_nonnull(added_task);
6044
g_assert_true(queue->next_run == current_time + 1000000);
6045
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6048
static void assert_send_password_to_socket_password(buffer password){
6049
__attribute__((cleanup(cleanup_close)))
6050
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6051
g_assert_cmpint(epoll_fd, >=, 0);
6052
char *const question_filename = strdup("/nonexistent/question");
6053
g_assert_nonnull(question_filename);
6054
char *const filename = strdup("/nonexistent/socket");
6055
g_assert_nonnull(filename);
6056
__attribute__((cleanup(string_set_clear)))
6057
string_set cancelled_filenames = {};
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
task_context task = {
6070
.func=send_password_to_socket,
6071
.question_filename=question_filename,
6075
.password=&password,
6076
.cancelled_filenames=&cancelled_filenames,
6077
.mandos_client_exited=(bool[]){true},
6078
.password_is_read=(bool[]){true},
6079
.current_time=(mono_microsecs[]){0},
6082
char *expected_written_data = malloc(password.length + 2);
6083
g_assert_nonnull(expected_written_data);
6084
expected_written_data[0] = '+';
6085
expected_written_data[password.length + 1] = '\0';
6086
if(password.length > 0){
6087
g_assert_nonnull(password.data);
6088
memcpy(expected_written_data + 1, password.data, password.length);
6091
task.func(task, queue);
6094
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6095
(int)(password.length + 2));
6096
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6098
g_assert_true(memcmp(expected_written_data, buf,
6099
password.length + 2) == 0);
6101
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6103
free(expected_written_data);
6107
test_send_password_to_socket_null_password(__attribute__((unused))
6108
test_fixture *fixture,
6109
__attribute__((unused))
6110
gconstpointer user_data){
6111
__attribute__((cleanup(cleanup_buffer)))
6112
buffer password = {};
6113
assert_send_password_to_socket_password(password);
6117
test_send_password_to_socket_empty_password(__attribute__((unused))
6118
test_fixture *fixture,
6119
__attribute__((unused))
6120
gconstpointer user_data){
6121
__attribute__((cleanup(cleanup_buffer)))
6123
.data=malloc(1), /* because malloc(0) may return NULL */
6125
.allocated=0, /* deliberate lie */
6127
g_assert_nonnull(password.data);
6128
assert_send_password_to_socket_password(password);
6132
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6133
test_fixture *fixture,
6134
__attribute__((unused))
6135
gconstpointer user_data){
6136
__attribute__((cleanup(cleanup_buffer)))
6142
if(mlock(password.data, password.allocated) != 0){
6143
g_assert_true(errno == EPERM or errno == ENOMEM);
6145
assert_send_password_to_socket_password(password);
6149
test_send_password_to_socket_text_password(__attribute__((unused))
6150
test_fixture *fixture,
6151
__attribute__((unused))
6152
gconstpointer user_data){
6153
const char dummy_test_password[] = "dummy test password";
6154
__attribute__((cleanup(cleanup_buffer)))
6156
.data = strdup(dummy_test_password),
6157
.length = strlen(dummy_test_password),
6158
.allocated = sizeof(dummy_test_password),
6160
if(mlock(password.data, password.allocated) != 0){
6161
g_assert_true(errno == EPERM or errno == ENOMEM);
6163
assert_send_password_to_socket_password(password);
6167
test_send_password_to_socket_binary_password(__attribute__((unused))
6168
test_fixture *fixture,
6169
__attribute__((unused))
6170
gconstpointer user_data){
6171
__attribute__((cleanup(cleanup_buffer)))
6177
g_assert_nonnull(password.data);
6178
if(mlock(password.data, password.allocated) != 0){
6179
g_assert_true(errno == EPERM or errno == ENOMEM);
6181
char c = 1; /* Start at 1, avoiding NUL */
6182
for(int i=0; i < 255; i++){
6183
password.data[i] = c++;
6185
assert_send_password_to_socket_password(password);
6189
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6190
test_fixture *fixture,
6191
__attribute__((unused))
6194
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6195
__attribute__((cleanup(cleanup_buffer)))
6197
.data=malloc(sizeof(test_password)),
6198
.length=sizeof(test_password),
6199
.allocated=sizeof(test_password),
6201
g_assert_nonnull(password.data);
6202
if(mlock(password.data, password.allocated) !=0){
6203
g_assert_true(errno == EPERM or errno == ENOMEM);
6205
memcpy(password.data, test_password, password.allocated);
6206
assert_send_password_to_socket_password(password);
6209
static bool assert_add_existing_questions_to_devnull(task_queue
6222
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6223
test_fixture *fixture,
6224
__attribute__((unused))
6227
__attribute__((cleanup(cleanup_queue)))
6228
task_queue *queue = create_queue();
6229
g_assert_nonnull(queue);
6230
__attribute__((cleanup(cleanup_close)))
6231
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6232
g_assert_cmpint(epoll_fd, >=, 0);
6233
__attribute__((cleanup(string_set_clear)))
6234
string_set cancelled_filenames = {};
6236
g_assert_false(assert_add_existing_questions_to_devnull
6239
(buffer[]){{}}, /* password */
6240
&cancelled_filenames,
6241
(mono_microsecs[]){0}, /* current_time */
6242
(bool[]){false}, /* mandos_client_exited */
6243
(bool[]){false}, /* password_is_read */
6244
"/nonexistent")); /* dirname */
6246
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6250
bool assert_add_existing_questions_to_devnull(task_queue
6257
*cancelled_filenames,
6258
const mono_microsecs
6259
*const current_time,
6261
mandos_client_exited,
6266
__attribute__((cleanup(cleanup_close)))
6267
const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6268
g_assert_cmpint(devnull_fd, >=, 0);
6269
__attribute__((cleanup(cleanup_close)))
6270
const int real_stderr_fd = dup(STDERR_FILENO);
6271
g_assert_cmpint(real_stderr_fd, >=, 0);
6272
dup2(devnull_fd, STDERR_FILENO);
6273
const bool ret = add_existing_questions(queue, epoll_fd, password,
6274
cancelled_filenames,
6276
mandos_client_exited,
6277
password_is_read, dirname);
6278
dup2(real_stderr_fd, STDERR_FILENO);
6283
void test_add_existing_questions_no_questions(__attribute__((unused))
6284
test_fixture *fixture,
6285
__attribute__((unused))
6288
__attribute__((cleanup(cleanup_queue)))
6289
task_queue *queue = create_queue();
6290
g_assert_nonnull(queue);
6291
__attribute__((cleanup(cleanup_close)))
6292
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6293
g_assert_cmpint(epoll_fd, >=, 0);
6294
__attribute__((cleanup(string_set_clear)))
6295
string_set cancelled_filenames = {};
6296
__attribute__((cleanup(cleanup_string)))
6297
char *tempdir = make_temporary_directory();
6298
g_assert_nonnull(tempdir);
6300
g_assert_false(assert_add_existing_questions_to_devnull
6303
(buffer[]){{}}, /* password */
6304
&cancelled_filenames,
6305
(mono_microsecs[]){0}, /* current_time */
6306
(bool[]){false}, /* mandos_client_exited */
6307
(bool[]){false}, /* password_is_read */
6310
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6312
g_assert_cmpint(rmdir(tempdir), ==, 0);
6315
static char *make_question_file_in_directory(const char *const);
6318
void test_add_existing_questions_one_question(__attribute__((unused))
6319
test_fixture *fixture,
6320
__attribute__((unused))
6323
__attribute__((cleanup(cleanup_queue)))
6324
task_queue *queue = create_queue();
6325
g_assert_nonnull(queue);
6326
__attribute__((cleanup(cleanup_close)))
6327
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6328
g_assert_cmpint(epoll_fd, >=, 0);
6329
__attribute__((cleanup(cleanup_buffer)))
6330
buffer password = {};
6331
__attribute__((cleanup(string_set_clear)))
6332
string_set cancelled_filenames = {};
6333
const mono_microsecs current_time = 0;
6334
bool mandos_client_exited = false;
6335
bool password_is_read = false;
6336
__attribute__((cleanup(cleanup_string)))
6337
char *tempdir = make_temporary_directory();
6338
g_assert_nonnull(tempdir);
6339
__attribute__((cleanup(cleanup_string)))
6340
char *question_filename
6341
= make_question_file_in_directory(tempdir);
6342
g_assert_nonnull(question_filename);
6344
g_assert_true(assert_add_existing_questions_to_devnull
6348
&cancelled_filenames,
6350
&mandos_client_exited,
6354
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6356
g_assert_nonnull(find_matching_task(queue, (task_context){
6357
.func=open_and_parse_question,
6359
.filename=question_filename,
6360
.question_filename=question_filename,
6361
.password=&password,
6362
.cancelled_filenames=&cancelled_filenames,
6363
.current_time=¤t_time,
6364
.mandos_client_exited=&mandos_client_exited,
6365
.password_is_read=&password_is_read,
6368
g_assert_true(queue->next_run == 1);
6370
g_assert_cmpint(unlink(question_filename), ==, 0);
6371
g_assert_cmpint(rmdir(tempdir), ==, 0);
6374
static char *make_question_file_in_directory(const char
6376
return make_temporary_prefixed_file_in_directory("ask.", dir);
6380
void test_add_existing_questions_two_questions(__attribute__((unused))
6381
test_fixture *fixture,
6382
__attribute__((unused))
6385
__attribute__((cleanup(cleanup_queue)))
6386
task_queue *queue = create_queue();
6387
g_assert_nonnull(queue);
6388
__attribute__((cleanup(cleanup_close)))
6389
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6390
g_assert_cmpint(epoll_fd, >=, 0);
6391
__attribute__((cleanup(cleanup_buffer)))
6392
buffer password = {};
6393
__attribute__((cleanup(string_set_clear)))
6394
string_set cancelled_filenames = {};
6395
const mono_microsecs current_time = 0;
6396
bool mandos_client_exited = false;
6397
bool password_is_read = false;
6398
__attribute__((cleanup(cleanup_string)))
6399
char *tempdir = make_temporary_directory();
6400
g_assert_nonnull(tempdir);
6401
__attribute__((cleanup(cleanup_string)))
6402
char *question_filename1
6403
= make_question_file_in_directory(tempdir);
6404
g_assert_nonnull(question_filename1);
6405
__attribute__((cleanup(cleanup_string)))
6406
char *question_filename2
6407
= make_question_file_in_directory(tempdir);
6408
g_assert_nonnull(question_filename2);
6410
g_assert_true(assert_add_existing_questions_to_devnull
6414
&cancelled_filenames,
6416
&mandos_client_exited,
6420
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6422
g_assert_true(queue->next_run == 1);
6424
__attribute__((cleanup(string_set_clear)))
6425
string_set seen_questions = {};
6427
bool queue_contains_question_opener(char *const question_filename){
6428
return(find_matching_task(queue, (task_context){
6429
.func=open_and_parse_question,
6431
.question_filename=question_filename,
6432
.password=&password,
6433
.cancelled_filenames=&cancelled_filenames,
6434
.current_time=¤t_time,
6435
.mandos_client_exited=&mandos_client_exited,
6436
.password_is_read=&password_is_read,
6440
g_assert_true(queue_contains_question_opener(question_filename1));
6441
g_assert_true(queue_contains_question_opener(question_filename2));
6443
g_assert_true(queue->next_run == 1);
6445
g_assert_cmpint(unlink(question_filename1), ==, 0);
6446
g_assert_cmpint(unlink(question_filename2), ==, 0);
6447
g_assert_cmpint(rmdir(tempdir), ==, 0);
6451
test_add_existing_questions_non_questions(__attribute__((unused))
6452
test_fixture *fixture,
6453
__attribute__((unused))
6454
gconstpointer user_data){
6455
__attribute__((cleanup(cleanup_queue)))
6456
task_queue *queue = create_queue();
6457
g_assert_nonnull(queue);
6458
__attribute__((cleanup(cleanup_close)))
6459
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6460
g_assert_cmpint(epoll_fd, >=, 0);
6461
__attribute__((cleanup(string_set_clear)))
6462
string_set cancelled_filenames = {};
6463
__attribute__((cleanup(cleanup_string)))
6464
char *tempdir = make_temporary_directory();
6465
g_assert_nonnull(tempdir);
6466
__attribute__((cleanup(cleanup_string)))
6467
char *question_filename1
6468
= make_temporary_file_in_directory(tempdir);
6469
g_assert_nonnull(question_filename1);
6470
__attribute__((cleanup(cleanup_string)))
6471
char *question_filename2
6472
= make_temporary_file_in_directory(tempdir);
6473
g_assert_nonnull(question_filename2);
6475
g_assert_false(assert_add_existing_questions_to_devnull
6478
(buffer[]){{}}, /* password */
6479
&cancelled_filenames,
6480
(mono_microsecs[]){0}, /* current_time */
6481
(bool[]){false}, /* mandos_client_exited */
6482
(bool[]){false}, /* password_is_read */
6485
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6487
g_assert_cmpint(unlink(question_filename1), ==, 0);
6488
g_assert_cmpint(unlink(question_filename2), ==, 0);
6489
g_assert_cmpint(rmdir(tempdir), ==, 0);
6493
test_add_existing_questions_both_types(__attribute__((unused))
6494
test_fixture *fixture,
6495
__attribute__((unused))
6496
gconstpointer user_data){
6497
__attribute__((cleanup(cleanup_queue)))
6498
task_queue *queue = create_queue();
6499
g_assert_nonnull(queue);
6500
__attribute__((cleanup(cleanup_close)))
6501
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6502
g_assert_cmpint(epoll_fd, >=, 0);
6503
__attribute__((cleanup(cleanup_buffer)))
6504
buffer password = {};
6505
__attribute__((cleanup(string_set_clear)))
6506
string_set cancelled_filenames = {};
6507
const mono_microsecs current_time = 0;
6508
bool mandos_client_exited = false;
6509
bool password_is_read = false;
6510
__attribute__((cleanup(cleanup_string)))
6511
char *tempdir = make_temporary_directory();
6512
g_assert_nonnull(tempdir);
6513
__attribute__((cleanup(cleanup_string)))
6514
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6515
g_assert_nonnull(tempfilename1);
6516
__attribute__((cleanup(cleanup_string)))
6517
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6518
g_assert_nonnull(tempfilename2);
6519
__attribute__((cleanup(cleanup_string)))
6520
char *question_filename
6521
= make_question_file_in_directory(tempdir);
6522
g_assert_nonnull(question_filename);
6524
g_assert_true(assert_add_existing_questions_to_devnull
6528
&cancelled_filenames,
6530
&mandos_client_exited,
6534
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6536
g_assert_nonnull(find_matching_task(queue, (task_context){
6537
.func=open_and_parse_question,
6539
.filename=question_filename,
6540
.question_filename=question_filename,
6541
.password=&password,
6542
.cancelled_filenames=&cancelled_filenames,
6543
.current_time=¤t_time,
6544
.mandos_client_exited=&mandos_client_exited,
6545
.password_is_read=&password_is_read,
6548
g_assert_true(queue->next_run == 1);
6550
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6551
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6552
g_assert_cmpint(unlink(question_filename), ==, 0);
6553
g_assert_cmpint(rmdir(tempdir), ==, 0);
6556
static void test_wait_for_event_timeout(__attribute__((unused))
6557
test_fixture *fixture,
6558
__attribute__((unused))
6559
gconstpointer user_data){
6560
__attribute__((cleanup(cleanup_close)))
6561
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6562
g_assert_cmpint(epoll_fd, >=, 0);
6564
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6567
static void test_wait_for_event_event(__attribute__((unused))
6568
test_fixture *fixture,
6569
__attribute__((unused))
6570
gconstpointer user_data){
6571
__attribute__((cleanup(cleanup_close)))
6572
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6573
g_assert_cmpint(epoll_fd, >=, 0);
6575
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6576
__attribute__((cleanup(cleanup_close)))
6577
const int read_pipe = pipefds[0];
6578
__attribute__((cleanup(cleanup_close)))
6579
const int write_pipe = pipefds[1];
6580
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6581
&(struct epoll_event)
6582
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6583
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6585
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6588
static void test_wait_for_event_sigchld(test_fixture *fixture,
6589
__attribute__((unused))
6590
gconstpointer user_data){
6591
const pid_t pid = fork();
6592
if(pid == 0){ /* Child */
6593
if(not restore_signal_handler(&fixture->orig_sigaction)){
6594
_exit(EXIT_FAILURE);
6596
if(not restore_sigmask(&fixture->orig_sigmask)){
6597
_exit(EXIT_FAILURE);
6601
g_assert_true(pid != -1);
6602
__attribute__((cleanup(cleanup_close)))
6603
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6604
g_assert_cmpint(epoll_fd, >=, 0);
6606
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6609
g_assert_true(waitpid(pid, &status, 0) == pid);
6610
g_assert_true(WIFEXITED(status));
6611
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6614
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6615
test_fixture *fixture,
6616
__attribute__((unused))
6617
gconstpointer user_data){
6618
__attribute__((cleanup(cleanup_queue)))
6619
task_queue *queue = create_queue();
6620
g_assert_nonnull(queue);
6621
queue->next_run = 1;
6622
__attribute__((cleanup(cleanup_close)))
6623
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6624
__attribute__((cleanup(string_set_clear)))
6625
string_set cancelled_filenames = {};
6626
bool quit_now = false;
6628
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6629
g_assert_false(quit_now);
6630
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6634
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6635
test_fixture *fixture,
6636
__attribute__((unused))
6639
__attribute__((cleanup(cleanup_queue)))
6640
task_queue *queue = create_queue();
6641
g_assert_nonnull(queue);
6642
__attribute__((cleanup(string_set_clear)))
6643
string_set cancelled_filenames = {};
6644
bool quit_now = false;
6645
const char question_filename[] = "/nonexistent/question_filename";
6646
g_assert_true(string_set_add(&cancelled_filenames,
6647
question_filename));
6649
g_assert_true(add_to_queue(queue,
6650
(task_context){ .func=dummy_func }));
6652
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6653
g_assert_false(quit_now);
6654
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6655
g_assert_false(string_set_contains(cancelled_filenames,
6656
question_filename));
6660
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6661
test_fixture *fixture,
6662
__attribute__((unused))
6665
__attribute__((cleanup(cleanup_queue)))
6666
task_queue *queue = create_queue();
6667
g_assert_nonnull(queue);
6668
__attribute__((cleanup(string_set_clear)))
6669
string_set cancelled_filenames = {};
6670
bool quit_now = false;
6672
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6673
__attribute__((cleanup(cleanup_close)))
6674
const int read_pipe = pipefds[0];
6675
g_assert_cmpint(close(pipefds[1]), ==, 0);
6676
const char question_filename[] = "/nonexistent/question_filename";
6677
g_assert_true(string_set_add(&cancelled_filenames,
6678
question_filename));
6679
__attribute__((nonnull))
6680
void quit_func(const task_context task,
6681
__attribute__((unused)) task_queue *const q){
6682
g_assert_nonnull(task.quit_now);
6683
*task.quit_now = true;
6685
task_context task = {
6687
.question_filename=strdup(question_filename),
6688
.quit_now=&quit_now,
6691
g_assert_nonnull(task.question_filename);
6693
g_assert_true(add_to_queue(queue, task));
6695
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6696
g_assert_false(quit_now);
6698
/* read_pipe should be closed already */
6700
bool read_pipe_closed = (close(read_pipe) == -1);
6701
read_pipe_closed &= (errno == EBADF);
6702
g_assert_true(read_pipe_closed);
6705
static void test_run_queue_one_task(__attribute__((unused))
6706
test_fixture *fixture,
6707
__attribute__((unused))
6708
gconstpointer user_data){
6709
__attribute__((cleanup(cleanup_queue)))
6710
task_queue *queue = create_queue();
6711
g_assert_nonnull(queue);
6712
__attribute__((cleanup(string_set_clear)))
6713
string_set cancelled_filenames = {};
6714
bool quit_now = false;
6716
__attribute__((nonnull))
6717
void next_run_func(__attribute__((unused))
6718
const task_context task,
6719
task_queue *const q){
6723
task_context task = {
6724
.func=next_run_func,
6726
g_assert_true(add_to_queue(queue, task));
6728
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6729
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6730
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6733
static void test_run_queue_two_tasks(__attribute__((unused))
6734
test_fixture *fixture,
6735
__attribute__((unused))
6736
gconstpointer user_data){
6737
__attribute__((cleanup(cleanup_queue)))
6738
task_queue *queue = create_queue();
6739
g_assert_nonnull(queue);
6740
queue->next_run = 1;
6741
__attribute__((cleanup(string_set_clear)))
6742
string_set cancelled_filenames = {};
6743
bool quit_now = false;
6744
bool mandos_client_exited = false;
6746
__attribute__((nonnull))
6747
void next_run_func(__attribute__((unused))
6748
const task_context task,
6749
task_queue *const q){
6753
__attribute__((nonnull))
6754
void exited_func(const task_context task,
6755
__attribute__((unused)) task_queue *const q){
6756
*task.mandos_client_exited = true;
6759
task_context task1 = {
6760
.func=next_run_func,
6762
g_assert_true(add_to_queue(queue, task1));
6764
task_context task2 = {
6766
.mandos_client_exited=&mandos_client_exited,
6768
g_assert_true(add_to_queue(queue, task2));
6770
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6771
g_assert_false(quit_now);
6772
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6773
g_assert_true(mandos_client_exited);
6774
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6777
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6778
test_fixture *fixture,
6779
__attribute__((unused))
6780
gconstpointer user_data){
6781
__attribute__((cleanup(cleanup_queue)))
6782
task_queue *queue = create_queue();
6783
g_assert_nonnull(queue);
6784
__attribute__((cleanup(string_set_clear)))
6785
string_set cancelled_filenames = {};
6786
bool quit_now = false;
6787
bool mandos_client_exited = false;
6788
bool password_is_read = false;
6790
__attribute__((nonnull))
6791
void set_exited_func(const task_context task,
6792
__attribute__((unused)) task_queue *const q){
6793
*task.mandos_client_exited = true;
6794
*task.quit_now = true;
6796
task_context task1 = {
6797
.func=set_exited_func,
6798
.quit_now=&quit_now,
6799
.mandos_client_exited=&mandos_client_exited,
6801
g_assert_true(add_to_queue(queue, task1));
6803
__attribute__((nonnull))
6804
void set_read_func(const task_context task,
6805
__attribute__((unused)) task_queue *const q){
6806
*task.quit_now = true;
6807
*task.password_is_read = true;
6809
task_context task2 = {
6810
.func=set_read_func,
6811
.quit_now=&quit_now,
6812
.password_is_read=&password_is_read,
6814
g_assert_true(add_to_queue(queue, task2));
6816
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6817
g_assert_true(quit_now);
6818
g_assert_true(mandos_client_exited xor password_is_read);
6819
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6822
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6823
test_fixture *fixture,
6824
__attribute__((unused))
6825
gconstpointer user_data){
6826
__attribute__((cleanup(cleanup_queue)))
6827
task_queue *queue = create_queue();
6828
g_assert_nonnull(queue);
6829
__attribute__((cleanup(string_set_clear)))
6830
string_set cancelled_filenames = {};
6832
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6833
__attribute__((cleanup(cleanup_close)))
6834
const int read_pipe = pipefds[0];
6835
__attribute__((cleanup(cleanup_close)))
6836
const int write_pipe = pipefds[1];
6837
bool quit_now = false;
6839
__attribute__((nonnull))
6840
void read_func(const task_context task,
6841
__attribute__((unused)) task_queue *const q){
6842
*task.quit_now = true;
6844
task_context task1 = {
6846
.quit_now=&quit_now,
6849
g_assert_true(add_to_queue(queue, task1));
6851
__attribute__((nonnull))
6852
void write_func(const task_context task,
6853
__attribute__((unused)) task_queue *const q){
6854
*task.quit_now = true;
6856
task_context task2 = {
6858
.quit_now=&quit_now,
6861
g_assert_true(add_to_queue(queue, task2));
6863
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6864
g_assert_true(quit_now);
6866
/* Either read_pipe or write_pipe should be closed already */
6868
bool close_read_pipe = (close(read_pipe) == -1);
6869
close_read_pipe &= (errno == EBADF);
6871
bool close_write_pipe = (close(write_pipe) == -1);
6872
close_write_pipe &= (errno == EBADF);
6873
g_assert_true(close_read_pipe xor close_write_pipe);
6874
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6877
static void test_setup_signal_handler(__attribute__((unused))
6878
test_fixture *fixture,
6879
__attribute__((unused))
6880
gconstpointer user_data){
6881
/* Save current SIGCHLD action, whatever it is */
6882
struct sigaction expected_sigchld_action;
6883
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6886
/* Act; i.e. run the setup_signal_handler() function */
6887
struct sigaction actual_old_sigchld_action;
6888
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6890
/* Check that the function correctly set "actual_old_sigchld_action"
6891
to the same values as the previously saved
6892
"expected_sigchld_action" */
6893
/* Check member sa_handler */
6894
g_assert_true(actual_old_sigchld_action.sa_handler
6895
== expected_sigchld_action.sa_handler);
6896
/* Check member sa_mask */
6897
for(int signum = 1; signum < NSIG; signum++){
6898
const int expected_old_block_state
6899
= sigismember(&expected_sigchld_action.sa_mask, signum);
6900
g_assert_cmpint(expected_old_block_state, >=, 0);
6901
const int actual_old_block_state
6902
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6903
g_assert_cmpint(actual_old_block_state, >=, 0);
6904
g_assert_cmpint(actual_old_block_state,
6905
==, expected_old_block_state);
6907
/* Check member sa_flags */
6908
g_assert_true((actual_old_sigchld_action.sa_flags
6909
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6910
== (expected_sigchld_action.sa_flags
6911
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6913
/* Retrieve the current signal handler for SIGCHLD as set by
6914
setup_signal_handler() */
6915
struct sigaction actual_new_sigchld_action;
6916
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6917
&actual_new_sigchld_action), ==, 0);
6918
/* Check that the signal handler (member sa_handler) is correctly
6919
set to the "handle_sigchld" function */
6920
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6921
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6922
g_assert_true(actual_new_sigchld_action.sa_handler
6924
/* Check (in member sa_mask) that at least a handful of signals are
6925
actually blocked during the signal handler */
6926
for(int signum = 1; signum < NSIG; signum++){
6927
int actual_new_block_state;
6933
actual_new_block_state
6934
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6935
g_assert_cmpint(actual_new_block_state, ==, 1);
6937
case SIGKILL: /* non-blockable */
6938
case SIGSTOP: /* non-blockable */
6939
case SIGCHLD: /* always blocked */
6944
/* Check member sa_flags */
6945
g_assert_true((actual_new_sigchld_action.sa_flags
6946
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6947
== (SA_NOCLDSTOP | SA_RESTART));
6949
/* Restore signal handler */
6950
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6954
static void test_restore_signal_handler(__attribute__((unused))
6955
test_fixture *fixture,
6956
__attribute__((unused))
6957
gconstpointer user_data){
6958
/* Save current SIGCHLD action, whatever it is */
6959
struct sigaction expected_sigchld_action;
6960
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6962
/* Since we haven't established a signal handler yet, there should
6963
not be one established. But another test may have relied on
6964
restore_signal_handler() to restore the signal handler, and if
6965
restore_signal_handler() is buggy (which we should be prepared
6966
for in this test) the signal handler may not have been restored
6967
properly; check for this: */
6968
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
6970
/* Establish a signal handler */
6971
struct sigaction sigchld_action = {
6972
.sa_handler=handle_sigchld,
6973
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
6975
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
6976
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
6978
/* Act; i.e. run the restore_signal_handler() function */
6979
g_assert_true(restore_signal_handler(&expected_sigchld_action));
6981
/* Retrieve the restored signal handler data */
6982
struct sigaction actual_restored_sigchld_action;
6983
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6984
&actual_restored_sigchld_action), ==, 0);
6986
/* Check that the function correctly restored the signal action, as
6987
saved in "actual_restored_sigchld_action", to the same values as
6988
the previously saved "expected_sigchld_action" */
6989
/* Check member sa_handler */
6990
g_assert_true(actual_restored_sigchld_action.sa_handler
6991
== expected_sigchld_action.sa_handler);
6992
/* Check member sa_mask */
6993
for(int signum = 1; signum < NSIG; signum++){
6994
const int expected_old_block_state
6995
= sigismember(&expected_sigchld_action.sa_mask, signum);
6996
g_assert_cmpint(expected_old_block_state, >=, 0);
6997
const int actual_restored_block_state
6998
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
6999
g_assert_cmpint(actual_restored_block_state, >=, 0);
7000
g_assert_cmpint(actual_restored_block_state,
7001
==, expected_old_block_state);
7003
/* Check member sa_flags */
7004
g_assert_true((actual_restored_sigchld_action.sa_flags
7005
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7006
== (expected_sigchld_action.sa_flags
7007
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7010
static void test_block_sigchld(__attribute__((unused))
7011
test_fixture *fixture,
7012
__attribute__((unused))
7013
gconstpointer user_data){
7014
/* Save original signal mask */
7015
sigset_t expected_sigmask;
7016
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7019
/* Make sure SIGCHLD is unblocked for this test */
7020
sigset_t sigchld_sigmask;
7021
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7022
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7023
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7026
/* Act; i.e. run the block_sigchld() function */
7027
sigset_t actual_old_sigmask;
7028
g_assert_true(block_sigchld(&actual_old_sigmask));
7030
/* Check the actual_old_sigmask; it should be the same as the
7031
previously saved signal mask "expected_sigmask". */
7032
for(int signum = 1; signum < NSIG; signum++){
7033
const int expected_old_block_state
7034
= sigismember(&expected_sigmask, signum);
7035
g_assert_cmpint(expected_old_block_state, >=, 0);
7036
const int actual_old_block_state
7037
= sigismember(&actual_old_sigmask, signum);
7038
g_assert_cmpint(actual_old_block_state, >=, 0);
7039
g_assert_cmpint(actual_old_block_state,
7040
==, expected_old_block_state);
7043
/* Retrieve the newly set signal mask */
7044
sigset_t actual_sigmask;
7045
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7047
/* SIGCHLD should be blocked */
7048
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7050
/* Restore signal mask */
7051
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7055
static void test_restore_sigmask(__attribute__((unused))
7056
test_fixture *fixture,
7057
__attribute__((unused))
7058
gconstpointer user_data){
7059
/* Save original signal mask */
7060
sigset_t orig_sigmask;
7061
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7063
/* Make sure SIGCHLD is blocked for this test */
7064
sigset_t sigchld_sigmask;
7065
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7066
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7067
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7070
/* Act; i.e. run the restore_sigmask() function */
7071
g_assert_true(restore_sigmask(&orig_sigmask));
7073
/* Retrieve the newly restored signal mask */
7074
sigset_t restored_sigmask;
7075
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7078
/* Check the restored_sigmask; it should be the same as the
7079
previously saved signal mask "orig_sigmask". */
7080
for(int signum = 1; signum < NSIG; signum++){
7081
const int orig_block_state = sigismember(&orig_sigmask, signum);
7082
g_assert_cmpint(orig_block_state, >=, 0);
7083
const int restored_block_state = sigismember(&restored_sigmask,
7085
g_assert_cmpint(restored_block_state, >=, 0);
7086
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7089
/* Restore signal mask */
7090
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7094
static void test_parse_arguments_noargs(__attribute__((unused))
7095
test_fixture *fixture,
7096
__attribute__((unused))
7097
gconstpointer user_data){
7101
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7103
char *agent_directory = NULL;
7104
char *helper_directory = NULL;
7107
char *mandos_argz = NULL;
7108
size_t mandos_argz_length = 0;
7110
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7111
&helper_directory, &user, &group,
7112
&mandos_argz, &mandos_argz_length));
7113
g_assert_null(agent_directory);
7114
g_assert_null(helper_directory);
7115
g_assert_true(user == 0);
7116
g_assert_true(group == 0);
7117
g_assert_null(mandos_argz);
7118
g_assert_true(mandos_argz_length == 0);
7120
for(char **arg = argv; *arg != NULL; arg++){
7125
__attribute__((nonnull))
7126
static bool parse_arguments_devnull(int argc, char *argv[],
7127
const bool exit_failure,
7128
char **agent_directory,
7129
char **helper_directory,
7133
size_t *mandos_argz_length){
7135
FILE *real_stderr = stderr;
7136
FILE *devnull = fopen("/dev/null", "we");
7137
g_assert_nonnull(devnull);
7140
const bool ret = parse_arguments(argc, argv, exit_failure,
7142
helper_directory, user, group,
7143
mandos_argz, mandos_argz_length);
7144
const error_t saved_errno = errno;
7146
stderr = real_stderr;
7147
g_assert_cmpint(fclose(devnull), ==, 0);
7149
errno = saved_errno;
7154
static void test_parse_arguments_invalid(__attribute__((unused))
7155
test_fixture *fixture,
7156
__attribute__((unused))
7157
gconstpointer user_data){
7160
strdup("--invalid"),
7162
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7164
char *agent_directory = NULL;
7165
char *helper_directory = NULL;
7168
char *mandos_argz = NULL;
7169
size_t mandos_argz_length = 0;
7171
g_assert_false(parse_arguments_devnull(argc, argv, false,
7173
&helper_directory, &user,
7174
&group, &mandos_argz,
7175
&mandos_argz_length));
7177
g_assert_true(errno == EINVAL);
7178
g_assert_null(agent_directory);
7179
g_assert_null(helper_directory);
7180
g_assert_null(mandos_argz);
7181
g_assert_true(mandos_argz_length == 0);
7183
for(char **arg = argv; *arg != NULL; arg++){
7188
static void test_parse_arguments_long_dir(__attribute__((unused))
7189
test_fixture *fixture,
7190
__attribute__((unused))
7191
gconstpointer user_data){
7194
strdup("--agent-directory"),
7197
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7199
__attribute__((cleanup(cleanup_string)))
7200
char *agent_directory = NULL;
7201
char *helper_directory = NULL;
7204
__attribute__((cleanup(cleanup_string)))
7205
char *mandos_argz = NULL;
7206
size_t mandos_argz_length = 0;
7208
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7209
&helper_directory, &user, &group,
7210
&mandos_argz, &mandos_argz_length));
7212
g_assert_cmpstr(agent_directory, ==, "/tmp");
7213
g_assert_null(helper_directory);
7214
g_assert_true(user == 0);
7215
g_assert_true(group == 0);
7216
g_assert_null(mandos_argz);
7217
g_assert_true(mandos_argz_length == 0);
7219
for(char **arg = argv; *arg != NULL; arg++){
7224
static void test_parse_arguments_short_dir(__attribute__((unused))
7225
test_fixture *fixture,
7226
__attribute__((unused))
7227
gconstpointer user_data){
7233
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7235
__attribute__((cleanup(cleanup_string)))
7236
char *agent_directory = NULL;
7237
char *helper_directory = NULL;
7240
__attribute__((cleanup(cleanup_string)))
7241
char *mandos_argz = NULL;
7242
size_t mandos_argz_length = 0;
7244
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7245
&helper_directory, &user, &group,
7246
&mandos_argz, &mandos_argz_length));
7248
g_assert_cmpstr(agent_directory, ==, "/tmp");
7249
g_assert_null(helper_directory);
7250
g_assert_true(user == 0);
7251
g_assert_true(group == 0);
7252
g_assert_null(mandos_argz);
7253
g_assert_true(mandos_argz_length == 0);
7255
for(char **arg = argv; *arg != NULL; arg++){
7261
void test_parse_arguments_helper_directory(__attribute__((unused))
7262
test_fixture *fixture,
7263
__attribute__((unused))
7264
gconstpointer user_data){
7267
strdup("--helper-directory"),
7270
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7272
char *agent_directory = NULL;
7273
__attribute__((cleanup(cleanup_string)))
7274
char *helper_directory = NULL;
7277
__attribute__((cleanup(cleanup_string)))
7278
char *mandos_argz = NULL;
7279
size_t mandos_argz_length = 0;
7281
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7282
&helper_directory, &user, &group,
7283
&mandos_argz, &mandos_argz_length));
7285
g_assert_cmpstr(helper_directory, ==, "/tmp");
7286
g_assert_null(agent_directory);
7287
g_assert_true(user == 0);
7288
g_assert_true(group == 0);
7289
g_assert_null(mandos_argz);
7290
g_assert_true(mandos_argz_length == 0);
7292
for(char **arg = argv; *arg != NULL; arg++){
7298
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7299
test_fixture *fixture,
7300
__attribute__((unused))
7301
gconstpointer user_data){
7304
strdup("--plugin-helper-dir"),
7307
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7309
char *agent_directory = NULL;
7310
__attribute__((cleanup(cleanup_string)))
7311
char *helper_directory = NULL;
7314
__attribute__((cleanup(cleanup_string)))
7315
char *mandos_argz = NULL;
7316
size_t mandos_argz_length = 0;
7318
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7319
&helper_directory, &user, &group,
7320
&mandos_argz, &mandos_argz_length));
7322
g_assert_cmpstr(helper_directory, ==, "/tmp");
7323
g_assert_null(agent_directory);
7324
g_assert_true(user == 0);
7325
g_assert_true(group == 0);
7326
g_assert_null(mandos_argz);
7327
g_assert_true(mandos_argz_length == 0);
7329
for(char **arg = argv; *arg != NULL; arg++){
7334
static void test_parse_arguments_user(__attribute__((unused))
7335
test_fixture *fixture,
7336
__attribute__((unused))
7337
gconstpointer user_data){
7343
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7345
char *agent_directory = NULL;
7346
__attribute__((cleanup(cleanup_string)))
7347
char *helper_directory = NULL;
7350
__attribute__((cleanup(cleanup_string)))
7351
char *mandos_argz = NULL;
7352
size_t mandos_argz_length = 0;
7354
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7355
&helper_directory, &user, &group,
7356
&mandos_argz, &mandos_argz_length));
7358
g_assert_null(helper_directory);
7359
g_assert_null(agent_directory);
7360
g_assert_cmpuint((unsigned int)user, ==, 1000);
7361
g_assert_true(group == 0);
7362
g_assert_null(mandos_argz);
7363
g_assert_true(mandos_argz_length == 0);
7365
for(char **arg = argv; *arg != NULL; arg++){
7370
static void test_parse_arguments_user_invalid(__attribute__((unused))
7371
test_fixture *fixture,
7372
__attribute__((unused))
7380
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7382
char *agent_directory = NULL;
7383
__attribute__((cleanup(cleanup_string)))
7384
char *helper_directory = NULL;
7387
__attribute__((cleanup(cleanup_string)))
7388
char *mandos_argz = NULL;
7389
size_t mandos_argz_length = 0;
7391
g_assert_false(parse_arguments_devnull(argc, argv, false,
7393
&helper_directory, &user,
7394
&group, &mandos_argz,
7395
&mandos_argz_length));
7397
g_assert_null(helper_directory);
7398
g_assert_null(agent_directory);
7399
g_assert_cmpuint((unsigned int)user, ==, 0);
7400
g_assert_true(group == 0);
7401
g_assert_null(mandos_argz);
7402
g_assert_true(mandos_argz_length == 0);
7404
for(char **arg = argv; *arg != NULL; arg++){
7410
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7411
test_fixture *fixture,
7412
__attribute__((unused))
7413
gconstpointer user_data){
7419
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7421
char *agent_directory = NULL;
7422
__attribute__((cleanup(cleanup_string)))
7423
char *helper_directory = NULL;
7426
__attribute__((cleanup(cleanup_string)))
7427
char *mandos_argz = NULL;
7428
size_t mandos_argz_length = 0;
7430
g_assert_false(parse_arguments_devnull(argc, argv, false,
7432
&helper_directory, &user,
7433
&group, &mandos_argz,
7434
&mandos_argz_length));
7436
g_assert_null(helper_directory);
7437
g_assert_null(agent_directory);
7438
g_assert_cmpuint((unsigned int)user, ==, 0);
7439
g_assert_true(group == 0);
7440
g_assert_null(mandos_argz);
7441
g_assert_true(mandos_argz_length == 0);
7443
for(char **arg = argv; *arg != NULL; arg++){
7448
static void test_parse_arguments_group(__attribute__((unused))
7449
test_fixture *fixture,
7450
__attribute__((unused))
7451
gconstpointer user_data){
7457
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7459
char *agent_directory = NULL;
7460
__attribute__((cleanup(cleanup_string)))
7461
char *helper_directory = NULL;
7464
__attribute__((cleanup(cleanup_string)))
7465
char *mandos_argz = NULL;
7466
size_t mandos_argz_length = 0;
7468
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7469
&helper_directory, &user, &group,
7470
&mandos_argz, &mandos_argz_length));
7472
g_assert_null(helper_directory);
7473
g_assert_null(agent_directory);
7474
g_assert_true(user == 0);
7475
g_assert_cmpuint((unsigned int)group, ==, 1000);
7476
g_assert_null(mandos_argz);
7477
g_assert_true(mandos_argz_length == 0);
7479
for(char **arg = argv; *arg != NULL; arg++){
7484
static void test_parse_arguments_group_invalid(__attribute__((unused))
7485
test_fixture *fixture,
7486
__attribute__((unused))
7494
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7496
char *agent_directory = NULL;
7497
__attribute__((cleanup(cleanup_string)))
7498
char *helper_directory = NULL;
7501
__attribute__((cleanup(cleanup_string)))
7502
char *mandos_argz = NULL;
7503
size_t mandos_argz_length = 0;
7505
g_assert_false(parse_arguments_devnull(argc, argv, false,
7507
&helper_directory, &user,
7508
&group, &mandos_argz,
7509
&mandos_argz_length));
7511
g_assert_null(helper_directory);
7512
g_assert_null(agent_directory);
7513
g_assert_true(user == 0);
7514
g_assert_true(group == 0);
7515
g_assert_null(mandos_argz);
7516
g_assert_true(mandos_argz_length == 0);
7518
for(char **arg = argv; *arg != NULL; arg++){
7524
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7525
test_fixture *fixture,
7526
__attribute__((unused))
7527
gconstpointer user_data){
7533
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7535
char *agent_directory = NULL;
7536
__attribute__((cleanup(cleanup_string)))
7537
char *helper_directory = NULL;
7540
__attribute__((cleanup(cleanup_string)))
7541
char *mandos_argz = NULL;
7542
size_t mandos_argz_length = 0;
7544
g_assert_false(parse_arguments_devnull(argc, argv, false,
7546
&helper_directory, &user,
7547
&group, &mandos_argz,
7548
&mandos_argz_length));
7550
g_assert_null(helper_directory);
7551
g_assert_null(agent_directory);
7552
g_assert_cmpuint((unsigned int)group, ==, 0);
7553
g_assert_true(group == 0);
7554
g_assert_null(mandos_argz);
7555
g_assert_true(mandos_argz_length == 0);
7557
for(char **arg = argv; *arg != NULL; arg++){
7562
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7563
test_fixture *fixture,
7564
__attribute__((unused))
7569
strdup("mandos-client"),
7571
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7573
__attribute__((cleanup(cleanup_string)))
7574
char *agent_directory = NULL;
7575
__attribute__((cleanup(cleanup_string)))
7576
char *helper_directory = NULL;
7579
__attribute__((cleanup(cleanup_string)))
7580
char *mandos_argz = NULL;
7581
size_t mandos_argz_length = 0;
7583
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7584
&helper_directory, &user, &group,
7585
&mandos_argz, &mandos_argz_length));
7587
g_assert_null(agent_directory);
7588
g_assert_null(helper_directory);
7589
g_assert_true(user == 0);
7590
g_assert_true(group == 0);
7591
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7592
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7593
mandos_argz_length),
7596
for(char **arg = argv; *arg != NULL; arg++){
7601
static void test_parse_arguments_mandos_args(__attribute__((unused))
7602
test_fixture *fixture,
7603
__attribute__((unused))
7604
gconstpointer user_data){
7607
strdup("mandos-client"),
7612
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7614
__attribute__((cleanup(cleanup_string)))
7615
char *agent_directory = NULL;
7616
__attribute__((cleanup(cleanup_string)))
7617
char *helper_directory = NULL;
7620
__attribute__((cleanup(cleanup_string)))
7621
char *mandos_argz = NULL;
7622
size_t mandos_argz_length = 0;
7624
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7625
&helper_directory, &user, &group,
7626
&mandos_argz, &mandos_argz_length));
7628
g_assert_null(agent_directory);
7629
g_assert_null(helper_directory);
7630
g_assert_true(user == 0);
7631
g_assert_true(group == 0);
7632
char *marg = mandos_argz;
7633
g_assert_cmpstr(marg, ==, "mandos-client");
7634
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7635
g_assert_cmpstr(marg, ==, "one");
7636
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7637
g_assert_cmpstr(marg, ==, "two");
7638
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7639
g_assert_cmpstr(marg, ==, "three");
7640
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7641
mandos_argz_length),
7644
for(char **arg = argv; *arg != NULL; arg++){
7649
static void test_parse_arguments_all_args(__attribute__((unused))
7650
test_fixture *fixture,
7651
__attribute__((unused))
7652
gconstpointer user_data){
7655
strdup("--agent-directory"),
7657
strdup("--helper-directory"),
7663
strdup("mandos-client"),
7668
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7670
__attribute__((cleanup(cleanup_string)))
7671
char *agent_directory = NULL;
7672
__attribute__((cleanup(cleanup_string)))
7673
char *helper_directory = NULL;
7676
__attribute__((cleanup(cleanup_string)))
7677
char *mandos_argz = NULL;
7678
size_t mandos_argz_length = 0;
7680
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7681
&helper_directory, &user, &group,
7682
&mandos_argz, &mandos_argz_length));
7684
g_assert_cmpstr(agent_directory, ==, "/tmp");
7685
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7686
g_assert_true(user == 1);
7687
g_assert_true(group == 2);
7688
char *marg = mandos_argz;
7689
g_assert_cmpstr(marg, ==, "mandos-client");
7690
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7691
g_assert_cmpstr(marg, ==, "one");
7692
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7693
g_assert_cmpstr(marg, ==, "two");
7694
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7695
g_assert_cmpstr(marg, ==, "three");
7696
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7697
mandos_argz_length),
7700
for(char **arg = argv; *arg != NULL; arg++){
7705
static void test_parse_arguments_mixed(__attribute__((unused))
7706
test_fixture *fixture,
7707
__attribute__((unused))
7708
gconstpointer user_data){
7711
strdup("mandos-client"),
7715
strdup("--agent-directory"),
7719
strdup("--helper-directory=/var/tmp"),
7721
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7723
__attribute__((cleanup(cleanup_string)))
7724
char *agent_directory = NULL;
7725
__attribute__((cleanup(cleanup_string)))
7726
char *helper_directory = NULL;
7729
__attribute__((cleanup(cleanup_string)))
7730
char *mandos_argz = NULL;
7731
size_t mandos_argz_length = 0;
7733
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7734
&helper_directory, &user, &group,
7735
&mandos_argz, &mandos_argz_length));
7737
g_assert_cmpstr(agent_directory, ==, "/tmp");
7738
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7739
g_assert_true(user == 1);
7740
g_assert_true(group == 0);
7741
char *marg = mandos_argz;
7742
g_assert_cmpstr(marg, ==, "mandos-client");
7743
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7744
g_assert_cmpstr(marg, ==, "one");
7745
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7746
g_assert_cmpstr(marg, ==, "two");
7747
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7748
g_assert_cmpstr(marg, ==, "three");
7749
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7750
mandos_argz_length),
7753
for(char **arg = argv; *arg != NULL; arg++){
7758
/* End of tests section */
7760
/* Test boilerplate section; New tests should be added to the test
7761
suite definition here, in the "run_tests" function.
7763
Finally, this section also contains the should_only_run_tests()
7764
function used by main() for deciding if tests should be run or to
7767
__attribute__((cold))
7768
static bool run_tests(int argc, char *argv[]){
7769
g_test_init(&argc, &argv, NULL);
7771
/* A macro to add a test with no setup or teardown functions */
7772
#define test_add(testpath, testfunc) \
7774
g_test_add((testpath), test_fixture, NULL, NULL, \
7775
(testfunc), NULL); \
7778
/* Test the signal-related functions first, since some other tests
7779
depend on these functions in their setups and teardowns */
7780
test_add("/signal-handling/setup", test_setup_signal_handler);
7781
test_add("/signal-handling/restore", test_restore_signal_handler);
7782
test_add("/signal-handling/block", test_block_sigchld);
7783
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7785
/* Regular non-signal-related tests; these use no setups or
7787
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7788
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7789
test_add("/parse_arguments/long-dir",
7790
test_parse_arguments_long_dir);
7791
test_add("/parse_arguments/short-dir",
7792
test_parse_arguments_short_dir);
7793
test_add("/parse_arguments/helper-directory",
7794
test_parse_arguments_helper_directory);
7795
test_add("/parse_arguments/plugin-helper-dir",
7796
test_parse_arguments_plugin_helper_dir);
7797
test_add("/parse_arguments/user", test_parse_arguments_user);
7798
test_add("/parse_arguments/user-invalid",
7799
test_parse_arguments_user_invalid);
7800
test_add("/parse_arguments/user-zero-invalid",
7801
test_parse_arguments_user_zero_invalid);
7802
test_add("/parse_arguments/group", test_parse_arguments_group);
7803
test_add("/parse_arguments/group-invalid",
7804
test_parse_arguments_group_invalid);
7805
test_add("/parse_arguments/group-zero-invalid",
7806
test_parse_arguments_group_zero_invalid);
7807
test_add("/parse_arguments/mandos-noargs",
7808
test_parse_arguments_mandos_noargs);
7809
test_add("/parse_arguments/mandos-args",
7810
test_parse_arguments_mandos_args);
7811
test_add("/parse_arguments/all-args",
7812
test_parse_arguments_all_args);
7813
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7814
test_add("/queue/create", test_create_queue);
7815
test_add("/queue/add", test_add_to_queue);
7816
test_add("/queue/has_question/empty",
7817
test_queue_has_question_empty);
7818
test_add("/queue/has_question/false",
7819
test_queue_has_question_false);
7820
test_add("/queue/has_question/true", test_queue_has_question_true);
7821
test_add("/queue/has_question/false2",
7822
test_queue_has_question_false2);
7823
test_add("/queue/has_question/true2",
7824
test_queue_has_question_true2);
7825
test_add("/buffer/cleanup", test_cleanup_buffer);
7826
test_add("/string_set/net-set-contains-nothing",
7827
test_string_set_new_set_contains_nothing);
7828
test_add("/string_set/with-added-string-contains-it",
7829
test_string_set_with_added_string_contains_it);
7830
test_add("/string_set/cleared-does-not-contain-string",
7831
test_string_set_cleared_does_not_contain_str);
7832
test_add("/string_set/swap/one-with-empty",
7833
test_string_set_swap_one_with_empty);
7834
test_add("/string_set/swap/empty-with-one",
7835
test_string_set_swap_empty_with_one);
7836
test_add("/string_set/swap/one-with-one",
7837
test_string_set_swap_one_with_one);
7839
/* A macro to add a test using the setup and teardown functions */
7840
#define test_add_st(path, func) \
7842
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7846
/* Signal-related tests; these use setups and teardowns which
7847
establish, during each test run, a signal handler for, and a
7848
signal mask blocking, the SIGCHLD signal, just like main() */
7849
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7850
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7851
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7852
test_add_st("/run_queue/zeroes-next-run",
7853
test_run_queue_zeroes_next_run);
7854
test_add_st("/run_queue/clears-cancelled_filenames",
7855
test_run_queue_clears_cancelled_filenames);
7856
test_add_st("/run_queue/skips-cancelled-filenames",
7857
test_run_queue_skips_cancelled_filenames);
7858
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7859
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7860
test_add_st("/run_queue/two-tasks/quit",
7861
test_run_queue_two_tasks_quit);
7862
test_add_st("/run_queue/two-tasks-cleanup",
7863
test_run_queue_two_tasks_cleanup);
7864
test_add_st("/task-creators/start_mandos_client",
7865
test_start_mandos_client);
7866
test_add_st("/task-creators/start_mandos_client/execv",
7867
test_start_mandos_client_execv);
7868
test_add_st("/task-creators/start_mandos_client/suid/euid",
7869
test_start_mandos_client_suid_euid);
7870
test_add_st("/task-creators/start_mandos_client/suid/egid",
7871
test_start_mandos_client_suid_egid);
7872
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7873
test_start_mandos_client_suid_ruid);
7874
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7875
test_start_mandos_client_suid_rgid);
7876
test_add_st("/task-creators/start_mandos_client/read",
7877
test_start_mandos_client_read);
7878
test_add_st("/task-creators/start_mandos_client/helper-directory",
7879
test_start_mandos_client_helper_directory);
7880
test_add_st("/task-creators/start_mandos_client/sigmask",
7881
test_start_mandos_client_sigmask);
7882
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7883
test_wait_for_mandos_client_exit_badpid);
7884
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7885
test_wait_for_mandos_client_exit_noexit);
7886
test_add_st("/task/wait_for_mandos_client_exit/success",
7887
test_wait_for_mandos_client_exit_success);
7888
test_add_st("/task/wait_for_mandos_client_exit/failure",
7889
test_wait_for_mandos_client_exit_failure);
7890
test_add_st("/task/wait_for_mandos_client_exit/killed",
7891
test_wait_for_mandos_client_exit_killed);
7892
test_add_st("/task/read_mandos_client_output/readerror",
7893
test_read_mandos_client_output_readerror);
7894
test_add_st("/task/read_mandos_client_output/nodata",
7895
test_read_mandos_client_output_nodata);
7896
test_add_st("/task/read_mandos_client_output/eof",
7897
test_read_mandos_client_output_eof);
7898
test_add_st("/task/read_mandos_client_output/once",
7899
test_read_mandos_client_output_once);
7900
test_add_st("/task/read_mandos_client_output/malloc",
7901
test_read_mandos_client_output_malloc);
7902
test_add_st("/task/read_mandos_client_output/append",
7903
test_read_mandos_client_output_append);
7904
test_add_st("/task-creators/add_inotify_dir_watch",
7905
test_add_inotify_dir_watch);
7906
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7907
test_add_inotify_dir_watch_fail);
7908
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7909
test_add_inotify_dir_watch_EAGAIN);
7910
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7911
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7912
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7913
test_add_inotify_dir_watch_IN_MOVED_TO);
7914
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7915
test_add_inotify_dir_watch_IN_MOVED_FROM);
7916
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7917
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7918
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7919
test_add_inotify_dir_watch_IN_DELETE);
7920
test_add_st("/task/read_inotify_event/readerror",
7921
test_read_inotify_event_readerror);
7922
test_add_st("/task/read_inotify_event/bad-epoll",
7923
test_read_inotify_event_bad_epoll);
7924
test_add_st("/task/read_inotify_event/nodata",
7925
test_read_inotify_event_nodata);
7926
test_add_st("/task/read_inotify_event/eof",
7927
test_read_inotify_event_eof);
7928
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7929
test_read_inotify_event_IN_CLOSE_WRITE);
7930
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7931
test_read_inotify_event_IN_MOVED_TO);
7932
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7933
test_read_inotify_event_IN_MOVED_FROM);
7934
test_add_st("/task/read_inotify_event/IN_DELETE",
7935
test_read_inotify_event_IN_DELETE);
7936
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7937
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7938
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7939
test_read_inotify_event_IN_MOVED_TO_badname);
7940
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7941
test_read_inotify_event_IN_MOVED_FROM_badname);
7942
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7943
test_read_inotify_event_IN_DELETE_badname);
7944
test_add_st("/task/open_and_parse_question/ENOENT",
7945
test_open_and_parse_question_ENOENT);
7946
test_add_st("/task/open_and_parse_question/EIO",
7947
test_open_and_parse_question_EIO);
7948
test_add_st("/task/open_and_parse_question/parse-error",
7949
test_open_and_parse_question_parse_error);
7950
test_add_st("/task/open_and_parse_question/nosocket",
7951
test_open_and_parse_question_nosocket);
7952
test_add_st("/task/open_and_parse_question/badsocket",
7953
test_open_and_parse_question_badsocket);
7954
test_add_st("/task/open_and_parse_question/nopid",
7955
test_open_and_parse_question_nopid);
7956
test_add_st("/task/open_and_parse_question/badpid",
7957
test_open_and_parse_question_badpid);
7958
test_add_st("/task/open_and_parse_question/noexist_pid",
7959
test_open_and_parse_question_noexist_pid);
7960
test_add_st("/task/open_and_parse_question/no-notafter",
7961
test_open_and_parse_question_no_notafter);
7962
test_add_st("/task/open_and_parse_question/bad-notafter",
7963
test_open_and_parse_question_bad_notafter);
7964
test_add_st("/task/open_and_parse_question/notafter-0",
7965
test_open_and_parse_question_notafter_0);
7966
test_add_st("/task/open_and_parse_question/notafter-1",
7967
test_open_and_parse_question_notafter_1);
7968
test_add_st("/task/open_and_parse_question/notafter-1-1",
7969
test_open_and_parse_question_notafter_1_1);
7970
test_add_st("/task/open_and_parse_question/notafter-1-2",
7971
test_open_and_parse_question_notafter_1_2);
7972
test_add_st("/task/open_and_parse_question/equal-notafter",
7973
test_open_and_parse_question_equal_notafter);
7974
test_add_st("/task/open_and_parse_question/late-notafter",
7975
test_open_and_parse_question_late_notafter);
7976
test_add_st("/task/cancel_old_question/0-1-2",
7977
test_cancel_old_question_0_1_2);
7978
test_add_st("/task/cancel_old_question/0-2-1",
7979
test_cancel_old_question_0_2_1);
7980
test_add_st("/task/cancel_old_question/1-2-3",
7981
test_cancel_old_question_1_2_3);
7982
test_add_st("/task/cancel_old_question/1-3-2",
7983
test_cancel_old_question_1_3_2);
7984
test_add_st("/task/cancel_old_question/2-1-3",
7985
test_cancel_old_question_2_1_3);
7986
test_add_st("/task/cancel_old_question/2-3-1",
7987
test_cancel_old_question_2_3_1);
7988
test_add_st("/task/cancel_old_question/3-1-2",
7989
test_cancel_old_question_3_1_2);
7990
test_add_st("/task/cancel_old_question/3-2-1",
7991
test_cancel_old_question_3_2_1);
7992
test_add_st("/task/connect_question_socket/name-too-long",
7993
test_connect_question_socket_name_too_long);
7994
test_add_st("/task/connect_question_socket/connect-fail",
7995
test_connect_question_socket_connect_fail);
7996
test_add_st("/task/connect_question_socket/bad-epoll",
7997
test_connect_question_socket_bad_epoll);
7998
test_add_st("/task/connect_question_socket/usable",
7999
test_connect_question_socket_usable);
8000
test_add_st("/task/send_password_to_socket/client-not-exited",
8001
test_send_password_to_socket_client_not_exited);
8002
test_add_st("/task/send_password_to_socket/password-not-read",
8003
test_send_password_to_socket_password_not_read);
8004
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8005
test_send_password_to_socket_EMSGSIZE);
8006
test_add_st("/task/send_password_to_socket/retry",
8007
test_send_password_to_socket_retry);
8008
test_add_st("/task/send_password_to_socket/bad-epoll",
8009
test_send_password_to_socket_bad_epoll);
8010
test_add_st("/task/send_password_to_socket/null-password",
8011
test_send_password_to_socket_null_password);
8012
test_add_st("/task/send_password_to_socket/empty-password",
8013
test_send_password_to_socket_empty_password);
8014
test_add_st("/task/send_password_to_socket/empty-str-password",
8015
test_send_password_to_socket_empty_str_pass);
8016
test_add_st("/task/send_password_to_socket/text-password",
8017
test_send_password_to_socket_text_password);
8018
test_add_st("/task/send_password_to_socket/binary-password",
8019
test_send_password_to_socket_binary_password);
8020
test_add_st("/task/send_password_to_socket/nuls-in-password",
8021
test_send_password_to_socket_nuls_in_password);
8022
test_add_st("/task-creators/add_existing_questions/ENOENT",
8023
test_add_existing_questions_ENOENT);
8024
test_add_st("/task-creators/add_existing_questions/no-questions",
8025
test_add_existing_questions_no_questions);
8026
test_add_st("/task-creators/add_existing_questions/one-question",
8027
test_add_existing_questions_one_question);
8028
test_add_st("/task-creators/add_existing_questions/two-questions",
8029
test_add_existing_questions_two_questions);
8030
test_add_st("/task-creators/add_existing_questions/non-questions",
8031
test_add_existing_questions_non_questions);
8032
test_add_st("/task-creators/add_existing_questions/both-types",
8033
test_add_existing_questions_both_types);
8035
return g_test_run() == 0;
8038
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8039
GOptionContext *context = g_option_context_new("");
8041
g_option_context_set_help_enabled(context, FALSE);
8042
g_option_context_set_ignore_unknown_options(context, TRUE);
8044
gboolean run_tests = FALSE;
8045
GOptionEntry entries[] = {
8046
{ "test", 0, 0, G_OPTION_ARG_NONE,
8047
&run_tests, "Run tests", NULL },
8050
g_option_context_add_main_entries(context, entries, NULL);
8052
GError *error = NULL;
8054
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8055
g_option_context_free(context);
8056
g_error("Failed to parse options: %s", error->message);
8059
g_option_context_free(context);
8060
return run_tests != FALSE;