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_NOCTTY,
88
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
90
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
91
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
92
IN_CLOEXEC, inotify_add_watch(),
93
IN_CLOSE_WRITE, IN_MOVED_TO,
94
IN_DELETE, struct inotify_event */
95
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
96
#include <stdio.h> /* asprintf(), FILE, fopen(),
97
getline(), sscanf(), feof(),
98
ferror(), fclose(), stderr,
99
rename(), fdopen(), fprintf(),
101
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
102
GError, g_key_file_load_from_file(),
103
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
104
g_key_file_get_string(), guint64,
105
g_key_file_get_uint64(),
106
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
107
g_assert_true(), g_assert_nonnull(),
108
g_assert_null(), g_assert_false(),
109
g_assert_cmpint(), g_assert_cmpuint(),
110
g_test_skip(), g_assert_cmpstr(),
111
g_test_init(), g_test_add(), g_test_run(),
112
GOptionContext, g_option_context_new(),
113
g_option_context_set_help_enabled(), FALSE,
114
g_option_context_set_ignore_unknown_options(),
115
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
116
g_option_context_add_main_entries(),
117
g_option_context_parse(),
118
g_option_context_free(), g_error() */
119
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
120
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
121
SOCK_DGRAM, SOCK_NONBLOCK,
122
SOCK_CLOEXEC, connect(),
123
struct sockaddr, socklen_t,
124
shutdown(), SHUT_RD, send(),
125
MSG_NOSIGNAL, bind(), recv(),
127
#include <glob.h> /* globfree(), glob_t, glob(),
128
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
129
GLOB_ABORTED, GLOB_NOMATCH,
132
/* End of includes */
134
/* Start of declarations of private types and functions */
136
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
137
typedef uintmax_t mono_microsecs;
139
/* "task_queue" - A queue of tasks to be run */
141
struct task_struct *tasks; /* Tasks in this queue */
142
size_t length; /* Number of tasks */
143
/* Memory allocated for "tasks", in bytes */
145
/* Time when this queue should be run, at the latest */
146
mono_microsecs next_run;
147
} __attribute__((designated_init)) task_queue;
149
/* "func_type" - A function type for task functions
151
I.e. functions for the code which runs when a task is run, all have
153
typedef void (task_func) (const struct task_struct,
155
__attribute__((nonnull));
157
/* "buffer" - A data buffer for a growing array of bytes
159
Used for the "password" variable */
164
} __attribute__((designated_init)) buffer;
166
/* "string_set" - A set type which can contain strings
168
Used by the "cancelled_filenames" variable */
170
char *argz; /* Do not access these except in */
171
size_t argz_len; /* the string_set_* functions */
172
} __attribute__((designated_init)) string_set;
174
/* "task_context" - local variables for tasks
176
This data structure distinguishes between different tasks which are
177
using the same function. This data structure is passed to every
178
task function when each task is run.
180
Note that not every task uses every struct member. */
181
typedef struct task_struct {
182
task_func *const func; /* The function run by this task */
183
char *const question_filename; /* The question file */
184
const pid_t pid; /* Mandos client process ID */
185
const int epoll_fd; /* The epoll set file descriptor */
186
bool *const quit_now; /* Set to true on fatal errors */
187
const int fd; /* General purpose file descriptor */
188
bool *const mandos_client_exited; /* Set true when client exits */
189
buffer *const password; /* As read from client process */
190
bool *const password_is_read; /* "password" is done growing */
191
char *filename; /* General purpose file name */
192
/* A set of strings of all the file names of questions which have
193
been cancelled for any reason; tasks pertaining to these question
194
files should not be run */
195
string_set *const cancelled_filenames;
196
const mono_microsecs notafter; /* "NotAfter" from question file */
197
/* Updated before each queue run; is compared with queue.next_run */
198
const mono_microsecs *const current_time;
199
} __attribute__((designated_init)) task_context;
201
/* Declare all our functions here so we can define them in any order
202
below. Note: test functions are *not* declared here, they are
203
declared in the test section. */
204
__attribute__((warn_unused_result))
205
static bool should_only_run_tests(int *, char **[]);
206
__attribute__((warn_unused_result, cold))
207
static bool run_tests(int, char *[]);
208
static void handle_sigchld(__attribute__((unused)) int sig){}
209
__attribute__((warn_unused_result, malloc))
210
task_queue *create_queue(void);
211
__attribute__((nonnull, warn_unused_result))
212
bool add_to_queue(task_queue *const, const task_context);
213
__attribute__((nonnull))
214
void cleanup_task(const task_context *const);
215
__attribute__((nonnull))
216
void cleanup_queue(task_queue *const *const);
217
__attribute__((pure, nonnull, warn_unused_result))
218
bool queue_has_question(const task_queue *const);
219
__attribute__((nonnull))
220
void cleanup_close(const int *const);
221
__attribute__((nonnull))
222
void cleanup_string(char *const *const);
223
__attribute__((nonnull))
224
void cleanup_buffer(buffer *const);
225
__attribute__((pure, nonnull, warn_unused_result))
226
bool string_set_contains(const string_set, const char *const);
227
__attribute__((nonnull, warn_unused_result))
228
bool string_set_add(string_set *const, const char *const);
229
__attribute__((nonnull))
230
void string_set_clear(string_set *);
231
void string_set_swap(string_set *const, string_set *const);
232
__attribute__((nonnull, warn_unused_result))
233
bool start_mandos_client(task_queue *const, const int, bool *const,
234
bool *const, buffer *const, bool *const,
235
const struct sigaction *const,
236
const sigset_t, const char *const,
237
const uid_t, const gid_t,
238
const char *const *const);
239
__attribute__((nonnull))
240
task_func wait_for_mandos_client_exit;
241
__attribute__((nonnull))
242
task_func read_mandos_client_output;
243
__attribute__((warn_unused_result))
244
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
245
buffer *const, const char *const,
246
string_set *, const mono_microsecs *const,
247
bool *const, bool *const);
248
__attribute__((nonnull))
249
task_func read_inotify_event;
250
__attribute__((nonnull))
251
task_func open_and_parse_question;
252
__attribute__((nonnull))
253
task_func cancel_old_question;
254
__attribute__((nonnull))
255
task_func connect_question_socket;
256
__attribute__((nonnull))
257
task_func send_password_to_socket;
258
__attribute__((warn_unused_result))
259
bool add_existing_questions(task_queue *const, const int,
260
buffer *const, string_set *,
261
const mono_microsecs *const,
262
bool *const, bool *const,
264
__attribute__((nonnull, warn_unused_result))
265
bool wait_for_event(const int, const mono_microsecs,
266
const mono_microsecs);
267
bool run_queue(task_queue **const, string_set *const, bool *const);
268
bool clear_all_fds_from_epoll_set(const int);
269
mono_microsecs get_current_time(void);
270
__attribute__((nonnull, warn_unused_result))
271
bool setup_signal_handler(struct sigaction *const);
272
__attribute__((nonnull))
273
bool restore_signal_handler(const struct sigaction *const);
274
__attribute__((nonnull, warn_unused_result))
275
bool block_sigchld(sigset_t *const);
276
__attribute__((nonnull))
277
bool restore_sigmask(const sigset_t *const);
278
__attribute__((nonnull))
279
bool parse_arguments(int, char *[], const bool, char **, char **,
280
uid_t *const , gid_t *const, char **, size_t *);
282
/* End of declarations of private types and functions */
284
/* Start of "main" section; this section LACKS TESTS!
286
Code here should be as simple as possible. */
288
/* These are required to be global by Argp */
289
const char *argp_program_version = "password-agent " VERSION;
290
const char *argp_program_bug_address = "<mandos@recompile.se>";
292
int main(int argc, char *argv[]){
294
/* If the --test option is passed, skip all normal operations and
295
instead only run the run_tests() function, which also does all
296
its own option parsing, so we don't have to do anything here. */
297
if(should_only_run_tests(&argc, &argv)){
298
if(run_tests(argc, argv)){
299
return EXIT_SUCCESS; /* All tests successful */
301
return EXIT_FAILURE; /* Some test(s) failed */
304
__attribute__((cleanup(cleanup_string)))
305
char *agent_directory = NULL;
307
__attribute__((cleanup(cleanup_string)))
308
char *helper_directory = NULL;
313
__attribute__((cleanup(cleanup_string)))
314
char *mandos_argz = NULL;
315
size_t mandos_argz_length = 0;
317
if(not parse_arguments(argc, argv, true, &agent_directory,
318
&helper_directory, &user, &group,
319
&mandos_argz, &mandos_argz_length)){
320
/* This should never happen, since "true" is passed as the third
321
argument to parse_arguments() above, which should make
322
argp_parse() call exit() if any parsing error occurs. */
323
error(EX_USAGE, errno, "Failed to parse arguments");
326
const char default_agent_directory[] = "/run/systemd/ask-password";
327
const char default_helper_directory[]
328
= "/lib/mandos/plugin-helpers";
329
const char *const default_argv[]
330
= {"/lib/mandos/plugins.d/mandos-client", NULL };
332
/* Set variables to default values if unset */
333
if(agent_directory == NULL){
334
agent_directory = strdup(default_agent_directory);
335
if(agent_directory == NULL){
336
error(EX_OSERR, errno, "Failed strdup()");
339
if(helper_directory == NULL){
340
helper_directory = strdup(default_helper_directory);
341
if(helper_directory == NULL){
342
error(EX_OSERR, errno, "Failed strdup()");
346
user = 65534; /* nobody */
349
group = 65534; /* nogroup */
351
/* If parse_opt did not create an argz vector, create one with
353
if(mandos_argz == NULL){
355
#pragma GCC diagnostic push
356
/* argz_create() takes a non-const argv for some unknown reason -
357
argz_create() isn't modifying the strings, just copying them.
358
Therefore, this cast to non-const should be safe. */
359
#pragma GCC diagnostic ignored "-Wcast-qual"
361
errno = argz_create((char *const *)default_argv, &mandos_argz,
362
&mandos_argz_length);
364
#pragma GCC diagnostic pop
367
error(EX_OSERR, errno, "Failed argz_create()");
370
/* Use argz vector to create a normal argv, usable by execv() */
372
char **mandos_argv = malloc((argz_count(mandos_argz,
374
+ 1) * sizeof(char *));
375
if(mandos_argv == NULL){
376
error_t saved_errno = errno;
378
error(EX_OSERR, saved_errno, "Failed malloc()");
380
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
382
sigset_t orig_sigmask;
383
if(not block_sigchld(&orig_sigmask)){
387
struct sigaction old_sigchld_action;
388
if(not setup_signal_handler(&old_sigchld_action)){
392
mono_microsecs current_time = 0;
394
bool mandos_client_exited = false;
395
bool quit_now = false;
396
__attribute__((cleanup(cleanup_close)))
397
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
399
error(EX_OSERR, errno, "Failed to create epoll set fd");
401
__attribute__((cleanup(cleanup_queue)))
402
task_queue *queue = create_queue();
404
error(EX_OSERR, errno, "Failed to create task queue");
407
__attribute__((cleanup(cleanup_buffer)))
408
buffer password = {};
409
bool password_is_read = false;
411
__attribute__((cleanup(string_set_clear)))
412
string_set cancelled_filenames = {};
414
/* Add tasks to queue */
415
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
416
&quit_now, &password, &password_is_read,
417
&old_sigchld_action, orig_sigmask,
418
helper_directory, user, group,
419
(const char *const *)mandos_argv)){
420
return EX_OSERR; /* Error has already been printed */
422
/* These variables were only for start_mandos_client() and are not
427
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
428
agent_directory, &cancelled_filenames,
429
¤t_time, &mandos_client_exited,
431
switch(errno){ /* Error has already been printed */
441
if(not add_existing_questions(queue, epoll_fd, &password,
442
&cancelled_filenames, ¤t_time,
443
&mandos_client_exited,
444
&password_is_read, agent_directory)){
445
return EXIT_FAILURE; /* Error has already been printed */
450
current_time = get_current_time();
451
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
452
const error_t saved_errno = errno;
453
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
457
current_time = get_current_time();
458
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
459
const error_t saved_errno = errno;
460
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
463
/* When no tasks about questions are left in the queue, break out
464
of the loop (and implicitly exit the program) */
465
} while(queue_has_question(queue));
467
restore_signal_handler(&old_sigchld_action);
468
restore_sigmask(&orig_sigmask);
473
__attribute__((warn_unused_result))
474
mono_microsecs get_current_time(void){
475
struct timespec currtime;
476
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
477
error(0, errno, "Failed to get current time");
480
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
481
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
484
/* End of "main" section */
486
/* Start of regular code section; ALL this code has tests */
488
__attribute__((nonnull))
489
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
490
char **agent_directory, char **helper_directory,
491
uid_t *const user, gid_t *const group,
492
char **mandos_argz, size_t *mandos_argz_length){
494
const struct argp_option options[] = {
495
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
496
.doc="Systemd password agent directory" },
497
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
498
.doc="Mandos Client password helper directory" },
499
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
500
.flags=OPTION_HIDDEN | OPTION_ALIAS },
501
{ .name="user", .key='u', .arg="USERID",
502
.doc="User ID the Mandos Client will use as its unprivileged"
504
{ .name="userid", .key=130, /* From plugin--runner */
505
.flags=OPTION_HIDDEN | OPTION_ALIAS },
506
{ .name="group", .key='g', .arg="GROUPID",
507
.doc="Group ID the Mandos Client will use as its unprivileged"
509
{ .name="groupid", .key=131, /* From plugin--runner */
510
.flags=OPTION_HIDDEN | OPTION_ALIAS },
511
{ .name="test", .key=255, /* See should_only_run_tests() */
512
.doc="Skip normal operation, and only run self-tests. See"
513
" --test --help.", .group=10, },
517
__attribute__((nonnull(3)))
518
error_t parse_opt(int key, char *arg, struct argp_state *state){
521
case 'd': /* --agent-directory */
522
*agent_directory = strdup(arg);
524
case 128: /* --helper-directory */
525
case 129: /* --plugin-helper-dir */
526
*helper_directory = strdup(arg);
528
case 'u': /* --user */
529
case 130: /* --userid */
532
uintmax_t tmp_id = 0;
534
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
535
if(errno != 0 or tmp == arg or *tmp != '\0'
536
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
537
return ARGP_ERR_UNKNOWN;
539
*user = (uid_t)tmp_id;
543
case 'g': /* --group */
544
case 131: /* --groupid */
547
uintmax_t tmp_id = 0;
549
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
550
if(errno != 0 or tmp == arg or *tmp != '\0'
551
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
552
return ARGP_ERR_UNKNOWN;
554
*group = (gid_t)tmp_id;
559
/* Copy arguments into argz vector */
560
return argz_create(state->argv + state->next, mandos_argz,
563
return ARGP_ERR_UNKNOWN;
568
const struct argp argp = {
571
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
572
.doc = "Mandos password agent -- runs Mandos client as a"
573
" systemd password agent",
576
errno = argp_parse(&argp, argc, argv,
577
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
582
__attribute__((nonnull, warn_unused_result))
583
bool block_sigchld(sigset_t *const orig_sigmask){
584
sigset_t sigchld_sigmask;
585
if(sigemptyset(&sigchld_sigmask) < 0){
586
error(0, errno, "Failed to empty signal set");
589
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
590
error(0, errno, "Failed to add SIGCHLD to signal set");
593
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
594
error(0, errno, "Failed to block SIGCHLD signal");
600
__attribute__((nonnull, warn_unused_result, const))
601
bool restore_sigmask(const sigset_t *const orig_sigmask){
602
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
603
error(0, errno, "Failed to restore blocked signals");
609
__attribute__((nonnull, warn_unused_result))
610
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
611
struct sigaction sigchld_action = {
612
.sa_handler=handle_sigchld,
613
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
615
/* Set all signals in "sa_mask" struct member; this makes all
616
signals automatically blocked during signal handler */
617
if(sigfillset(&sigchld_action.sa_mask) != 0){
618
error(0, errno, "Failed to do sigfillset()");
621
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
622
error(0, errno, "Failed to set SIGCHLD signal handler");
628
__attribute__((nonnull, warn_unused_result))
629
bool restore_signal_handler(const struct sigaction *const
631
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
632
error(0, errno, "Failed to restore signal handler");
638
__attribute__((warn_unused_result, malloc))
639
task_queue *create_queue(void){
640
task_queue *queue = malloc(sizeof(task_queue));
644
queue->allocated = 0;
650
__attribute__((nonnull, warn_unused_result))
651
bool add_to_queue(task_queue *const queue, const task_context task){
652
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
653
if(needed_size > (queue->allocated)){
654
task_context *const new_tasks = realloc(queue->tasks,
656
if(new_tasks == NULL){
657
error(0, errno, "Failed to allocate %" PRIuMAX
658
" bytes for queue->tasks", (uintmax_t)needed_size);
661
queue->tasks = new_tasks;
662
queue->allocated = needed_size;
664
/* Using memcpy here is necessary because doing */
665
/* queue->tasks[queue->length++] = task; */
666
/* would violate const-ness of task members */
667
memcpy(&(queue->tasks[queue->length++]), &task,
668
sizeof(task_context));
672
__attribute__((nonnull))
673
void cleanup_task(const task_context *const task){
674
const error_t saved_errno = errno;
675
/* free and close all task data */
676
free(task->question_filename);
677
if(task->filename != task->question_filename){
678
free(task->filename);
681
kill(task->pid, SIGTERM);
689
__attribute__((nonnull))
690
void free_queue(task_queue *const queue){
695
__attribute__((nonnull))
696
void cleanup_queue(task_queue *const *const queue){
700
for(size_t i = 0; i < (*queue)->length; i++){
701
const task_context *const task = ((*queue)->tasks)+i;
707
__attribute__((pure, nonnull, warn_unused_result))
708
bool queue_has_question(const task_queue *const queue){
709
for(size_t i=0; i < queue->length; i++){
710
if(queue->tasks[i].question_filename != NULL){
717
__attribute__((nonnull))
718
void cleanup_close(const int *const fd){
719
const error_t saved_errno = errno;
724
__attribute__((nonnull))
725
void cleanup_string(char *const *const ptr){
729
__attribute__((nonnull))
730
void cleanup_buffer(buffer *buf){
731
if(buf->allocated > 0){
732
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
733
explicit_bzero(buf->data, buf->allocated);
735
memset(buf->data, '\0', buf->allocated);
738
if(buf->data != NULL){
739
if(munlock(buf->data, buf->allocated) != 0){
740
error(0, errno, "Failed to unlock memory of old buffer");
749
__attribute__((pure, nonnull, warn_unused_result))
750
bool string_set_contains(const string_set set, const char *const str){
751
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
752
s = argz_next(set.argz, set.argz_len, s)){
753
if(strcmp(s, str) == 0){
760
__attribute__((nonnull, warn_unused_result))
761
bool string_set_add(string_set *const set, const char *const str){
762
if(string_set_contains(*set, str)){
765
error_t error = argz_add(&set->argz, &set->argz_len, str);
773
__attribute__((nonnull))
774
void string_set_clear(string_set *set){
780
__attribute__((nonnull))
781
void string_set_swap(string_set *const set1, string_set *const set2){
782
/* Swap contents of two string sets */
784
char *const tmp_argz = set1->argz;
785
set1->argz = set2->argz;
786
set2->argz = tmp_argz;
789
const size_t tmp_argz_len = set1->argz_len;
790
set1->argz_len = set2->argz_len;
791
set2->argz_len = tmp_argz_len;
795
__attribute__((nonnull, warn_unused_result))
796
bool start_mandos_client(task_queue *const queue,
798
bool *const mandos_client_exited,
799
bool *const quit_now, buffer *const password,
800
bool *const password_is_read,
801
const struct sigaction *const
802
old_sigchld_action, const sigset_t sigmask,
803
const char *const helper_directory,
804
const uid_t user, const gid_t group,
805
const char *const *const argv){
807
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
808
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
812
const pid_t pid = fork();
814
if(not restore_signal_handler(old_sigchld_action)){
817
if(not restore_sigmask(&sigmask)){
820
if(close(pipefds[0]) != 0){
821
error(0, errno, "Failed to close() parent pipe fd");
824
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
825
error(0, errno, "Failed to dup2() pipe fd to stdout");
828
if(close(pipefds[1]) != 0){
829
error(0, errno, "Failed to close() old child pipe fd");
832
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
833
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
834
" \"%s\", 1)", helper_directory);
837
if(group != 0 and setresgid(group, 0, 0) == -1){
838
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
839
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
842
if(user != 0 and setresuid(user, 0, 0) == -1){
843
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
844
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
848
#pragma GCC diagnostic push
849
/* For historical reasons, the "argv" argument to execv() is not
850
const, but it is safe to override this. */
851
#pragma GCC diagnostic ignored "-Wcast-qual"
853
execv(argv[0], (char **)argv);
855
#pragma GCC diagnostic pop
857
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
862
if(not add_to_queue(queue, (task_context){
863
.func=wait_for_mandos_client_exit,
865
.mandos_client_exited=mandos_client_exited,
868
error(0, errno, "Failed to add wait_for_mandos_client to queue");
873
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
874
&(struct epoll_event)
875
{ .events=EPOLLIN | EPOLLRDHUP });
876
if(ret != 0 and errno != EEXIST){
877
error(0, errno, "Failed to add file descriptor to epoll set");
882
return add_to_queue(queue, (task_context){
883
.func=read_mandos_client_output,
888
.password_is_read=password_is_read,
892
__attribute__((nonnull))
893
void wait_for_mandos_client_exit(const task_context task,
894
task_queue *const queue){
895
const pid_t pid = task.pid;
896
bool *const mandos_client_exited = task.mandos_client_exited;
897
bool *const quit_now = task.quit_now;
900
switch(waitpid(pid, &status, WNOHANG)){
901
case 0: /* Not exited yet */
902
if(not add_to_queue(queue, task)){
903
error(0, errno, "Failed to add myself to queue");
908
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
914
default: /* Has exited */
915
*mandos_client_exited = true;
916
if((not WIFEXITED(status))
917
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
918
error(0, 0, "Mandos client failed or was killed");
924
__attribute__((nonnull))
925
void read_mandos_client_output(const task_context task,
926
task_queue *const queue){
927
buffer *const password = task.password;
928
bool *const quit_now = task.quit_now;
929
bool *const password_is_read = task.password_is_read;
930
const int fd = task.fd;
931
const int epoll_fd = task.epoll_fd;
933
const size_t new_potential_size = (password->length + PIPE_BUF);
934
if(password->allocated < new_potential_size){
935
char *const new_buffer = calloc(new_potential_size, 1);
936
if(new_buffer == NULL){
937
error(0, errno, "Failed to allocate %" PRIuMAX
938
" bytes for password", (uintmax_t)new_potential_size);
943
if(mlock(new_buffer, new_potential_size) != 0){
944
/* Warn but do not treat as fatal error */
945
if(errno != EPERM and errno != ENOMEM){
946
error(0, errno, "Failed to lock memory for password");
949
if(password->length > 0){
950
memcpy(new_buffer, password->data, password->length);
951
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
952
explicit_bzero(password->data, password->allocated);
954
memset(password->data, '\0', password->allocated);
957
if(password->data != NULL){
958
if(munlock(password->data, password->allocated) != 0){
959
error(0, errno, "Failed to unlock memory of old buffer");
961
free(password->data);
963
password->data = new_buffer;
964
password->allocated = new_potential_size;
967
const ssize_t read_length = read(fd, password->data
968
+ password->length, PIPE_BUF);
970
if(read_length == 0){ /* EOF */
971
*password_is_read = true;
975
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
976
error(0, errno, "Failed to read password from Mandos client");
981
if(read_length > 0){ /* Data has been read */
982
password->length += (size_t)read_length;
985
/* Either data was read, or EAGAIN was indicated, meaning no data
988
/* Re-add the fd to the epoll set */
989
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
990
&(struct epoll_event)
991
{ .events=EPOLLIN | EPOLLRDHUP });
992
if(ret != 0 and errno != EEXIST){
993
error(0, errno, "Failed to re-add file descriptor to epoll set");
999
/* Re-add myself to the queue */
1000
if(not add_to_queue(queue, task)){
1001
error(0, errno, "Failed to add myself to queue");
1007
__attribute__((nonnull, warn_unused_result))
1008
bool add_inotify_dir_watch(task_queue *const queue,
1009
const int epoll_fd, bool *const quit_now,
1010
buffer *const password,
1011
const char *const dir,
1012
string_set *cancelled_filenames,
1013
const mono_microsecs *const current_time,
1014
bool *const mandos_client_exited,
1015
bool *const password_is_read){
1016
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1018
error(0, errno, "Failed to create inotify instance");
1022
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1023
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1026
error(0, errno, "Failed to create inotify watch on %s", dir);
1030
/* Add the inotify fd to the epoll set */
1031
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1032
&(struct epoll_event)
1033
{ .events=EPOLLIN | EPOLLRDHUP });
1034
if(ret != 0 and errno != EEXIST){
1035
error(0, errno, "Failed to add file descriptor to epoll set");
1040
const task_context read_inotify_event_task = {
1041
.func=read_inotify_event,
1046
.filename=strdup(dir),
1047
.cancelled_filenames=cancelled_filenames,
1048
.current_time=current_time,
1049
.mandos_client_exited=mandos_client_exited,
1050
.password_is_read=password_is_read,
1052
if(read_inotify_event_task.filename == NULL){
1053
error(0, errno, "Failed to strdup(\"%s\")", dir);
1058
return add_to_queue(queue, read_inotify_event_task);
1061
__attribute__((nonnull))
1062
void read_inotify_event(const task_context task,
1063
task_queue *const queue){
1064
const int fd = task.fd;
1065
const int epoll_fd = task.epoll_fd;
1066
char *const filename = task.filename;
1067
bool *quit_now = task.quit_now;
1068
buffer *const password = task.password;
1069
string_set *const cancelled_filenames = task.cancelled_filenames;
1070
const mono_microsecs *const current_time = task.current_time;
1071
bool *const mandos_client_exited = task.mandos_client_exited;
1072
bool *const password_is_read = task.password_is_read;
1074
/* "sufficient to read at least one event." - inotify(7) */
1075
const size_t ievent_size = (sizeof(struct inotify_event)
1078
struct inotify_event event;
1079
char name_buffer[NAME_MAX + 1];
1081
struct inotify_event *const ievent = &ievent_buffer.event;
1083
const ssize_t read_length = read(fd, ievent, ievent_size);
1084
if(read_length == 0){ /* EOF */
1085
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1087
cleanup_task(&task);
1090
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1091
error(0, errno, "Failed to read from inotify fd for directory %s",
1094
cleanup_task(&task);
1097
if(read_length > 0 /* Data has been read */
1098
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1099
char *question_filename = NULL;
1100
const ssize_t question_filename_length
1101
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1102
if(question_filename_length < 0){
1103
error(0, errno, "Failed to create file name from directory name"
1104
" %s and file name %s", filename, ievent->name);
1106
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1107
if(not add_to_queue(queue, (task_context){
1108
.func=open_and_parse_question,
1110
.question_filename=question_filename,
1111
.filename=question_filename,
1113
.cancelled_filenames=cancelled_filenames,
1114
.current_time=current_time,
1115
.mandos_client_exited=mandos_client_exited,
1116
.password_is_read=password_is_read,
1118
error(0, errno, "Failed to add open_and_parse_question task"
1119
" for file name %s to queue", filename);
1121
/* Force the added task (open_and_parse_question) to run
1123
queue->next_run = 1;
1125
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1126
if(not string_set_add(cancelled_filenames,
1127
question_filename)){
1128
error(0, errno, "Could not add question %s to"
1129
" cancelled_questions", question_filename);
1131
free(question_filename);
1132
cleanup_task(&task);
1135
free(question_filename);
1140
/* Either data was read, or EAGAIN was indicated, meaning no data
1143
/* Re-add myself to the queue */
1144
if(not add_to_queue(queue, task)){
1145
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1146
" queue", filename);
1148
cleanup_task(&task);
1152
/* Re-add the fd to the epoll set */
1153
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1154
&(struct epoll_event)
1155
{ .events=EPOLLIN | EPOLLRDHUP });
1156
if(ret != 0 and errno != EEXIST){
1157
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1158
" directory %s to epoll set", fd, filename);
1159
/* Force the added task (read_inotify_event) to run again, at most
1160
one second from now */
1161
if((queue->next_run == 0)
1162
or (queue->next_run > (*current_time + 1000000))){
1163
queue->next_run = *current_time + 1000000;
1168
__attribute__((nonnull))
1169
void open_and_parse_question(const task_context task,
1170
task_queue *const queue){
1171
__attribute__((cleanup(cleanup_string)))
1172
char *question_filename = task.question_filename;
1173
const int epoll_fd = task.epoll_fd;
1174
buffer *const password = task.password;
1175
string_set *const cancelled_filenames = task.cancelled_filenames;
1176
const mono_microsecs *const current_time = task.current_time;
1177
bool *const mandos_client_exited = task.mandos_client_exited;
1178
bool *const password_is_read = task.password_is_read;
1180
/* We use the GLib "Key-value file parser" functions to parse the
1181
question file. See <https://www.freedesktop.org/wiki/Software
1182
/systemd/PasswordAgents/> for specification of contents */
1183
__attribute__((nonnull))
1184
void cleanup_g_key_file(GKeyFile **key_file){
1185
if(*key_file != NULL){
1186
g_key_file_free(*key_file);
1190
__attribute__((cleanup(cleanup_g_key_file)))
1191
GKeyFile *key_file = g_key_file_new();
1192
if(key_file == NULL){
1193
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1197
GError *glib_error = NULL;
1198
if(g_key_file_load_from_file(key_file, question_filename,
1199
G_KEY_FILE_NONE, &glib_error) != TRUE){
1200
/* If a file was removed, we should ignore it, so */
1201
/* only show error message if file actually existed */
1202
if(glib_error->code != G_FILE_ERROR_NOENT){
1203
error(0, 0, "Failed to load question data from file \"%s\": %s",
1204
question_filename, glib_error->message);
1209
__attribute__((cleanup(cleanup_string)))
1210
char *socket_name = g_key_file_get_string(key_file, "Ask",
1213
if(socket_name == NULL){
1214
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1215
question_filename, glib_error->message);
1219
if(strlen(socket_name) == 0){
1220
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1225
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1227
if(glib_error != NULL){
1228
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1229
question_filename, glib_error->message);
1233
if((pid != (guint64)((pid_t)pid))
1234
or (kill((pid_t)pid, 0) != 0)){
1235
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1236
" does not exist", (uintmax_t)pid, question_filename);
1240
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1241
"NotAfter", &glib_error);
1242
if(glib_error != NULL){
1243
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1244
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1245
" %s", question_filename, glib_error->message);
1250
if(queue->next_run == 0 or (queue->next_run > notafter)){
1251
queue->next_run = notafter;
1253
if(*current_time >= notafter){
1258
const task_context connect_question_socket_task = {
1259
.func=connect_question_socket,
1260
.question_filename=strdup(question_filename),
1263
.filename=strdup(socket_name),
1264
.cancelled_filenames=task.cancelled_filenames,
1265
.mandos_client_exited=mandos_client_exited,
1266
.password_is_read=password_is_read,
1267
.current_time=current_time,
1269
if(connect_question_socket_task.question_filename == NULL
1270
or connect_question_socket_task.filename == NULL
1271
or not add_to_queue(queue, connect_question_socket_task)){
1272
error(0, errno, "Failed to add connect_question_socket for socket"
1273
" %s (from \"%s\") to queue", socket_name,
1275
cleanup_task(&connect_question_socket_task);
1278
/* Force the added task (connect_question_socket) to run
1280
queue->next_run = 1;
1283
char *const dup_filename = strdup(question_filename);
1284
const task_context cancel_old_question_task = {
1285
.func=cancel_old_question,
1286
.question_filename=dup_filename,
1288
.filename=dup_filename,
1289
.cancelled_filenames=cancelled_filenames,
1290
.current_time=current_time,
1292
if(cancel_old_question_task.question_filename == NULL
1293
or not add_to_queue(queue, cancel_old_question_task)){
1294
error(0, errno, "Failed to add cancel_old_question for file "
1295
"\"%s\" to queue", question_filename);
1296
cleanup_task(&cancel_old_question_task);
1302
__attribute__((nonnull))
1303
void cancel_old_question(const task_context task,
1304
task_queue *const queue){
1305
char *const question_filename = task.question_filename;
1306
string_set *const cancelled_filenames = task.cancelled_filenames;
1307
const mono_microsecs notafter = task.notafter;
1308
const mono_microsecs *const current_time = task.current_time;
1310
if(*current_time >= notafter){
1311
if(not string_set_add(cancelled_filenames, question_filename)){
1312
error(0, errno, "Failed to cancel question for file %s",
1315
cleanup_task(&task);
1319
if(not add_to_queue(queue, task)){
1320
error(0, errno, "Failed to add cancel_old_question for file "
1321
"%s to queue", question_filename);
1322
cleanup_task(&task);
1326
if((queue->next_run == 0) or (queue->next_run > notafter)){
1327
queue->next_run = notafter;
1331
__attribute__((nonnull))
1332
void connect_question_socket(const task_context task,
1333
task_queue *const queue){
1334
char *const question_filename = task.question_filename;
1335
char *const filename = task.filename;
1336
const int epoll_fd = task.epoll_fd;
1337
buffer *const password = task.password;
1338
string_set *const cancelled_filenames = task.cancelled_filenames;
1339
bool *const mandos_client_exited = task.mandos_client_exited;
1340
bool *const password_is_read = task.password_is_read;
1341
const mono_microsecs *const current_time = task.current_time;
1343
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1345
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1346
error(0, 0, "Socket filename is larger than"
1347
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1348
(uintmax_t)sizeof(sock_name.sun_path), filename);
1349
if(not string_set_add(cancelled_filenames, question_filename)){
1350
error(0, errno, "Failed to cancel question for file %s",
1353
cleanup_task(&task);
1357
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1358
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1361
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1362
if(not add_to_queue(queue, task)){
1363
error(0, errno, "Failed to add connect_question_socket for file"
1364
" \"%s\" and socket \"%s\" to queue", question_filename,
1366
cleanup_task(&task);
1368
/* Force the added task (connect_question_socket) to run
1370
queue->next_run = 1;
1375
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1376
if(connect(fd, (struct sockaddr *)&sock_name,
1377
(socklen_t)SUN_LEN(&sock_name)) != 0){
1378
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1379
if(not add_to_queue(queue, task)){
1380
error(0, errno, "Failed to add connect_question_socket for file"
1381
" \"%s\" and socket \"%s\" to queue", question_filename,
1383
cleanup_task(&task);
1385
/* Force the added task (connect_question_socket) to run again,
1386
at most one second from now */
1387
if((queue->next_run == 0)
1388
or (queue->next_run > (*current_time + 1000000))){
1389
queue->next_run = *current_time + 1000000;
1395
/* Not necessary, but we can try, and merely warn on failure */
1396
if(shutdown(fd, SHUT_RD) != 0){
1397
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1401
/* Add the fd to the epoll set */
1402
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1403
&(struct epoll_event){ .events=EPOLLOUT })
1405
error(0, errno, "Failed to add inotify file descriptor %d for"
1406
" socket %s to epoll set", fd, filename);
1407
if(not add_to_queue(queue, task)){
1408
error(0, errno, "Failed to add connect_question_socket for file"
1409
" \"%s\" and socket \"%s\" to queue", question_filename,
1411
cleanup_task(&task);
1413
/* Force the added task (connect_question_socket) to run again,
1414
at most one second from now */
1415
if((queue->next_run == 0)
1416
or (queue->next_run > (*current_time + 1000000))){
1417
queue->next_run = *current_time + 1000000;
1423
/* add task send_password_to_socket to queue */
1424
const task_context send_password_to_socket_task = {
1425
.func=send_password_to_socket,
1426
.question_filename=question_filename,
1431
.cancelled_filenames=cancelled_filenames,
1432
.mandos_client_exited=mandos_client_exited,
1433
.password_is_read=password_is_read,
1434
.current_time=current_time,
1437
if(not add_to_queue(queue, send_password_to_socket_task)){
1438
error(0, errno, "Failed to add send_password_to_socket for"
1439
" file \"%s\" and socket \"%s\" to queue",
1440
question_filename, filename);
1441
cleanup_task(&send_password_to_socket_task);
1445
__attribute__((nonnull))
1446
void send_password_to_socket(const task_context task,
1447
task_queue *const queue){
1448
char *const question_filename=task.question_filename;
1449
char *const filename=task.filename;
1450
const int epoll_fd=task.epoll_fd;
1451
const int fd=task.fd;
1452
buffer *const password=task.password;
1453
string_set *const cancelled_filenames=task.cancelled_filenames;
1454
bool *const mandos_client_exited = task.mandos_client_exited;
1455
bool *const password_is_read = task.password_is_read;
1456
const mono_microsecs *const current_time = task.current_time;
1458
if(*mandos_client_exited and *password_is_read){
1460
const size_t send_buffer_length = password->length + 2;
1461
char *send_buffer = malloc(send_buffer_length);
1462
if(send_buffer == NULL){
1463
error(0, errno, "Failed to allocate send_buffer");
1465
if(mlock(send_buffer, send_buffer_length) != 0){
1466
/* Warn but do not treat as fatal error */
1467
if(errno != EPERM and errno != ENOMEM){
1468
error(0, errno, "Failed to lock memory for password"
1472
/* “[…] send a single datagram to the socket consisting of the
1473
password string either prefixed with "+" or with "-"
1474
depending on whether the password entry was successful or
1475
not. You may but don't have to include a final NUL byte in
1478
— <https://www.freedesktop.org/wiki/Software/systemd/
1479
PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1481
send_buffer[0] = '+'; /* Prefix with "+" */
1482
/* Always add an extra NUL */
1483
send_buffer[password->length + 1] = '\0';
1484
if(password->length > 0){
1485
memcpy(send_buffer + 1, password->data, password->length);
1488
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1490
const error_t saved_errno = errno;
1491
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1492
explicit_bzero(send_buffer, send_buffer_length);
1494
memset(send_buffer, '\0', send_buffer_length);
1496
if(munlock(send_buffer, send_buffer_length) != 0){
1497
error(0, errno, "Failed to unlock memory of send buffer");
1500
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1501
switch(saved_errno){
1514
error(0, 0, "Password of size %" PRIuMAX " is too big",
1515
(uintmax_t)password->length);
1519
__attribute__((fallthrough));
1522
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1523
error(0, 0, "Password only partially sent to socket");
1528
__attribute__((fallthrough));
1531
error(0, saved_errno, "Failed to send() to socket %s",
1533
if(not string_set_add(cancelled_filenames,
1534
question_filename)){
1535
error(0, errno, "Failed to cancel question for file %s",
1538
cleanup_task(&task);
1543
cleanup_task(&task);
1549
/* We failed or are not ready yet; retry later */
1551
if(not add_to_queue(queue, task)){
1552
error(0, errno, "Failed to add send_password_to_socket for"
1553
" file %s and socket %s to queue", question_filename,
1555
cleanup_task(&task);
1558
/* Add the fd to the epoll set */
1559
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1560
&(struct epoll_event){ .events=EPOLLOUT })
1562
error(0, errno, "Failed to add socket file descriptor %d for"
1563
" socket %s to epoll set", fd, filename);
1564
/* Force the added task (send_password_to_socket) to run again, at
1565
most one second from now */
1566
if((queue->next_run == 0)
1567
or (queue->next_run > (*current_time + 1000000))){
1568
queue->next_run = *current_time + 1000000;
1573
__attribute__((warn_unused_result))
1574
bool add_existing_questions(task_queue *const queue,
1576
buffer *const password,
1577
string_set *cancelled_filenames,
1578
const mono_microsecs *const current_time,
1579
bool *const mandos_client_exited,
1580
bool *const password_is_read,
1581
const char *const dirname){
1582
__attribute__((cleanup(cleanup_string)))
1583
char *dir_pattern = NULL;
1584
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1585
if(ret < 0 or dir_pattern == NULL){
1586
error(0, errno, "Could not create glob pattern for directory %s",
1590
__attribute__((cleanup(globfree)))
1591
glob_t question_filenames = {};
1592
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1593
NULL, &question_filenames)){
1596
error(0, errno, "Failed to open directory %s", dirname);
1599
error(0, errno, "There are no question files in %s", dirname);
1602
error(0, errno, "Could not allocate memory for question file"
1603
" names in %s", dirname);
1607
__attribute__((fallthrough));
1610
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1611
char *const question_filename = strdup(question_filenames
1613
const task_context task = {
1614
.func=open_and_parse_question,
1616
.question_filename=question_filename,
1617
.filename=question_filename,
1619
.cancelled_filenames=cancelled_filenames,
1620
.current_time=current_time,
1621
.mandos_client_exited=mandos_client_exited,
1622
.password_is_read=password_is_read,
1625
if(question_filename == NULL
1626
or not add_to_queue(queue, task)){
1627
error(0, errno, "Failed to add open_and_parse_question for"
1628
" file %s to queue",
1629
question_filenames.gl_pathv[i]);
1630
free(question_filename);
1632
queue->next_run = 1;
1639
__attribute__((nonnull, warn_unused_result))
1640
bool wait_for_event(const int epoll_fd,
1641
const mono_microsecs queue_next_run,
1642
const mono_microsecs current_time){
1643
__attribute__((const))
1644
int milliseconds_to_wait(const mono_microsecs currtime,
1645
const mono_microsecs nextrun){
1646
if(currtime >= nextrun){
1649
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1650
if(wait_time_ms > (uintmax_t)INT_MAX){
1653
return (int)wait_time_ms;
1656
const int wait_time_ms = milliseconds_to_wait(current_time,
1659
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1660
sigset_t temporary_unblocked_sigmask;
1661
/* Get current signal mask */
1662
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1665
/* Remove SIGCHLD from the signal mask */
1666
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1669
struct epoll_event events[8]; /* Ignored */
1670
int ret = epoll_pwait(epoll_fd, events,
1671
sizeof(events) / sizeof(struct epoll_event),
1672
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1673
&temporary_unblocked_sigmask);
1674
if(ret < 0 and errno != EINTR){
1675
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1677
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1680
return clear_all_fds_from_epoll_set(epoll_fd);
1683
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1684
/* Create a new empty epoll set */
1685
__attribute__((cleanup(cleanup_close)))
1686
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1687
if(new_epoll_fd < 0){
1690
/* dup3() the new epoll set fd over the old one, replacing it */
1691
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1697
__attribute__((nonnull, warn_unused_result))
1698
bool run_queue(task_queue **const queue,
1699
string_set *const cancelled_filenames,
1700
bool *const quit_now){
1702
task_queue *new_queue = create_queue();
1703
if(new_queue == NULL){
1707
__attribute__((cleanup(string_set_clear)))
1708
string_set old_cancelled_filenames = {};
1709
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1711
/* Declare i outside the for loop, since we might need i after the
1712
loop in case we aborted in the middle */
1714
for(i=0; i < (*queue)->length and not *quit_now; i++){
1715
task_context *const task = &((*queue)->tasks[i]);
1716
const char *const question_filename = task->question_filename;
1717
/* Skip any task referencing a cancelled question filename */
1718
if(question_filename != NULL
1719
and string_set_contains(old_cancelled_filenames,
1720
question_filename)){
1724
task->func(*task, new_queue);
1728
/* we might be in the middle of the queue, so clean up any
1729
remaining tasks in the current queue */
1730
for(; i < (*queue)->length; i++){
1731
cleanup_task(&((*queue)->tasks[i]));
1745
/* End of regular code section */
1747
/* Start of tests section; here are the tests for the above code */
1749
/* This "fixture" data structure is used by the test setup and
1750
teardown functions */
1752
struct sigaction orig_sigaction;
1753
sigset_t orig_sigmask;
1756
static void test_setup(test_fixture *fixture,
1757
__attribute__((unused))
1758
gconstpointer user_data){
1759
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1760
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1763
static void test_teardown(test_fixture *fixture,
1764
__attribute__((unused))
1765
gconstpointer user_data){
1766
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1767
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1770
/* Utility function used by tests to search queue for matching task */
1771
__attribute__((pure, nonnull, warn_unused_result))
1772
static task_context *find_matching_task(const task_queue *const queue,
1773
const task_context task){
1774
/* The argument "task" structure is a pattern to match; 0 in any
1775
member means any value matches, otherwise the value must match.
1776
The filename strings are compared by strcmp(), not by pointer. */
1777
for(size_t i = 0; i < queue->length; i++){
1778
task_context *const current_task = queue->tasks+i;
1779
/* Check all members of task_context, if set to a non-zero value.
1780
If a member does not match, continue to next task in queue */
1782
/* task_func *const func */
1783
if(task.func != NULL and current_task->func != task.func){
1786
/* char *const question_filename; */
1787
if(task.question_filename != NULL
1788
and (current_task->question_filename == NULL
1789
or strcmp(current_task->question_filename,
1790
task.question_filename) != 0)){
1793
/* const pid_t pid; */
1794
if(task.pid != 0 and current_task->pid != task.pid){
1797
/* const int epoll_fd; */
1798
if(task.epoll_fd != 0
1799
and current_task->epoll_fd != task.epoll_fd){
1802
/* bool *const quit_now; */
1803
if(task.quit_now != NULL
1804
and current_task->quit_now != task.quit_now){
1808
if(task.fd != 0 and current_task->fd != task.fd){
1811
/* bool *const mandos_client_exited; */
1812
if(task.mandos_client_exited != NULL
1813
and current_task->mandos_client_exited
1814
!= task.mandos_client_exited){
1817
/* buffer *const password; */
1818
if(task.password != NULL
1819
and current_task->password != task.password){
1822
/* bool *const password_is_read; */
1823
if(task.password_is_read != NULL
1824
and current_task->password_is_read != task.password_is_read){
1827
/* char *filename; */
1828
if(task.filename != NULL
1829
and (current_task->filename == NULL
1830
or strcmp(current_task->filename, task.filename) != 0)){
1833
/* string_set *const cancelled_filenames; */
1834
if(task.cancelled_filenames != NULL
1835
and current_task->cancelled_filenames
1836
!= task.cancelled_filenames){
1839
/* const mono_microsecs notafter; */
1840
if(task.notafter != 0
1841
and current_task->notafter != task.notafter){
1844
/* const mono_microsecs *const current_time; */
1845
if(task.current_time != NULL
1846
and current_task->current_time != task.current_time){
1849
/* Current task matches all members; return it */
1850
return current_task;
1852
/* No task in queue matches passed pattern task */
1856
static void test_create_queue(__attribute__((unused))
1857
test_fixture *fixture,
1858
__attribute__((unused))
1859
gconstpointer user_data){
1860
__attribute__((cleanup(cleanup_queue)))
1861
task_queue *const queue = create_queue();
1862
g_assert_nonnull(queue);
1863
g_assert_null(queue->tasks);
1864
g_assert_true(queue->length == 0);
1865
g_assert_true(queue->next_run == 0);
1868
static task_func dummy_func;
1870
static void test_add_to_queue(__attribute__((unused))
1871
test_fixture *fixture,
1872
__attribute__((unused))
1873
gconstpointer user_data){
1874
__attribute__((cleanup(cleanup_queue)))
1875
task_queue *queue = create_queue();
1876
g_assert_nonnull(queue);
1878
g_assert_true(add_to_queue(queue,
1879
(task_context){ .func=dummy_func }));
1880
g_assert_true(queue->length == 1);
1881
g_assert_nonnull(queue->tasks);
1882
g_assert_true(queue->tasks[0].func == dummy_func);
1885
static void dummy_func(__attribute__((unused))
1886
const task_context task,
1887
__attribute__((unused))
1888
task_queue *const queue){
1891
static void test_queue_has_question_empty(__attribute__((unused))
1892
test_fixture *fixture,
1893
__attribute__((unused))
1894
gconstpointer user_data){
1895
__attribute__((cleanup(cleanup_queue)))
1896
task_queue *queue = create_queue();
1897
g_assert_nonnull(queue);
1898
g_assert_false(queue_has_question(queue));
1901
static void test_queue_has_question_false(__attribute__((unused))
1902
test_fixture *fixture,
1903
__attribute__((unused))
1904
gconstpointer user_data){
1905
__attribute__((cleanup(cleanup_queue)))
1906
task_queue *queue = create_queue();
1907
g_assert_nonnull(queue);
1908
g_assert_true(add_to_queue(queue,
1909
(task_context){ .func=dummy_func }));
1910
g_assert_false(queue_has_question(queue));
1913
static void test_queue_has_question_true(__attribute__((unused))
1914
test_fixture *fixture,
1915
__attribute__((unused))
1916
gconstpointer user_data){
1917
__attribute__((cleanup(cleanup_queue)))
1918
task_queue *queue = create_queue();
1919
g_assert_nonnull(queue);
1920
char *const question_filename
1921
= strdup("/nonexistent/question_filename");
1922
g_assert_nonnull(question_filename);
1923
task_context task = {
1925
.question_filename=question_filename,
1927
g_assert_true(add_to_queue(queue, task));
1928
g_assert_true(queue_has_question(queue));
1931
static void test_queue_has_question_false2(__attribute__((unused))
1932
test_fixture *fixture,
1933
__attribute__((unused))
1934
gconstpointer user_data){
1935
__attribute__((cleanup(cleanup_queue)))
1936
task_queue *queue = create_queue();
1937
g_assert_nonnull(queue);
1938
task_context task = { .func=dummy_func };
1939
g_assert_true(add_to_queue(queue, task));
1940
g_assert_true(add_to_queue(queue, task));
1941
g_assert_cmpint((int)queue->length, ==, 2);
1942
g_assert_false(queue_has_question(queue));
1945
static void test_queue_has_question_true2(__attribute__((unused))
1946
test_fixture *fixture,
1947
__attribute__((unused))
1948
gconstpointer user_data){
1949
__attribute__((cleanup(cleanup_queue)))
1950
task_queue *queue = create_queue();
1951
g_assert_nonnull(queue);
1952
task_context task1 = { .func=dummy_func };
1953
g_assert_true(add_to_queue(queue, task1));
1954
char *const question_filename
1955
= strdup("/nonexistent/question_filename");
1956
g_assert_nonnull(question_filename);
1957
task_context task2 = {
1959
.question_filename=question_filename,
1961
g_assert_true(add_to_queue(queue, task2));
1962
g_assert_cmpint((int)queue->length, ==, 2);
1963
g_assert_true(queue_has_question(queue));
1966
static void test_cleanup_buffer(__attribute__((unused))
1967
test_fixture *fixture,
1968
__attribute__((unused))
1969
gconstpointer user_data){
1972
const size_t buffersize = 10;
1974
buf.data = malloc(buffersize);
1975
g_assert_nonnull(buf.data);
1976
if(mlock(buf.data, buffersize) != 0){
1977
g_assert_true(errno == EPERM or errno == ENOMEM);
1980
cleanup_buffer(&buf);
1981
g_assert_null(buf.data);
1985
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1986
test_fixture *fixture,
1987
__attribute__((unused))
1990
__attribute__((cleanup(string_set_clear)))
1991
string_set set = {};
1992
g_assert_false(string_set_contains(set, "")); /* Empty string */
1993
g_assert_false(string_set_contains(set, "test_string"));
1997
test_string_set_with_added_string_contains_it(__attribute__((unused))
1998
test_fixture *fixture,
1999
__attribute__((unused))
2002
__attribute__((cleanup(string_set_clear)))
2003
string_set set = {};
2004
g_assert_true(string_set_add(&set, "test_string"));
2005
g_assert_true(string_set_contains(set, "test_string"));
2009
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2010
test_fixture *fixture,
2011
__attribute__((unused))
2012
gconstpointer user_data){
2013
__attribute__((cleanup(string_set_clear)))
2014
string_set set = {};
2015
g_assert_true(string_set_add(&set, "test_string"));
2016
string_set_clear(&set);
2017
g_assert_false(string_set_contains(set, "test_string"));
2021
void test_string_set_swap_one_with_empty(__attribute__((unused))
2022
test_fixture *fixture,
2023
__attribute__((unused))
2024
gconstpointer user_data){
2025
__attribute__((cleanup(string_set_clear)))
2026
string_set set1 = {};
2027
__attribute__((cleanup(string_set_clear)))
2028
string_set set2 = {};
2029
g_assert_true(string_set_add(&set1, "test_string1"));
2030
string_set_swap(&set1, &set2);
2031
g_assert_false(string_set_contains(set1, "test_string1"));
2032
g_assert_true(string_set_contains(set2, "test_string1"));
2036
void test_string_set_swap_empty_with_one(__attribute__((unused))
2037
test_fixture *fixture,
2038
__attribute__((unused))
2039
gconstpointer user_data){
2040
__attribute__((cleanup(string_set_clear)))
2041
string_set set1 = {};
2042
__attribute__((cleanup(string_set_clear)))
2043
string_set set2 = {};
2044
g_assert_true(string_set_add(&set2, "test_string2"));
2045
string_set_swap(&set1, &set2);
2046
g_assert_true(string_set_contains(set1, "test_string2"));
2047
g_assert_false(string_set_contains(set2, "test_string2"));
2050
static void test_string_set_swap_one_with_one(__attribute__((unused))
2051
test_fixture *fixture,
2052
__attribute__((unused))
2055
__attribute__((cleanup(string_set_clear)))
2056
string_set set1 = {};
2057
__attribute__((cleanup(string_set_clear)))
2058
string_set set2 = {};
2059
g_assert_true(string_set_add(&set1, "test_string1"));
2060
g_assert_true(string_set_add(&set2, "test_string2"));
2061
string_set_swap(&set1, &set2);
2062
g_assert_false(string_set_contains(set1, "test_string1"));
2063
g_assert_true(string_set_contains(set1, "test_string2"));
2064
g_assert_false(string_set_contains(set2, "test_string2"));
2065
g_assert_true(string_set_contains(set2, "test_string1"));
2068
static bool fd_has_cloexec_and_nonblock(const int);
2070
static bool epoll_set_contains(int, int, uint32_t);
2072
static void test_start_mandos_client(test_fixture *fixture,
2073
__attribute__((unused))
2074
gconstpointer user_data){
2076
bool mandos_client_exited = false;
2077
bool quit_now = false;
2078
__attribute__((cleanup(cleanup_close)))
2079
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2080
g_assert_cmpint(epoll_fd, >=, 0);
2081
__attribute__((cleanup(cleanup_queue)))
2082
task_queue *queue = create_queue();
2083
g_assert_nonnull(queue);
2084
buffer password = {};
2085
bool password_is_read = false;
2086
const char helper_directory[] = "/nonexistent";
2087
const char *const argv[] = { "/bin/true", NULL };
2089
g_assert_true(start_mandos_client(queue, epoll_fd,
2090
&mandos_client_exited, &quit_now,
2091
&password, &password_is_read,
2092
&fixture->orig_sigaction,
2093
fixture->orig_sigmask,
2094
helper_directory, 0, 0, argv));
2096
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2098
const task_context *const added_wait_task
2099
= find_matching_task(queue, (task_context){
2100
.func=wait_for_mandos_client_exit,
2101
.mandos_client_exited=&mandos_client_exited,
2102
.quit_now=&quit_now,
2104
g_assert_nonnull(added_wait_task);
2105
g_assert_cmpint(added_wait_task->pid, >, 0);
2106
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2107
waitpid(added_wait_task->pid, NULL, 0);
2109
const task_context *const added_read_task
2110
= find_matching_task(queue, (task_context){
2111
.func=read_mandos_client_output,
2113
.password=&password,
2114
.password_is_read=&password_is_read,
2115
.quit_now=&quit_now,
2117
g_assert_nonnull(added_read_task);
2118
g_assert_cmpint(added_read_task->fd, >, 2);
2119
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2120
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2121
EPOLLIN | EPOLLRDHUP));
2124
static bool fd_has_cloexec_and_nonblock(const int fd){
2125
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2126
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2127
return ((socket_fd_flags >= 0)
2128
and (socket_fd_flags & FD_CLOEXEC)
2129
and (socket_file_flags >= 0)
2130
and (socket_file_flags & O_NONBLOCK));
2133
__attribute__((const))
2134
bool is_privileged(void){
2135
uid_t user = getuid() + 1;
2136
if(user == 0){ /* Overflow check */
2139
gid_t group = getuid() + 1;
2140
if(group == 0){ /* Overflow check */
2143
const pid_t pid = fork();
2144
if(pid == 0){ /* Child */
2145
if(setresgid((uid_t)-1, group, group) == -1){
2147
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2148
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2152
if(setresuid((uid_t)-1, user, user) == -1){
2154
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2155
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2162
waitpid(pid, &status, 0);
2163
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2169
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2170
/* Only scan for events in this eventmask */
2171
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2172
__attribute__((cleanup(cleanup_string)))
2173
char *fdinfo_name = NULL;
2174
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2175
g_assert_cmpint(ret, >, 0);
2176
g_assert_nonnull(fdinfo_name);
2178
FILE *fdinfo = fopen(fdinfo_name, "r");
2179
g_assert_nonnull(fdinfo);
2180
uint32_t reported_events;
2185
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2188
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2189
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2190
&found_fd, &reported_events) == 2){
2195
} while(not feof(fdinfo) and not ferror(fdinfo));
2196
g_assert_cmpint(fclose(fdinfo), ==, 0);
2203
/* Don't check events if none are given */
2206
return (reported_events & eventmask) == (events & eventmask);
2209
static void test_start_mandos_client_execv(test_fixture *fixture,
2210
__attribute__((unused))
2211
gconstpointer user_data){
2212
bool mandos_client_exited = false;
2213
bool quit_now = false;
2214
__attribute__((cleanup(cleanup_close)))
2215
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2216
g_assert_cmpint(epoll_fd, >=, 0);
2217
__attribute__((cleanup(cleanup_queue)))
2218
task_queue *queue = create_queue();
2219
g_assert_nonnull(queue);
2220
__attribute__((cleanup(cleanup_buffer)))
2221
buffer password = {};
2222
const char helper_directory[] = "/nonexistent";
2223
/* Can't execv("/", ...), so this should fail */
2224
const char *const argv[] = { "/", NULL };
2227
__attribute__((cleanup(cleanup_close)))
2228
const int devnull_fd = open("/dev/null",
2229
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2230
g_assert_cmpint(devnull_fd, >=, 0);
2231
__attribute__((cleanup(cleanup_close)))
2232
const int real_stderr_fd = dup(STDERR_FILENO);
2233
g_assert_cmpint(real_stderr_fd, >=, 0);
2234
dup2(devnull_fd, STDERR_FILENO);
2236
const bool success = start_mandos_client(queue, epoll_fd,
2237
&mandos_client_exited,
2241
&fixture->orig_sigaction,
2242
fixture->orig_sigmask,
2243
helper_directory, 0, 0,
2245
dup2(real_stderr_fd, STDERR_FILENO);
2246
g_assert_true(success);
2248
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2250
struct timespec starttime, currtime;
2251
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2253
queue->next_run = 0;
2254
string_set cancelled_filenames = {};
2257
__attribute__((cleanup(cleanup_close)))
2258
const int devnull_fd = open("/dev/null",
2259
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2260
g_assert_cmpint(devnull_fd, >=, 0);
2261
__attribute__((cleanup(cleanup_close)))
2262
const int real_stderr_fd = dup(STDERR_FILENO);
2263
g_assert_cmpint(real_stderr_fd, >=, 0);
2264
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2265
dup2(devnull_fd, STDERR_FILENO);
2266
const bool success = run_queue(&queue, &cancelled_filenames,
2268
dup2(real_stderr_fd, STDERR_FILENO);
2273
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2274
} while(((queue->length) > 0)
2276
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2278
g_assert_true(quit_now);
2279
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2280
g_assert_true(mandos_client_exited);
2283
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2284
__attribute__((unused))
2287
if(not is_privileged()){
2288
g_test_skip("Not privileged");
2292
bool mandos_client_exited = false;
2293
bool quit_now = false;
2294
__attribute__((cleanup(cleanup_close)))
2295
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2296
g_assert_cmpint(epoll_fd, >=, 0);
2297
__attribute__((cleanup(cleanup_queue)))
2298
task_queue *queue = create_queue();
2299
g_assert_nonnull(queue);
2300
__attribute__((cleanup(cleanup_buffer)))
2301
buffer password = {};
2302
bool password_is_read = false;
2303
const char helper_directory[] = "/nonexistent";
2304
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2308
const bool success = start_mandos_client(queue, epoll_fd,
2309
&mandos_client_exited,
2310
&quit_now, &password,
2312
&fixture->orig_sigaction,
2313
fixture->orig_sigmask,
2314
helper_directory, user,
2316
g_assert_true(success);
2317
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2319
struct timespec starttime, currtime;
2320
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2322
queue->next_run = 0;
2323
string_set cancelled_filenames = {};
2324
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2325
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2326
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2327
} while(((queue->length) > 0)
2329
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2331
g_assert_false(quit_now);
2332
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2333
g_assert_true(mandos_client_exited);
2335
g_assert_true(password_is_read);
2336
g_assert_nonnull(password.data);
2339
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2341
g_assert_true((uid_t)id == id);
2343
g_assert_cmpuint((unsigned int)id, ==, 0);
2346
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2347
__attribute__((unused))
2350
if(not is_privileged()){
2351
g_test_skip("Not privileged");
2355
bool mandos_client_exited = false;
2356
bool quit_now = false;
2357
__attribute__((cleanup(cleanup_close)))
2358
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2359
g_assert_cmpint(epoll_fd, >=, 0);
2360
__attribute__((cleanup(cleanup_queue)))
2361
task_queue *queue = create_queue();
2362
g_assert_nonnull(queue);
2363
__attribute__((cleanup(cleanup_buffer)))
2364
buffer password = {};
2365
bool password_is_read = false;
2366
const char helper_directory[] = "/nonexistent";
2367
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2371
const bool success = start_mandos_client(queue, epoll_fd,
2372
&mandos_client_exited,
2373
&quit_now, &password,
2375
&fixture->orig_sigaction,
2376
fixture->orig_sigmask,
2377
helper_directory, user,
2379
g_assert_true(success);
2380
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2382
struct timespec starttime, currtime;
2383
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2385
queue->next_run = 0;
2386
string_set cancelled_filenames = {};
2387
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2388
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2389
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2390
} while(((queue->length) > 0)
2392
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2394
g_assert_false(quit_now);
2395
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2396
g_assert_true(mandos_client_exited);
2398
g_assert_true(password_is_read);
2399
g_assert_nonnull(password.data);
2402
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2404
g_assert_true((gid_t)id == id);
2406
g_assert_cmpuint((unsigned int)id, ==, 0);
2409
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2410
__attribute__((unused))
2413
if(not is_privileged()){
2414
g_test_skip("Not privileged");
2418
bool mandos_client_exited = false;
2419
bool quit_now = false;
2420
__attribute__((cleanup(cleanup_close)))
2421
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2422
g_assert_cmpint(epoll_fd, >=, 0);
2423
__attribute__((cleanup(cleanup_queue)))
2424
task_queue *queue = create_queue();
2425
g_assert_nonnull(queue);
2426
__attribute__((cleanup(cleanup_buffer)))
2427
buffer password = {};
2428
bool password_is_read = false;
2429
const char helper_directory[] = "/nonexistent";
2430
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2435
const bool success = start_mandos_client(queue, epoll_fd,
2436
&mandos_client_exited,
2437
&quit_now, &password,
2439
&fixture->orig_sigaction,
2440
fixture->orig_sigmask,
2441
helper_directory, user,
2443
g_assert_true(success);
2444
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2446
struct timespec starttime, currtime;
2447
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2449
queue->next_run = 0;
2450
string_set cancelled_filenames = {};
2451
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2452
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2453
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2454
} while(((queue->length) > 0)
2456
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2458
g_assert_false(quit_now);
2459
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2460
g_assert_true(mandos_client_exited);
2462
g_assert_true(password_is_read);
2463
g_assert_nonnull(password.data);
2466
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2468
g_assert_true((uid_t)id == id);
2470
g_assert_cmpuint((unsigned int)id, ==, user);
2473
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2474
__attribute__((unused))
2477
if(not is_privileged()){
2478
g_test_skip("Not privileged");
2482
bool mandos_client_exited = false;
2483
bool quit_now = false;
2484
__attribute__((cleanup(cleanup_close)))
2485
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2486
g_assert_cmpint(epoll_fd, >=, 0);
2487
__attribute__((cleanup(cleanup_queue)))
2488
task_queue *queue = create_queue();
2489
g_assert_nonnull(queue);
2490
__attribute__((cleanup(cleanup_buffer)))
2491
buffer password = {};
2492
bool password_is_read = false;
2493
const char helper_directory[] = "/nonexistent";
2494
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2499
const bool success = start_mandos_client(queue, epoll_fd,
2500
&mandos_client_exited,
2501
&quit_now, &password,
2503
&fixture->orig_sigaction,
2504
fixture->orig_sigmask,
2505
helper_directory, user,
2507
g_assert_true(success);
2508
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2510
struct timespec starttime, currtime;
2511
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2513
queue->next_run = 0;
2514
string_set cancelled_filenames = {};
2515
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2516
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2517
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2518
} while(((queue->length) > 0)
2520
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2522
g_assert_false(quit_now);
2523
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2524
g_assert_true(mandos_client_exited);
2526
g_assert_true(password_is_read);
2527
g_assert_nonnull(password.data);
2530
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2532
g_assert_true((gid_t)id == id);
2534
g_assert_cmpuint((unsigned int)id, ==, group);
2537
static void test_start_mandos_client_read(test_fixture *fixture,
2538
__attribute__((unused))
2539
gconstpointer user_data){
2540
bool mandos_client_exited = false;
2541
bool quit_now = false;
2542
__attribute__((cleanup(cleanup_close)))
2543
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2544
g_assert_cmpint(epoll_fd, >=, 0);
2545
__attribute__((cleanup(cleanup_queue)))
2546
task_queue *queue = create_queue();
2547
g_assert_nonnull(queue);
2548
__attribute__((cleanup(cleanup_buffer)))
2549
buffer password = {};
2550
bool password_is_read = false;
2551
const char dummy_test_password[] = "dummy test password";
2552
const char helper_directory[] = "/nonexistent";
2553
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2556
const bool success = start_mandos_client(queue, epoll_fd,
2557
&mandos_client_exited,
2558
&quit_now, &password,
2560
&fixture->orig_sigaction,
2561
fixture->orig_sigmask,
2562
helper_directory, 0, 0,
2564
g_assert_true(success);
2565
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2567
struct timespec starttime, currtime;
2568
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2570
queue->next_run = 0;
2571
string_set cancelled_filenames = {};
2572
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2573
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2574
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2575
} while(((queue->length) > 0)
2577
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2579
g_assert_false(quit_now);
2580
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2581
g_assert_true(mandos_client_exited);
2583
g_assert_true(password_is_read);
2584
g_assert_cmpint((int)password.length, ==,
2585
sizeof(dummy_test_password)-1);
2586
g_assert_nonnull(password.data);
2587
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2588
sizeof(dummy_test_password)-1), ==, 0);
2592
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2593
__attribute__((unused))
2596
bool mandos_client_exited = false;
2597
bool quit_now = false;
2598
__attribute__((cleanup(cleanup_close)))
2599
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2600
g_assert_cmpint(epoll_fd, >=, 0);
2601
__attribute__((cleanup(cleanup_queue)))
2602
task_queue *queue = create_queue();
2603
g_assert_nonnull(queue);
2604
__attribute__((cleanup(cleanup_buffer)))
2605
buffer password = {};
2606
bool password_is_read = false;
2607
const char helper_directory[] = "/nonexistent";
2608
const char *const argv[] = { "/bin/sh", "-c",
2609
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2611
const bool success = start_mandos_client(queue, epoll_fd,
2612
&mandos_client_exited,
2613
&quit_now, &password,
2615
&fixture->orig_sigaction,
2616
fixture->orig_sigmask,
2617
helper_directory, 0, 0,
2619
g_assert_true(success);
2620
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2622
struct timespec starttime, currtime;
2623
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2625
queue->next_run = 0;
2626
string_set cancelled_filenames = {};
2627
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2628
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2629
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2630
} while(((queue->length) > 0)
2632
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2634
g_assert_false(quit_now);
2635
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2636
g_assert_true(mandos_client_exited);
2638
g_assert_true(password_is_read);
2639
g_assert_cmpint((int)password.length, ==,
2640
sizeof(helper_directory)-1);
2641
g_assert_nonnull(password.data);
2642
g_assert_cmpint(memcmp(helper_directory, password.data,
2643
sizeof(helper_directory)-1), ==, 0);
2646
__attribute__((nonnull, warn_unused_result))
2647
static bool proc_status_sigblk_to_sigset(const char *const,
2650
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2651
__attribute__((unused))
2652
gconstpointer user_data){
2653
bool mandos_client_exited = false;
2654
bool quit_now = false;
2655
__attribute__((cleanup(cleanup_close)))
2656
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2657
g_assert_cmpint(epoll_fd, >=, 0);
2658
__attribute__((cleanup(cleanup_queue)))
2659
task_queue *queue = create_queue();
2660
g_assert_nonnull(queue);
2661
__attribute__((cleanup(cleanup_buffer)))
2662
buffer password = {};
2663
bool password_is_read = false;
2664
const char helper_directory[] = "/nonexistent";
2665
/* see proc(5) for format of /proc/self/status */
2666
const char *const argv[] = { "/usr/bin/awk",
2667
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2669
g_assert_true(start_mandos_client(queue, epoll_fd,
2670
&mandos_client_exited, &quit_now,
2671
&password, &password_is_read,
2672
&fixture->orig_sigaction,
2673
fixture->orig_sigmask,
2674
helper_directory, 0, 0, argv));
2676
struct timespec starttime, currtime;
2677
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2679
queue->next_run = 0;
2680
string_set cancelled_filenames = {};
2681
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2682
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2683
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2684
} while((not (mandos_client_exited and password_is_read))
2686
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2687
g_assert_true(mandos_client_exited);
2688
g_assert_true(password_is_read);
2690
sigset_t parsed_sigmask;
2691
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2694
for(int signum = 1; signum < NSIG; signum++){
2695
const bool has_signal = sigismember(&parsed_sigmask, signum);
2696
if(sigismember(&fixture->orig_sigmask, signum)){
2697
g_assert_true(has_signal);
2699
g_assert_false(has_signal);
2704
__attribute__((nonnull, warn_unused_result))
2705
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2706
sigset_t *const sigmask){
2707
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2708
uintmax_t scanned_sigmask;
2709
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2712
if(sigemptyset(sigmask) != 0){
2715
for(int signum = 1; signum < NSIG; signum++){
2716
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2717
if(sigaddset(sigmask, signum) != 0){
2725
static void run_task_with_stderr_to_dev_null(const task_context task,
2726
task_queue *const queue);
2729
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2730
test_fixture *fixture,
2731
__attribute__((unused))
2732
gconstpointer user_data){
2734
bool mandos_client_exited = false;
2735
bool quit_now = false;
2737
__attribute__((cleanup(cleanup_queue)))
2738
task_queue *queue = create_queue();
2739
g_assert_nonnull(queue);
2740
const task_context task = {
2741
.func=wait_for_mandos_client_exit,
2743
.mandos_client_exited=&mandos_client_exited,
2744
.quit_now=&quit_now,
2746
run_task_with_stderr_to_dev_null(task, queue);
2748
g_assert_false(mandos_client_exited);
2749
g_assert_true(quit_now);
2750
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2753
static void run_task_with_stderr_to_dev_null(const task_context task,
2754
task_queue *const queue){
2755
FILE *real_stderr = stderr;
2756
FILE *devnull = fopen("/dev/null", "we");
2757
g_assert_nonnull(devnull);
2760
task.func(task, queue);
2761
stderr = real_stderr;
2763
g_assert_cmpint(fclose(devnull), ==, 0);
2767
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2768
__attribute__((unused))
2769
gconstpointer user_data){
2770
bool mandos_client_exited = false;
2771
bool quit_now = false;
2773
pid_t create_eternal_process(void){
2774
const pid_t pid = fork();
2775
if(pid == 0){ /* Child */
2776
if(not restore_signal_handler(&fixture->orig_sigaction)){
2777
_exit(EXIT_FAILURE);
2779
if(not restore_sigmask(&fixture->orig_sigmask)){
2780
_exit(EXIT_FAILURE);
2788
pid_t pid = create_eternal_process();
2789
g_assert_true(pid != -1);
2791
__attribute__((cleanup(cleanup_queue)))
2792
task_queue *queue = create_queue();
2793
g_assert_nonnull(queue);
2794
const task_context task = {
2795
.func=wait_for_mandos_client_exit,
2797
.mandos_client_exited=&mandos_client_exited,
2798
.quit_now=&quit_now,
2800
task.func(task, queue);
2802
g_assert_false(mandos_client_exited);
2803
g_assert_false(quit_now);
2804
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2806
g_assert_nonnull(find_matching_task(queue, (task_context){
2807
.func=wait_for_mandos_client_exit,
2809
.mandos_client_exited=&mandos_client_exited,
2810
.quit_now=&quit_now,
2815
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2816
__attribute__((unused))
2819
bool mandos_client_exited = false;
2820
bool quit_now = false;
2822
pid_t create_successful_process(void){
2823
const pid_t pid = fork();
2824
if(pid == 0){ /* Child */
2825
if(not restore_signal_handler(&fixture->orig_sigaction)){
2826
_exit(EXIT_FAILURE);
2828
if(not restore_sigmask(&fixture->orig_sigmask)){
2829
_exit(EXIT_FAILURE);
2835
const pid_t pid = create_successful_process();
2836
g_assert_true(pid != -1);
2838
__attribute__((cleanup(cleanup_queue)))
2839
task_queue *queue = create_queue();
2840
g_assert_nonnull(queue);
2841
const task_context initial_task = {
2842
.func=wait_for_mandos_client_exit,
2844
.mandos_client_exited=&mandos_client_exited,
2845
.quit_now=&quit_now,
2847
g_assert_true(add_to_queue(queue, initial_task));
2849
struct timespec starttime, currtime;
2850
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2851
__attribute__((cleanup(cleanup_close)))
2852
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2854
queue->next_run = 0;
2855
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2856
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2857
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2858
} while((not mandos_client_exited)
2860
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2862
g_assert_true(mandos_client_exited);
2863
g_assert_false(quit_now);
2864
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2868
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2869
__attribute__((unused))
2872
bool mandos_client_exited = false;
2873
bool quit_now = false;
2875
pid_t create_failing_process(void){
2876
const pid_t pid = fork();
2877
if(pid == 0){ /* Child */
2878
if(not restore_signal_handler(&fixture->orig_sigaction)){
2879
_exit(EXIT_FAILURE);
2881
if(not restore_sigmask(&fixture->orig_sigmask)){
2882
_exit(EXIT_FAILURE);
2888
const pid_t pid = create_failing_process();
2889
g_assert_true(pid != -1);
2891
__attribute__((cleanup(string_set_clear)))
2892
string_set cancelled_filenames = {};
2893
__attribute__((cleanup(cleanup_close)))
2894
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2895
g_assert_cmpint(epoll_fd, >=, 0);
2896
__attribute__((cleanup(cleanup_queue)))
2897
task_queue *queue = create_queue();
2898
g_assert_nonnull(queue);
2899
g_assert_true(add_to_queue(queue, (task_context){
2900
.func=wait_for_mandos_client_exit,
2902
.mandos_client_exited=&mandos_client_exited,
2903
.quit_now=&quit_now,
2906
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2908
__attribute__((cleanup(cleanup_close)))
2909
const int devnull_fd = open("/dev/null",
2910
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2911
g_assert_cmpint(devnull_fd, >=, 0);
2912
__attribute__((cleanup(cleanup_close)))
2913
const int real_stderr_fd = dup(STDERR_FILENO);
2914
g_assert_cmpint(real_stderr_fd, >=, 0);
2916
struct timespec starttime, currtime;
2917
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2919
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2920
dup2(devnull_fd, STDERR_FILENO);
2921
const bool success = run_queue(&queue, &cancelled_filenames,
2923
dup2(real_stderr_fd, STDERR_FILENO);
2928
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2929
} while((not mandos_client_exited)
2931
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2933
g_assert_true(quit_now);
2934
g_assert_true(mandos_client_exited);
2935
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2939
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2940
__attribute__((unused))
2941
gconstpointer user_data){
2942
bool mandos_client_exited = false;
2943
bool quit_now = false;
2945
pid_t create_killed_process(void){
2946
const pid_t pid = fork();
2947
if(pid == 0){ /* Child */
2948
if(not restore_signal_handler(&fixture->orig_sigaction)){
2949
_exit(EXIT_FAILURE);
2951
if(not restore_sigmask(&fixture->orig_sigmask)){
2952
_exit(EXIT_FAILURE);
2961
const pid_t pid = create_killed_process();
2962
g_assert_true(pid != -1);
2964
__attribute__((cleanup(string_set_clear)))
2965
string_set cancelled_filenames = {};
2966
__attribute__((cleanup(cleanup_close)))
2967
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2968
g_assert_cmpint(epoll_fd, >=, 0);
2969
__attribute__((cleanup(cleanup_queue)))
2970
task_queue *queue = create_queue();
2971
g_assert_nonnull(queue);
2972
g_assert_true(add_to_queue(queue, (task_context){
2973
.func=wait_for_mandos_client_exit,
2975
.mandos_client_exited=&mandos_client_exited,
2976
.quit_now=&quit_now,
2979
__attribute__((cleanup(cleanup_close)))
2980
const int devnull_fd = open("/dev/null",
2981
O_WRONLY | O_CLOEXEC, O_NOCTTY);
2982
g_assert_cmpint(devnull_fd, >=, 0);
2983
__attribute__((cleanup(cleanup_close)))
2984
const int real_stderr_fd = dup(STDERR_FILENO);
2985
g_assert_cmpint(real_stderr_fd, >=, 0);
2987
struct timespec starttime, currtime;
2988
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2990
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2991
dup2(devnull_fd, STDERR_FILENO);
2992
const bool success = run_queue(&queue, &cancelled_filenames,
2994
dup2(real_stderr_fd, STDERR_FILENO);
2999
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3000
} while((not mandos_client_exited)
3002
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3004
g_assert_true(mandos_client_exited);
3005
g_assert_true(quit_now);
3006
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3009
static bool epoll_set_does_not_contain(int, int);
3012
void test_read_mandos_client_output_readerror(__attribute__((unused))
3013
test_fixture *fixture,
3014
__attribute__((unused))
3017
__attribute__((cleanup(cleanup_close)))
3018
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3019
g_assert_cmpint(epoll_fd, >=, 0);
3021
__attribute__((cleanup(cleanup_buffer)))
3022
buffer password = {};
3024
/* Reading /proc/self/mem from offset 0 will always give EIO */
3025
const int fd = open("/proc/self/mem",
3026
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3028
bool password_is_read = false;
3029
bool quit_now = false;
3030
__attribute__((cleanup(cleanup_queue)))
3031
task_queue *queue = create_queue();
3032
g_assert_nonnull(queue);
3034
task_context task = {
3035
.func=read_mandos_client_output,
3038
.password=&password,
3039
.password_is_read=&password_is_read,
3040
.quit_now=&quit_now,
3042
run_task_with_stderr_to_dev_null(task, queue);
3043
g_assert_false(password_is_read);
3044
g_assert_cmpint((int)password.length, ==, 0);
3045
g_assert_true(quit_now);
3046
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3048
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3050
g_assert_cmpint(close(fd), ==, -1);
3053
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3054
return not epoll_set_contains(epoll_fd, fd, 0);
3058
void test_read_mandos_client_output_nodata(__attribute__((unused))
3059
test_fixture *fixture,
3060
__attribute__((unused))
3061
gconstpointer user_data){
3062
__attribute__((cleanup(cleanup_close)))
3063
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3064
g_assert_cmpint(epoll_fd, >=, 0);
3067
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3069
__attribute__((cleanup(cleanup_buffer)))
3070
buffer password = {};
3072
bool password_is_read = false;
3073
bool quit_now = false;
3074
__attribute__((cleanup(cleanup_queue)))
3075
task_queue *queue = create_queue();
3076
g_assert_nonnull(queue);
3078
task_context task = {
3079
.func=read_mandos_client_output,
3082
.password=&password,
3083
.password_is_read=&password_is_read,
3084
.quit_now=&quit_now,
3086
task.func(task, queue);
3087
g_assert_false(password_is_read);
3088
g_assert_cmpint((int)password.length, ==, 0);
3089
g_assert_false(quit_now);
3090
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3092
g_assert_nonnull(find_matching_task(queue, (task_context){
3093
.func=read_mandos_client_output,
3096
.password=&password,
3097
.password_is_read=&password_is_read,
3098
.quit_now=&quit_now,
3101
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3102
EPOLLIN | EPOLLRDHUP));
3104
g_assert_cmpint(close(pipefds[1]), ==, 0);
3107
static void test_read_mandos_client_output_eof(__attribute__((unused))
3108
test_fixture *fixture,
3109
__attribute__((unused))
3112
__attribute__((cleanup(cleanup_close)))
3113
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3114
g_assert_cmpint(epoll_fd, >=, 0);
3117
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3118
g_assert_cmpint(close(pipefds[1]), ==, 0);
3120
__attribute__((cleanup(cleanup_buffer)))
3121
buffer password = {};
3123
bool password_is_read = false;
3124
bool quit_now = false;
3125
__attribute__((cleanup(cleanup_queue)))
3126
task_queue *queue = create_queue();
3127
g_assert_nonnull(queue);
3129
task_context task = {
3130
.func=read_mandos_client_output,
3133
.password=&password,
3134
.password_is_read=&password_is_read,
3135
.quit_now=&quit_now,
3137
task.func(task, queue);
3138
g_assert_true(password_is_read);
3139
g_assert_cmpint((int)password.length, ==, 0);
3140
g_assert_false(quit_now);
3141
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3143
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3145
g_assert_cmpint(close(pipefds[0]), ==, -1);
3149
void test_read_mandos_client_output_once(__attribute__((unused))
3150
test_fixture *fixture,
3151
__attribute__((unused))
3152
gconstpointer user_data){
3153
__attribute__((cleanup(cleanup_close)))
3154
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3155
g_assert_cmpint(epoll_fd, >=, 0);
3158
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3160
const char dummy_test_password[] = "dummy test password";
3161
/* Start with a pre-allocated buffer */
3162
__attribute__((cleanup(cleanup_buffer)))
3164
.data=malloc(sizeof(dummy_test_password)),
3166
.allocated=sizeof(dummy_test_password),
3168
g_assert_nonnull(password.data);
3169
if(mlock(password.data, password.allocated) != 0){
3170
g_assert_true(errno == EPERM or errno == ENOMEM);
3173
bool password_is_read = false;
3174
bool quit_now = false;
3175
__attribute__((cleanup(cleanup_queue)))
3176
task_queue *queue = create_queue();
3177
g_assert_nonnull(queue);
3179
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3180
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3181
sizeof(dummy_test_password)),
3182
==, (int)sizeof(dummy_test_password));
3184
task_context task = {
3185
.func=read_mandos_client_output,
3188
.password=&password,
3189
.password_is_read=&password_is_read,
3190
.quit_now=&quit_now,
3192
task.func(task, queue);
3194
g_assert_false(password_is_read);
3195
g_assert_cmpint((int)password.length, ==,
3196
(int)sizeof(dummy_test_password));
3197
g_assert_nonnull(password.data);
3198
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3199
sizeof(dummy_test_password)), ==, 0);
3201
g_assert_false(quit_now);
3202
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3204
g_assert_nonnull(find_matching_task(queue, (task_context){
3205
.func=read_mandos_client_output,
3208
.password=&password,
3209
.password_is_read=&password_is_read,
3210
.quit_now=&quit_now,
3213
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3214
EPOLLIN | EPOLLRDHUP));
3216
g_assert_cmpint(close(pipefds[1]), ==, 0);
3220
void test_read_mandos_client_output_malloc(__attribute__((unused))
3221
test_fixture *fixture,
3222
__attribute__((unused))
3223
gconstpointer user_data){
3224
__attribute__((cleanup(cleanup_close)))
3225
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3226
g_assert_cmpint(epoll_fd, >=, 0);
3229
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3231
const char dummy_test_password[] = "dummy test password";
3232
/* Start with an empty buffer */
3233
__attribute__((cleanup(cleanup_buffer)))
3234
buffer password = {};
3236
bool password_is_read = false;
3237
bool quit_now = false;
3238
__attribute__((cleanup(cleanup_queue)))
3239
task_queue *queue = create_queue();
3240
g_assert_nonnull(queue);
3242
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3243
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3244
sizeof(dummy_test_password)),
3245
==, (int)sizeof(dummy_test_password));
3247
task_context task = {
3248
.func=read_mandos_client_output,
3251
.password=&password,
3252
.password_is_read=&password_is_read,
3253
.quit_now=&quit_now,
3255
task.func(task, queue);
3257
g_assert_false(password_is_read);
3258
g_assert_cmpint((int)password.length, ==,
3259
(int)sizeof(dummy_test_password));
3260
g_assert_nonnull(password.data);
3261
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3262
sizeof(dummy_test_password)), ==, 0);
3264
g_assert_false(quit_now);
3265
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3267
g_assert_nonnull(find_matching_task(queue, (task_context){
3268
.func=read_mandos_client_output,
3271
.password=&password,
3272
.password_is_read=&password_is_read,
3273
.quit_now=&quit_now,
3276
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3277
EPOLLIN | EPOLLRDHUP));
3279
g_assert_cmpint(close(pipefds[1]), ==, 0);
3283
void test_read_mandos_client_output_append(__attribute__((unused))
3284
test_fixture *fixture,
3285
__attribute__((unused))
3286
gconstpointer user_data){
3287
__attribute__((cleanup(cleanup_close)))
3288
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3289
g_assert_cmpint(epoll_fd, >=, 0);
3292
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3294
const char dummy_test_password[] = "dummy test password";
3295
__attribute__((cleanup(cleanup_buffer)))
3297
.data=malloc(PIPE_BUF),
3299
.allocated=PIPE_BUF,
3301
g_assert_nonnull(password.data);
3302
if(mlock(password.data, password.allocated) != 0){
3303
g_assert_true(errno == EPERM or errno == ENOMEM);
3306
memset(password.data, 'x', PIPE_BUF);
3307
char password_expected[PIPE_BUF];
3308
memcpy(password_expected, password.data, PIPE_BUF);
3310
bool password_is_read = false;
3311
bool quit_now = false;
3312
__attribute__((cleanup(cleanup_queue)))
3313
task_queue *queue = create_queue();
3314
g_assert_nonnull(queue);
3316
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3317
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3318
sizeof(dummy_test_password)),
3319
==, (int)sizeof(dummy_test_password));
3321
task_context task = {
3322
.func=read_mandos_client_output,
3325
.password=&password,
3326
.password_is_read=&password_is_read,
3327
.quit_now=&quit_now,
3329
task.func(task, queue);
3331
g_assert_false(password_is_read);
3332
g_assert_cmpint((int)password.length, ==,
3333
PIPE_BUF + sizeof(dummy_test_password));
3334
g_assert_nonnull(password.data);
3335
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3337
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3338
dummy_test_password,
3339
sizeof(dummy_test_password)), ==, 0);
3340
g_assert_false(quit_now);
3341
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3343
g_assert_nonnull(find_matching_task(queue, (task_context){
3344
.func=read_mandos_client_output,
3347
.password=&password,
3348
.password_is_read=&password_is_read,
3349
.quit_now=&quit_now,
3352
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3353
EPOLLIN | EPOLLRDHUP));
3356
static char *make_temporary_directory(void);
3358
static void test_add_inotify_dir_watch(__attribute__((unused))
3359
test_fixture *fixture,
3360
__attribute__((unused))
3361
gconstpointer user_data){
3362
__attribute__((cleanup(cleanup_close)))
3363
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3364
g_assert_cmpint(epoll_fd, >=, 0);
3365
__attribute__((cleanup(cleanup_queue)))
3366
task_queue *queue = create_queue();
3367
g_assert_nonnull(queue);
3368
__attribute__((cleanup(string_set_clear)))
3369
string_set cancelled_filenames = {};
3370
const mono_microsecs current_time = 0;
3372
bool quit_now = false;
3373
buffer password = {};
3374
bool mandos_client_exited = false;
3375
bool password_is_read = false;
3377
__attribute__((cleanup(cleanup_string)))
3378
char *tempdir = make_temporary_directory();
3379
g_assert_nonnull(tempdir);
3381
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3383
&cancelled_filenames,
3385
&mandos_client_exited,
3386
&password_is_read));
3388
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3390
const task_context *const added_read_task
3391
= find_matching_task(queue, (task_context){
3392
.func=read_inotify_event,
3394
.quit_now=&quit_now,
3395
.password=&password,
3397
.cancelled_filenames=&cancelled_filenames,
3398
.current_time=¤t_time,
3399
.mandos_client_exited=&mandos_client_exited,
3400
.password_is_read=&password_is_read,
3402
g_assert_nonnull(added_read_task);
3404
g_assert_cmpint(added_read_task->fd, >, 2);
3405
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3406
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3407
added_read_task->fd,
3408
EPOLLIN | EPOLLRDHUP));
3410
g_assert_cmpint(rmdir(tempdir), ==, 0);
3413
static char *make_temporary_directory(void){
3414
char *name = strdup("/tmp/mandosXXXXXX");
3415
g_assert_nonnull(name);
3416
char *result = mkdtemp(name);
3423
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3424
test_fixture *fixture,
3425
__attribute__((unused))
3426
gconstpointer user_data){
3427
__attribute__((cleanup(cleanup_close)))
3428
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3429
g_assert_cmpint(epoll_fd, >=, 0);
3430
__attribute__((cleanup(cleanup_queue)))
3431
task_queue *queue = create_queue();
3432
g_assert_nonnull(queue);
3433
__attribute__((cleanup(string_set_clear)))
3434
string_set cancelled_filenames = {};
3435
const mono_microsecs current_time = 0;
3437
bool quit_now = false;
3438
buffer password = {};
3439
bool mandos_client_exited = false;
3440
bool password_is_read = false;
3442
const char nonexistent_dir[] = "/nonexistent";
3444
FILE *real_stderr = stderr;
3445
FILE *devnull = fopen("/dev/null", "we");
3446
g_assert_nonnull(devnull);
3448
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3449
&password, nonexistent_dir,
3450
&cancelled_filenames,
3452
&mandos_client_exited,
3453
&password_is_read));
3454
stderr = real_stderr;
3455
g_assert_cmpint(fclose(devnull), ==, 0);
3457
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3460
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3461
test_fixture *fixture,
3462
__attribute__((unused))
3465
__attribute__((cleanup(cleanup_close)))
3466
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3467
g_assert_cmpint(epoll_fd, >=, 0);
3468
__attribute__((cleanup(cleanup_queue)))
3469
task_queue *queue = create_queue();
3470
g_assert_nonnull(queue);
3471
__attribute__((cleanup(string_set_clear)))
3472
string_set cancelled_filenames = {};
3473
const mono_microsecs current_time = 0;
3475
bool quit_now = false;
3476
buffer password = {};
3477
bool mandos_client_exited = false;
3478
bool password_is_read = false;
3480
const char not_a_directory[] = "/dev/tty";
3482
FILE *real_stderr = stderr;
3483
FILE *devnull = fopen("/dev/null", "we");
3484
g_assert_nonnull(devnull);
3486
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3487
&password, not_a_directory,
3488
&cancelled_filenames,
3490
&mandos_client_exited,
3491
&password_is_read));
3492
stderr = real_stderr;
3493
g_assert_cmpint(fclose(devnull), ==, 0);
3495
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3498
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3499
test_fixture *fixture,
3500
__attribute__((unused))
3503
__attribute__((cleanup(cleanup_close)))
3504
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3505
g_assert_cmpint(epoll_fd, >=, 0);
3506
__attribute__((cleanup(cleanup_queue)))
3507
task_queue *queue = create_queue();
3508
g_assert_nonnull(queue);
3509
__attribute__((cleanup(string_set_clear)))
3510
string_set cancelled_filenames = {};
3511
const mono_microsecs current_time = 0;
3513
bool quit_now = false;
3514
buffer password = {};
3515
bool mandos_client_exited = false;
3516
bool password_is_read = false;
3518
__attribute__((cleanup(cleanup_string)))
3519
char *tempdir = make_temporary_directory();
3520
g_assert_nonnull(tempdir);
3522
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3524
&cancelled_filenames,
3526
&mandos_client_exited,
3527
&password_is_read));
3529
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3531
const task_context *const added_read_task
3532
= find_matching_task(queue,
3533
(task_context){ .func=read_inotify_event });
3534
g_assert_nonnull(added_read_task);
3536
g_assert_cmpint(added_read_task->fd, >, 2);
3537
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3539
/* "sufficient to read at least one event." - inotify(7) */
3540
const size_t ievent_size = (sizeof(struct inotify_event)
3542
struct inotify_event *ievent = malloc(ievent_size);
3543
g_assert_nonnull(ievent);
3545
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3547
g_assert_cmpint(errno, ==, EAGAIN);
3551
g_assert_cmpint(rmdir(tempdir), ==, 0);
3554
static char *make_temporary_file_in_directory(const char
3558
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3559
test_fixture *fixture,
3560
__attribute__((unused))
3563
__attribute__((cleanup(cleanup_close)))
3564
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3565
g_assert_cmpint(epoll_fd, >=, 0);
3566
__attribute__((cleanup(cleanup_queue)))
3567
task_queue *queue = create_queue();
3568
g_assert_nonnull(queue);
3569
__attribute__((cleanup(string_set_clear)))
3570
string_set cancelled_filenames = {};
3571
const mono_microsecs current_time = 0;
3573
bool quit_now = false;
3574
buffer password = {};
3575
bool mandos_client_exited = false;
3576
bool password_is_read = false;
3578
__attribute__((cleanup(cleanup_string)))
3579
char *tempdir = make_temporary_directory();
3580
g_assert_nonnull(tempdir);
3582
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3584
&cancelled_filenames,
3586
&mandos_client_exited,
3587
&password_is_read));
3589
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3591
const task_context *const added_read_task
3592
= find_matching_task(queue,
3593
(task_context){ .func=read_inotify_event });
3594
g_assert_nonnull(added_read_task);
3596
g_assert_cmpint(added_read_task->fd, >, 2);
3597
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3599
__attribute__((cleanup(cleanup_string)))
3600
char *filename = make_temporary_file_in_directory(tempdir);
3601
g_assert_nonnull(filename);
3603
/* "sufficient to read at least one event." - inotify(7) */
3604
const size_t ievent_size = (sizeof(struct inotify_event)
3606
struct inotify_event *ievent = malloc(ievent_size);
3607
g_assert_nonnull(ievent);
3609
ssize_t read_size = 0;
3610
read_size = read(added_read_task->fd, ievent, ievent_size);
3612
g_assert_cmpint((int)read_size, >, 0);
3613
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3614
g_assert_cmpstr(ievent->name, ==, basename(filename));
3618
g_assert_cmpint(unlink(filename), ==, 0);
3619
g_assert_cmpint(rmdir(tempdir), ==, 0);
3622
static char *make_temporary_prefixed_file_in_directory(const char
3626
char *filename = NULL;
3627
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3629
g_assert_nonnull(filename);
3630
const int fd = mkostemp(filename, O_CLOEXEC);
3631
g_assert_cmpint(fd, >=, 0);
3632
g_assert_cmpint(close(fd), ==, 0);
3636
static char *make_temporary_file_in_directory(const char
3638
return make_temporary_prefixed_file_in_directory("temp", dir);
3642
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3643
test_fixture *fixture,
3644
__attribute__((unused))
3645
gconstpointer user_data){
3646
__attribute__((cleanup(cleanup_close)))
3647
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3648
g_assert_cmpint(epoll_fd, >=, 0);
3649
__attribute__((cleanup(cleanup_queue)))
3650
task_queue *queue = create_queue();
3651
g_assert_nonnull(queue);
3652
__attribute__((cleanup(string_set_clear)))
3653
string_set cancelled_filenames = {};
3654
const mono_microsecs current_time = 0;
3656
bool quit_now = false;
3657
buffer password = {};
3658
bool mandos_client_exited = false;
3659
bool password_is_read = false;
3661
__attribute__((cleanup(cleanup_string)))
3662
char *watchdir = make_temporary_directory();
3663
g_assert_nonnull(watchdir);
3665
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3666
&password, watchdir,
3667
&cancelled_filenames,
3669
&mandos_client_exited,
3670
&password_is_read));
3672
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3674
const task_context *const added_read_task
3675
= find_matching_task(queue,
3676
(task_context){ .func=read_inotify_event });
3677
g_assert_nonnull(added_read_task);
3679
g_assert_cmpint(added_read_task->fd, >, 2);
3680
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3682
char *sourcedir = make_temporary_directory();
3683
g_assert_nonnull(sourcedir);
3685
__attribute__((cleanup(cleanup_string)))
3686
char *filename = make_temporary_file_in_directory(sourcedir);
3687
g_assert_nonnull(filename);
3689
__attribute__((cleanup(cleanup_string)))
3690
char *targetfilename = NULL;
3691
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3692
basename(filename)), >, 0);
3693
g_assert_nonnull(targetfilename);
3695
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3696
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3699
/* "sufficient to read at least one event." - inotify(7) */
3700
const size_t ievent_size = (sizeof(struct inotify_event)
3702
struct inotify_event *ievent = malloc(ievent_size);
3703
g_assert_nonnull(ievent);
3705
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3707
g_assert_cmpint((int)read_size, >, 0);
3708
g_assert_true(ievent->mask & IN_MOVED_TO);
3709
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3713
g_assert_cmpint(unlink(targetfilename), ==, 0);
3714
g_assert_cmpint(rmdir(watchdir), ==, 0);
3718
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3719
test_fixture *fixture,
3720
__attribute__((unused))
3723
__attribute__((cleanup(cleanup_close)))
3724
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3725
g_assert_cmpint(epoll_fd, >=, 0);
3726
__attribute__((cleanup(cleanup_queue)))
3727
task_queue *queue = create_queue();
3728
g_assert_nonnull(queue);
3729
__attribute__((cleanup(string_set_clear)))
3730
string_set cancelled_filenames = {};
3731
const mono_microsecs current_time = 0;
3733
bool quit_now = false;
3734
buffer password = {};
3735
bool mandos_client_exited = false;
3736
bool password_is_read = false;
3738
__attribute__((cleanup(cleanup_string)))
3739
char *tempdir = make_temporary_directory();
3740
g_assert_nonnull(tempdir);
3742
__attribute__((cleanup(cleanup_string)))
3743
char *tempfilename = make_temporary_file_in_directory(tempdir);
3744
g_assert_nonnull(tempfilename);
3746
__attribute__((cleanup(cleanup_string)))
3747
char *targetdir = make_temporary_directory();
3748
g_assert_nonnull(targetdir);
3750
__attribute__((cleanup(cleanup_string)))
3751
char *targetfilename = NULL;
3752
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3753
basename(tempfilename)), >, 0);
3754
g_assert_nonnull(targetfilename);
3756
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3758
&cancelled_filenames,
3760
&mandos_client_exited,
3761
&password_is_read));
3763
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3765
const task_context *const added_read_task
3766
= find_matching_task(queue,
3767
(task_context){ .func=read_inotify_event });
3768
g_assert_nonnull(added_read_task);
3770
/* "sufficient to read at least one event." - inotify(7) */
3771
const size_t ievent_size = (sizeof(struct inotify_event)
3773
struct inotify_event *ievent = malloc(ievent_size);
3774
g_assert_nonnull(ievent);
3776
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3778
g_assert_cmpint((int)read_size, >, 0);
3779
g_assert_true(ievent->mask & IN_MOVED_FROM);
3780
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3784
g_assert_cmpint(unlink(targetfilename), ==, 0);
3785
g_assert_cmpint(rmdir(targetdir), ==, 0);
3786
g_assert_cmpint(rmdir(tempdir), ==, 0);
3790
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3791
test_fixture *fixture,
3792
__attribute__((unused))
3793
gconstpointer user_data){
3794
__attribute__((cleanup(cleanup_close)))
3795
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3796
g_assert_cmpint(epoll_fd, >=, 0);
3797
__attribute__((cleanup(cleanup_queue)))
3798
task_queue *queue = create_queue();
3799
g_assert_nonnull(queue);
3800
__attribute__((cleanup(string_set_clear)))
3801
string_set cancelled_filenames = {};
3802
const mono_microsecs current_time = 0;
3804
bool quit_now = false;
3805
buffer password = {};
3806
bool mandos_client_exited = false;
3807
bool password_is_read = false;
3809
__attribute__((cleanup(cleanup_string)))
3810
char *tempdir = make_temporary_directory();
3811
g_assert_nonnull(tempdir);
3813
__attribute__((cleanup(cleanup_string)))
3814
char *tempfile = make_temporary_file_in_directory(tempdir);
3815
g_assert_nonnull(tempfile);
3817
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3819
&cancelled_filenames,
3821
&mandos_client_exited,
3822
&password_is_read));
3823
g_assert_cmpint(unlink(tempfile), ==, 0);
3825
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3827
const task_context *const added_read_task
3828
= find_matching_task(queue,
3829
(task_context){ .func=read_inotify_event });
3830
g_assert_nonnull(added_read_task);
3832
g_assert_cmpint(added_read_task->fd, >, 2);
3833
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3835
/* "sufficient to read at least one event." - inotify(7) */
3836
const size_t ievent_size = (sizeof(struct inotify_event)
3838
struct inotify_event *ievent = malloc(ievent_size);
3839
g_assert_nonnull(ievent);
3841
ssize_t read_size = 0;
3842
read_size = read(added_read_task->fd, ievent, ievent_size);
3844
g_assert_cmpint((int)read_size, >, 0);
3845
g_assert_true(ievent->mask & IN_DELETE);
3846
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3850
g_assert_cmpint(rmdir(tempdir), ==, 0);
3854
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3855
test_fixture *fixture,
3856
__attribute__((unused))
3859
__attribute__((cleanup(cleanup_close)))
3860
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3861
g_assert_cmpint(epoll_fd, >=, 0);
3862
__attribute__((cleanup(cleanup_queue)))
3863
task_queue *queue = create_queue();
3864
g_assert_nonnull(queue);
3865
__attribute__((cleanup(string_set_clear)))
3866
string_set cancelled_filenames = {};
3867
const mono_microsecs current_time = 0;
3869
bool quit_now = false;
3870
buffer password = {};
3871
bool mandos_client_exited = false;
3872
bool password_is_read = false;
3874
__attribute__((cleanup(cleanup_string)))
3875
char *tempdir = make_temporary_directory();
3876
g_assert_nonnull(tempdir);
3878
__attribute__((cleanup(cleanup_string)))
3879
char *tempfile = make_temporary_file_in_directory(tempdir);
3880
g_assert_nonnull(tempfile);
3881
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3883
g_assert_cmpint(tempfile_fd, >, 2);
3885
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3887
&cancelled_filenames,
3889
&mandos_client_exited,
3890
&password_is_read));
3891
g_assert_cmpint(unlink(tempfile), ==, 0);
3893
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3895
const task_context *const added_read_task
3896
= find_matching_task(queue,
3897
(task_context){ .func=read_inotify_event });
3898
g_assert_nonnull(added_read_task);
3900
g_assert_cmpint(added_read_task->fd, >, 2);
3901
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3903
/* "sufficient to read at least one event." - inotify(7) */
3904
const size_t ievent_size = (sizeof(struct inotify_event)
3906
struct inotify_event *ievent = malloc(ievent_size);
3907
g_assert_nonnull(ievent);
3909
ssize_t read_size = 0;
3910
read_size = read(added_read_task->fd, ievent, ievent_size);
3912
g_assert_cmpint((int)read_size, >, 0);
3913
g_assert_true(ievent->mask & IN_DELETE);
3914
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3916
g_assert_cmpint(close(tempfile_fd), ==, 0);
3918
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3919
file not appear as an ievent, so we should not see it now. */
3920
read_size = read(added_read_task->fd, ievent, ievent_size);
3921
g_assert_cmpint((int)read_size, ==, -1);
3922
g_assert_true(errno == EAGAIN);
3926
g_assert_cmpint(rmdir(tempdir), ==, 0);
3929
static void test_read_inotify_event_readerror(__attribute__((unused))
3930
test_fixture *fixture,
3931
__attribute__((unused))
3934
__attribute__((cleanup(cleanup_close)))
3935
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3936
g_assert_cmpint(epoll_fd, >=, 0);
3937
const mono_microsecs current_time = 0;
3939
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3940
const int fd = open("/proc/self/mem",
3941
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3943
bool quit_now = false;
3944
__attribute__((cleanup(cleanup_queue)))
3945
task_queue *queue = create_queue();
3946
g_assert_nonnull(queue);
3948
task_context task = {
3949
.func=read_inotify_event,
3952
.quit_now=&quit_now,
3953
.filename=strdup("/nonexistent"),
3954
.cancelled_filenames = &(string_set){},
3956
.current_time=¤t_time,
3958
g_assert_nonnull(task.filename);
3959
run_task_with_stderr_to_dev_null(task, queue);
3960
g_assert_true(quit_now);
3961
g_assert_true(queue->next_run == 0);
3962
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3964
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3966
g_assert_cmpint(close(fd), ==, -1);
3969
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3970
test_fixture *fixture,
3971
__attribute__((unused))
3974
const mono_microsecs current_time = 17;
3977
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3978
const int epoll_fd = pipefds[0]; /* This will obviously fail */
3980
bool quit_now = false;
3981
buffer password = {};
3982
bool mandos_client_exited = false;
3983
bool password_is_read = false;
3984
__attribute__((cleanup(cleanup_queue)))
3985
task_queue *queue = create_queue();
3986
g_assert_nonnull(queue);
3988
task_context task = {
3989
.func=read_inotify_event,
3992
.quit_now=&quit_now,
3993
.password=&password,
3994
.filename=strdup("/nonexistent"),
3995
.cancelled_filenames = &(string_set){},
3997
.current_time=¤t_time,
3998
.mandos_client_exited=&mandos_client_exited,
3999
.password_is_read=&password_is_read,
4001
g_assert_nonnull(task.filename);
4002
run_task_with_stderr_to_dev_null(task, queue);
4004
g_assert_nonnull(find_matching_task(queue, task));
4005
g_assert_true(queue->next_run == 1000000 + current_time);
4007
g_assert_cmpint(close(pipefds[0]), ==, 0);
4008
g_assert_cmpint(close(pipefds[1]), ==, 0);
4011
static void test_read_inotify_event_nodata(__attribute__((unused))
4012
test_fixture *fixture,
4013
__attribute__((unused))
4014
gconstpointer user_data){
4015
__attribute__((cleanup(cleanup_close)))
4016
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4017
g_assert_cmpint(epoll_fd, >=, 0);
4018
const mono_microsecs current_time = 0;
4021
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4023
bool quit_now = false;
4024
buffer password = {};
4025
bool mandos_client_exited = false;
4026
bool password_is_read = false;
4027
__attribute__((cleanup(cleanup_queue)))
4028
task_queue *queue = create_queue();
4029
g_assert_nonnull(queue);
4031
task_context task = {
4032
.func=read_inotify_event,
4035
.quit_now=&quit_now,
4036
.password=&password,
4037
.filename=strdup("/nonexistent"),
4038
.cancelled_filenames = &(string_set){},
4040
.current_time=¤t_time,
4041
.mandos_client_exited=&mandos_client_exited,
4042
.password_is_read=&password_is_read,
4044
g_assert_nonnull(task.filename);
4045
task.func(task, queue);
4046
g_assert_false(quit_now);
4047
g_assert_true(queue->next_run == 0);
4048
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4050
g_assert_nonnull(find_matching_task(queue, (task_context){
4051
.func=read_inotify_event,
4054
.quit_now=&quit_now,
4055
.password=&password,
4056
.filename=task.filename,
4057
.cancelled_filenames=task.cancelled_filenames,
4058
.current_time=¤t_time,
4059
.mandos_client_exited=&mandos_client_exited,
4060
.password_is_read=&password_is_read,
4063
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4064
EPOLLIN | EPOLLRDHUP));
4066
g_assert_cmpint(close(pipefds[1]), ==, 0);
4069
static void test_read_inotify_event_eof(__attribute__((unused))
4070
test_fixture *fixture,
4071
__attribute__((unused))
4072
gconstpointer user_data){
4073
__attribute__((cleanup(cleanup_close)))
4074
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4075
g_assert_cmpint(epoll_fd, >=, 0);
4076
const mono_microsecs current_time = 0;
4079
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4080
g_assert_cmpint(close(pipefds[1]), ==, 0);
4082
bool quit_now = false;
4083
buffer password = {};
4084
__attribute__((cleanup(cleanup_queue)))
4085
task_queue *queue = create_queue();
4086
g_assert_nonnull(queue);
4088
task_context task = {
4089
.func=read_inotify_event,
4092
.quit_now=&quit_now,
4093
.password=&password,
4094
.filename=strdup("/nonexistent"),
4095
.cancelled_filenames = &(string_set){},
4097
.current_time=¤t_time,
4099
run_task_with_stderr_to_dev_null(task, queue);
4100
g_assert_true(quit_now);
4101
g_assert_true(queue->next_run == 0);
4102
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4104
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4106
g_assert_cmpint(close(pipefds[0]), ==, -1);
4110
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4111
test_fixture *fixture,
4112
__attribute__((unused))
4113
gconstpointer user_data){
4114
__attribute__((cleanup(cleanup_close)))
4115
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4116
g_assert_cmpint(epoll_fd, >=, 0);
4117
const mono_microsecs current_time = 0;
4120
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4122
/* "sufficient to read at least one event." - inotify(7) */
4123
const size_t ievent_max_size = (sizeof(struct inotify_event)
4125
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4127
struct inotify_event event;
4128
char name_buffer[NAME_MAX + 1];
4130
struct inotify_event *const ievent = &ievent_buffer.event;
4132
const char dummy_file_name[] = "ask.dummy_file_name";
4133
ievent->mask = IN_CLOSE_WRITE;
4134
ievent->len = sizeof(dummy_file_name);
4135
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4136
const size_t ievent_size = (sizeof(struct inotify_event)
4137
+ sizeof(dummy_file_name));
4138
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4140
g_assert_cmpint(close(pipefds[1]), ==, 0);
4142
bool quit_now = false;
4143
buffer password = {};
4144
bool mandos_client_exited = false;
4145
bool password_is_read = false;
4146
__attribute__((cleanup(cleanup_queue)))
4147
task_queue *queue = create_queue();
4148
g_assert_nonnull(queue);
4150
task_context task = {
4151
.func=read_inotify_event,
4154
.quit_now=&quit_now,
4155
.password=&password,
4156
.filename=strdup("/nonexistent"),
4157
.cancelled_filenames = &(string_set){},
4159
.current_time=¤t_time,
4160
.mandos_client_exited=&mandos_client_exited,
4161
.password_is_read=&password_is_read,
4163
task.func(task, queue);
4164
g_assert_false(quit_now);
4165
g_assert_true(queue->next_run != 0);
4166
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4168
g_assert_nonnull(find_matching_task(queue, (task_context){
4169
.func=read_inotify_event,
4172
.quit_now=&quit_now,
4173
.password=&password,
4174
.filename=task.filename,
4175
.cancelled_filenames=task.cancelled_filenames,
4176
.current_time=¤t_time,
4177
.mandos_client_exited=&mandos_client_exited,
4178
.password_is_read=&password_is_read,
4181
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4182
EPOLLIN | EPOLLRDHUP));
4184
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4186
__attribute__((cleanup(cleanup_string)))
4187
char *filename = NULL;
4188
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4189
dummy_file_name), >, 0);
4190
g_assert_nonnull(filename);
4191
g_assert_nonnull(find_matching_task(queue, (task_context){
4192
.func=open_and_parse_question,
4195
.question_filename=filename,
4196
.password=&password,
4197
.cancelled_filenames=task.cancelled_filenames,
4198
.current_time=¤t_time,
4199
.mandos_client_exited=&mandos_client_exited,
4200
.password_is_read=&password_is_read,
4205
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4206
test_fixture *fixture,
4207
__attribute__((unused))
4208
gconstpointer user_data){
4209
__attribute__((cleanup(cleanup_close)))
4210
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4211
g_assert_cmpint(epoll_fd, >=, 0);
4212
const mono_microsecs current_time = 0;
4215
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4217
/* "sufficient to read at least one event." - inotify(7) */
4218
const size_t ievent_max_size = (sizeof(struct inotify_event)
4220
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4222
struct inotify_event event;
4223
char name_buffer[NAME_MAX + 1];
4225
struct inotify_event *const ievent = &ievent_buffer.event;
4227
const char dummy_file_name[] = "ask.dummy_file_name";
4228
ievent->mask = IN_MOVED_TO;
4229
ievent->len = sizeof(dummy_file_name);
4230
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4231
const size_t ievent_size = (sizeof(struct inotify_event)
4232
+ sizeof(dummy_file_name));
4233
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4235
g_assert_cmpint(close(pipefds[1]), ==, 0);
4237
bool quit_now = false;
4238
buffer password = {};
4239
bool mandos_client_exited = false;
4240
bool password_is_read = false;
4241
__attribute__((cleanup(cleanup_queue)))
4242
task_queue *queue = create_queue();
4243
g_assert_nonnull(queue);
4245
task_context task = {
4246
.func=read_inotify_event,
4249
.quit_now=&quit_now,
4250
.password=&password,
4251
.filename=strdup("/nonexistent"),
4252
.cancelled_filenames = &(string_set){},
4254
.current_time=¤t_time,
4255
.mandos_client_exited=&mandos_client_exited,
4256
.password_is_read=&password_is_read,
4258
task.func(task, queue);
4259
g_assert_false(quit_now);
4260
g_assert_true(queue->next_run != 0);
4261
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4263
g_assert_nonnull(find_matching_task(queue, (task_context){
4264
.func=read_inotify_event,
4267
.quit_now=&quit_now,
4268
.password=&password,
4269
.filename=task.filename,
4270
.cancelled_filenames=task.cancelled_filenames,
4271
.current_time=¤t_time,
4272
.mandos_client_exited=&mandos_client_exited,
4273
.password_is_read=&password_is_read,
4276
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4277
EPOLLIN | EPOLLRDHUP));
4279
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4281
__attribute__((cleanup(cleanup_string)))
4282
char *filename = NULL;
4283
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4284
dummy_file_name), >, 0);
4285
g_assert_nonnull(filename);
4286
g_assert_nonnull(find_matching_task(queue, (task_context){
4287
.func=open_and_parse_question,
4290
.question_filename=filename,
4291
.password=&password,
4292
.cancelled_filenames=task.cancelled_filenames,
4293
.current_time=¤t_time,
4294
.mandos_client_exited=&mandos_client_exited,
4295
.password_is_read=&password_is_read,
4300
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4301
test_fixture *fixture,
4302
__attribute__((unused))
4303
gconstpointer user_data){
4304
__attribute__((cleanup(cleanup_close)))
4305
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4306
g_assert_cmpint(epoll_fd, >=, 0);
4307
__attribute__((cleanup(string_set_clear)))
4308
string_set cancelled_filenames = {};
4309
const mono_microsecs current_time = 0;
4312
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4314
/* "sufficient to read at least one event." - inotify(7) */
4315
const size_t ievent_max_size = (sizeof(struct inotify_event)
4317
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4319
struct inotify_event event;
4320
char name_buffer[NAME_MAX + 1];
4322
struct inotify_event *const ievent = &ievent_buffer.event;
4324
const char dummy_file_name[] = "ask.dummy_file_name";
4325
ievent->mask = IN_MOVED_FROM;
4326
ievent->len = sizeof(dummy_file_name);
4327
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4328
const size_t ievent_size = (sizeof(struct inotify_event)
4329
+ sizeof(dummy_file_name));
4330
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4332
g_assert_cmpint(close(pipefds[1]), ==, 0);
4334
bool quit_now = false;
4335
buffer password = {};
4336
bool mandos_client_exited = false;
4337
bool password_is_read = false;
4338
__attribute__((cleanup(cleanup_queue)))
4339
task_queue *queue = create_queue();
4340
g_assert_nonnull(queue);
4342
task_context task = {
4343
.func=read_inotify_event,
4346
.quit_now=&quit_now,
4347
.password=&password,
4348
.filename=strdup("/nonexistent"),
4349
.cancelled_filenames=&cancelled_filenames,
4350
.current_time=¤t_time,
4351
.mandos_client_exited=&mandos_client_exited,
4352
.password_is_read=&password_is_read,
4354
task.func(task, queue);
4355
g_assert_false(quit_now);
4356
g_assert_true(queue->next_run == 0);
4357
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4359
g_assert_nonnull(find_matching_task(queue, (task_context){
4360
.func=read_inotify_event,
4363
.quit_now=&quit_now,
4364
.password=&password,
4365
.filename=task.filename,
4366
.cancelled_filenames=&cancelled_filenames,
4367
.current_time=¤t_time,
4368
.mandos_client_exited=&mandos_client_exited,
4369
.password_is_read=&password_is_read,
4372
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4373
EPOLLIN | EPOLLRDHUP));
4375
__attribute__((cleanup(cleanup_string)))
4376
char *filename = NULL;
4377
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4378
dummy_file_name), >, 0);
4379
g_assert_nonnull(filename);
4380
g_assert_true(string_set_contains(*task.cancelled_filenames,
4384
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4385
test_fixture *fixture,
4386
__attribute__((unused))
4389
__attribute__((cleanup(cleanup_close)))
4390
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4391
g_assert_cmpint(epoll_fd, >=, 0);
4392
__attribute__((cleanup(string_set_clear)))
4393
string_set cancelled_filenames = {};
4394
const mono_microsecs current_time = 0;
4397
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4399
/* "sufficient to read at least one event." - inotify(7) */
4400
const size_t ievent_max_size = (sizeof(struct inotify_event)
4402
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4404
struct inotify_event event;
4405
char name_buffer[NAME_MAX + 1];
4407
struct inotify_event *const ievent = &ievent_buffer.event;
4409
const char dummy_file_name[] = "ask.dummy_file_name";
4410
ievent->mask = IN_DELETE;
4411
ievent->len = sizeof(dummy_file_name);
4412
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4413
const size_t ievent_size = (sizeof(struct inotify_event)
4414
+ sizeof(dummy_file_name));
4415
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4417
g_assert_cmpint(close(pipefds[1]), ==, 0);
4419
bool quit_now = false;
4420
buffer password = {};
4421
bool mandos_client_exited = false;
4422
bool password_is_read = false;
4423
__attribute__((cleanup(cleanup_queue)))
4424
task_queue *queue = create_queue();
4425
g_assert_nonnull(queue);
4427
task_context task = {
4428
.func=read_inotify_event,
4431
.quit_now=&quit_now,
4432
.password=&password,
4433
.filename=strdup("/nonexistent"),
4434
.cancelled_filenames=&cancelled_filenames,
4435
.current_time=¤t_time,
4436
.mandos_client_exited=&mandos_client_exited,
4437
.password_is_read=&password_is_read,
4439
task.func(task, queue);
4440
g_assert_false(quit_now);
4441
g_assert_true(queue->next_run == 0);
4442
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4444
g_assert_nonnull(find_matching_task(queue, (task_context){
4445
.func=read_inotify_event,
4448
.quit_now=&quit_now,
4449
.password=&password,
4450
.filename=task.filename,
4451
.cancelled_filenames=&cancelled_filenames,
4452
.current_time=¤t_time,
4453
.mandos_client_exited=&mandos_client_exited,
4454
.password_is_read=&password_is_read,
4457
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4458
EPOLLIN | EPOLLRDHUP));
4460
__attribute__((cleanup(cleanup_string)))
4461
char *filename = NULL;
4462
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4463
dummy_file_name), >, 0);
4464
g_assert_nonnull(filename);
4465
g_assert_true(string_set_contains(*task.cancelled_filenames,
4470
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4471
test_fixture *fixture,
4472
__attribute__((unused))
4475
__attribute__((cleanup(cleanup_close)))
4476
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4477
g_assert_cmpint(epoll_fd, >=, 0);
4478
const mono_microsecs current_time = 0;
4481
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4483
/* "sufficient to read at least one event." - inotify(7) */
4484
const size_t ievent_max_size = (sizeof(struct inotify_event)
4486
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4488
struct inotify_event event;
4489
char name_buffer[NAME_MAX + 1];
4491
struct inotify_event *const ievent = &ievent_buffer.event;
4493
const char dummy_file_name[] = "ignored.dummy_file_name";
4494
ievent->mask = IN_CLOSE_WRITE;
4495
ievent->len = sizeof(dummy_file_name);
4496
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4497
const size_t ievent_size = (sizeof(struct inotify_event)
4498
+ sizeof(dummy_file_name));
4499
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4501
g_assert_cmpint(close(pipefds[1]), ==, 0);
4503
bool quit_now = false;
4504
buffer password = {};
4505
bool mandos_client_exited = false;
4506
bool password_is_read = false;
4507
__attribute__((cleanup(cleanup_queue)))
4508
task_queue *queue = create_queue();
4509
g_assert_nonnull(queue);
4511
task_context task = {
4512
.func=read_inotify_event,
4515
.quit_now=&quit_now,
4516
.password=&password,
4517
.filename=strdup("/nonexistent"),
4518
.cancelled_filenames = &(string_set){},
4520
.current_time=¤t_time,
4521
.mandos_client_exited=&mandos_client_exited,
4522
.password_is_read=&password_is_read,
4524
task.func(task, queue);
4525
g_assert_false(quit_now);
4526
g_assert_true(queue->next_run == 0);
4527
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4529
g_assert_nonnull(find_matching_task(queue, (task_context){
4530
.func=read_inotify_event,
4533
.quit_now=&quit_now,
4534
.password=&password,
4535
.filename=task.filename,
4536
.cancelled_filenames=task.cancelled_filenames,
4537
.current_time=¤t_time,
4538
.mandos_client_exited=&mandos_client_exited,
4539
.password_is_read=&password_is_read,
4542
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4543
EPOLLIN | EPOLLRDHUP));
4547
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4548
test_fixture *fixture,
4549
__attribute__((unused))
4550
gconstpointer user_data){
4551
__attribute__((cleanup(cleanup_close)))
4552
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4553
g_assert_cmpint(epoll_fd, >=, 0);
4554
const mono_microsecs current_time = 0;
4557
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4559
/* "sufficient to read at least one event." - inotify(7) */
4560
const size_t ievent_max_size = (sizeof(struct inotify_event)
4562
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4564
struct inotify_event event;
4565
char name_buffer[NAME_MAX + 1];
4567
struct inotify_event *const ievent = &ievent_buffer.event;
4569
const char dummy_file_name[] = "ignored.dummy_file_name";
4570
ievent->mask = IN_MOVED_TO;
4571
ievent->len = sizeof(dummy_file_name);
4572
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4573
const size_t ievent_size = (sizeof(struct inotify_event)
4574
+ sizeof(dummy_file_name));
4575
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4577
g_assert_cmpint(close(pipefds[1]), ==, 0);
4579
bool quit_now = false;
4580
buffer password = {};
4581
bool mandos_client_exited = false;
4582
bool password_is_read = false;
4583
__attribute__((cleanup(cleanup_queue)))
4584
task_queue *queue = create_queue();
4585
g_assert_nonnull(queue);
4587
task_context task = {
4588
.func=read_inotify_event,
4591
.quit_now=&quit_now,
4592
.password=&password,
4593
.filename=strdup("/nonexistent"),
4594
.cancelled_filenames = &(string_set){},
4596
.current_time=¤t_time,
4597
.mandos_client_exited=&mandos_client_exited,
4598
.password_is_read=&password_is_read,
4600
task.func(task, queue);
4601
g_assert_false(quit_now);
4602
g_assert_true(queue->next_run == 0);
4603
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4605
g_assert_nonnull(find_matching_task(queue, (task_context){
4606
.func=read_inotify_event,
4609
.quit_now=&quit_now,
4610
.password=&password,
4611
.filename=task.filename,
4612
.cancelled_filenames=task.cancelled_filenames,
4613
.current_time=¤t_time,
4614
.mandos_client_exited=&mandos_client_exited,
4615
.password_is_read=&password_is_read,
4618
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4619
EPOLLIN | EPOLLRDHUP));
4623
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4624
test_fixture *fixture,
4625
__attribute__((unused))
4628
__attribute__((cleanup(cleanup_close)))
4629
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4630
g_assert_cmpint(epoll_fd, >=, 0);
4631
__attribute__((cleanup(string_set_clear)))
4632
string_set cancelled_filenames = {};
4633
const mono_microsecs current_time = 0;
4636
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4638
/* "sufficient to read at least one event." - inotify(7) */
4639
const size_t ievent_max_size = (sizeof(struct inotify_event)
4641
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4643
struct inotify_event event;
4644
char name_buffer[NAME_MAX + 1];
4646
struct inotify_event *const ievent = &ievent_buffer.event;
4648
const char dummy_file_name[] = "ignored.dummy_file_name";
4649
ievent->mask = IN_MOVED_FROM;
4650
ievent->len = sizeof(dummy_file_name);
4651
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4652
const size_t ievent_size = (sizeof(struct inotify_event)
4653
+ sizeof(dummy_file_name));
4654
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4656
g_assert_cmpint(close(pipefds[1]), ==, 0);
4658
bool quit_now = false;
4659
buffer password = {};
4660
bool mandos_client_exited = false;
4661
bool password_is_read = false;
4662
__attribute__((cleanup(cleanup_queue)))
4663
task_queue *queue = create_queue();
4664
g_assert_nonnull(queue);
4666
task_context task = {
4667
.func=read_inotify_event,
4670
.quit_now=&quit_now,
4671
.password=&password,
4672
.filename=strdup("/nonexistent"),
4673
.cancelled_filenames=&cancelled_filenames,
4674
.current_time=¤t_time,
4675
.mandos_client_exited=&mandos_client_exited,
4676
.password_is_read=&password_is_read,
4678
task.func(task, queue);
4679
g_assert_false(quit_now);
4680
g_assert_true(queue->next_run == 0);
4681
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4683
g_assert_nonnull(find_matching_task(queue, (task_context){
4684
.func=read_inotify_event,
4687
.quit_now=&quit_now,
4688
.password=&password,
4689
.filename=task.filename,
4690
.cancelled_filenames=&cancelled_filenames,
4691
.current_time=¤t_time,
4692
.mandos_client_exited=&mandos_client_exited,
4693
.password_is_read=&password_is_read,
4696
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4697
EPOLLIN | EPOLLRDHUP));
4699
__attribute__((cleanup(cleanup_string)))
4700
char *filename = NULL;
4701
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4702
dummy_file_name), >, 0);
4703
g_assert_nonnull(filename);
4704
g_assert_false(string_set_contains(cancelled_filenames, filename));
4708
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4709
test_fixture *fixture,
4710
__attribute__((unused))
4713
__attribute__((cleanup(cleanup_close)))
4714
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4715
g_assert_cmpint(epoll_fd, >=, 0);
4716
__attribute__((cleanup(string_set_clear)))
4717
string_set cancelled_filenames = {};
4718
const mono_microsecs current_time = 0;
4721
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4723
/* "sufficient to read at least one event." - inotify(7) */
4724
const size_t ievent_max_size = (sizeof(struct inotify_event)
4726
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4728
struct inotify_event event;
4729
char name_buffer[NAME_MAX + 1];
4731
struct inotify_event *const ievent = &ievent_buffer.event;
4733
const char dummy_file_name[] = "ignored.dummy_file_name";
4734
ievent->mask = IN_DELETE;
4735
ievent->len = sizeof(dummy_file_name);
4736
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4737
const size_t ievent_size = (sizeof(struct inotify_event)
4738
+ sizeof(dummy_file_name));
4739
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4741
g_assert_cmpint(close(pipefds[1]), ==, 0);
4743
bool quit_now = false;
4744
buffer password = {};
4745
bool mandos_client_exited = false;
4746
bool password_is_read = false;
4747
__attribute__((cleanup(cleanup_queue)))
4748
task_queue *queue = create_queue();
4749
g_assert_nonnull(queue);
4751
task_context task = {
4752
.func=read_inotify_event,
4755
.quit_now=&quit_now,
4756
.password=&password,
4757
.filename=strdup("/nonexistent"),
4758
.cancelled_filenames=&cancelled_filenames,
4759
.current_time=¤t_time,
4760
.mandos_client_exited=&mandos_client_exited,
4761
.password_is_read=&password_is_read,
4763
task.func(task, queue);
4764
g_assert_false(quit_now);
4765
g_assert_true(queue->next_run == 0);
4766
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4768
g_assert_nonnull(find_matching_task(queue, (task_context){
4769
.func=read_inotify_event,
4772
.quit_now=&quit_now,
4773
.password=&password,
4774
.filename=task.filename,
4775
.cancelled_filenames=&cancelled_filenames,
4776
.current_time=¤t_time,
4777
.mandos_client_exited=&mandos_client_exited,
4778
.password_is_read=&password_is_read,
4781
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4782
EPOLLIN | EPOLLRDHUP));
4784
__attribute__((cleanup(cleanup_string)))
4785
char *filename = NULL;
4786
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4787
dummy_file_name), >, 0);
4788
g_assert_nonnull(filename);
4789
g_assert_false(string_set_contains(cancelled_filenames, filename));
4793
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4794
test_fixture *fixture,
4795
__attribute__((unused))
4796
gconstpointer user_data){
4797
__attribute__((cleanup(cleanup_close)))
4798
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4799
g_assert_cmpint(epoll_fd, >=, 0);
4800
__attribute__((cleanup(string_set_clear)))
4801
string_set cancelled_filenames = {};
4802
bool mandos_client_exited = false;
4803
bool password_is_read = false;
4804
__attribute__((cleanup(cleanup_queue)))
4805
task_queue *queue = create_queue();
4806
g_assert_nonnull(queue);
4808
char *const filename = strdup("/nonexistent");
4809
g_assert_nonnull(filename);
4810
task_context task = {
4811
.func=open_and_parse_question,
4812
.question_filename=filename,
4814
.password=(buffer[]){{}},
4816
.cancelled_filenames=&cancelled_filenames,
4817
.current_time=(mono_microsecs[]){0},
4818
.mandos_client_exited=&mandos_client_exited,
4819
.password_is_read=&password_is_read,
4821
task.func(task, queue);
4822
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4825
static void test_open_and_parse_question_EIO(__attribute__((unused))
4826
test_fixture *fixture,
4827
__attribute__((unused))
4828
gconstpointer user_data){
4829
__attribute__((cleanup(cleanup_close)))
4830
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4831
g_assert_cmpint(epoll_fd, >=, 0);
4832
__attribute__((cleanup(string_set_clear)))
4833
string_set cancelled_filenames = {};
4834
buffer password = {};
4835
bool mandos_client_exited = false;
4836
bool password_is_read = false;
4837
__attribute__((cleanup(cleanup_queue)))
4838
task_queue *queue = create_queue();
4839
g_assert_nonnull(queue);
4840
const mono_microsecs current_time = 0;
4842
char *filename = strdup("/proc/self/mem");
4843
task_context task = {
4844
.func=open_and_parse_question,
4845
.question_filename=filename,
4847
.password=&password,
4849
.cancelled_filenames=&cancelled_filenames,
4850
.current_time=¤t_time,
4851
.mandos_client_exited=&mandos_client_exited,
4852
.password_is_read=&password_is_read,
4854
run_task_with_stderr_to_dev_null(task, queue);
4855
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4859
test_open_and_parse_question_parse_error(__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 tempfile = mkostemp(tempfilename, O_CLOEXEC);
4876
g_assert_cmpint(tempfile, >, 0);
4877
const char bad_data[] = "this is bad syntax\n";
4878
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4879
==, sizeof(bad_data));
4880
g_assert_cmpint(close(tempfile), ==, 0);
4882
char *const filename = strdup(tempfilename);
4883
g_assert_nonnull(filename);
4884
task_context task = {
4885
.func=open_and_parse_question,
4886
.question_filename=filename,
4888
.password=(buffer[]){{}},
4890
.cancelled_filenames=&cancelled_filenames,
4891
.current_time=(mono_microsecs[]){0},
4892
.mandos_client_exited=(bool[]){false},
4893
.password_is_read=(bool[]){false},
4895
run_task_with_stderr_to_dev_null(task, queue);
4897
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4899
g_assert_cmpint(unlink(tempfilename), ==, 0);
4903
void test_open_and_parse_question_nosocket(__attribute__((unused))
4904
test_fixture *fixture,
4905
__attribute__((unused))
4906
gconstpointer user_data){
4907
__attribute__((cleanup(cleanup_close)))
4908
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4909
g_assert_cmpint(epoll_fd, >=, 0);
4910
__attribute__((cleanup(string_set_clear)))
4911
string_set cancelled_filenames = {};
4912
__attribute__((cleanup(cleanup_queue)))
4913
task_queue *queue = create_queue();
4914
g_assert_nonnull(queue);
4916
__attribute__((cleanup(cleanup_string)))
4917
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4918
g_assert_nonnull(tempfilename);
4919
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4920
g_assert_cmpint(questionfile, >, 0);
4921
FILE *qf = fdopen(questionfile, "w");
4922
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4923
g_assert_cmpint(fclose(qf), ==, 0);
4925
char *const filename = strdup(tempfilename);
4926
g_assert_nonnull(filename);
4927
task_context task = {
4928
.func=open_and_parse_question,
4929
.question_filename=filename,
4931
.password=(buffer[]){{}},
4933
.cancelled_filenames=&cancelled_filenames,
4934
.current_time=(mono_microsecs[]){0},
4935
.mandos_client_exited=(bool[]){false},
4936
.password_is_read=(bool[]){false},
4938
run_task_with_stderr_to_dev_null(task, queue);
4939
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4941
g_assert_cmpint(unlink(tempfilename), ==, 0);
4945
void test_open_and_parse_question_badsocket(__attribute__((unused))
4946
test_fixture *fixture,
4947
__attribute__((unused))
4948
gconstpointer user_data){
4949
__attribute__((cleanup(cleanup_close)))
4950
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4951
g_assert_cmpint(epoll_fd, >=, 0);
4952
__attribute__((cleanup(string_set_clear)))
4953
string_set cancelled_filenames = {};
4954
__attribute__((cleanup(cleanup_queue)))
4955
task_queue *queue = create_queue();
4956
g_assert_nonnull(queue);
4958
__attribute__((cleanup(cleanup_string)))
4959
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4960
g_assert_nonnull(tempfilename);
4961
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4962
g_assert_cmpint(questionfile, >, 0);
4963
FILE *qf = fdopen(questionfile, "w");
4964
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4965
g_assert_cmpint(fclose(qf), ==, 0);
4967
char *const filename = strdup(tempfilename);
4968
g_assert_nonnull(filename);
4969
task_context task = {
4970
.func=open_and_parse_question,
4971
.question_filename=filename,
4973
.password=(buffer[]){{}},
4975
.cancelled_filenames=&cancelled_filenames,
4976
.current_time=(mono_microsecs[]){0},
4977
.mandos_client_exited=(bool[]){false},
4978
.password_is_read=(bool[]){false},
4980
run_task_with_stderr_to_dev_null(task, queue);
4981
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4983
g_assert_cmpint(unlink(tempfilename), ==, 0);
4987
void test_open_and_parse_question_nopid(__attribute__((unused))
4988
test_fixture *fixture,
4989
__attribute__((unused))
4990
gconstpointer user_data){
4991
__attribute__((cleanup(cleanup_close)))
4992
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4993
g_assert_cmpint(epoll_fd, >=, 0);
4994
__attribute__((cleanup(string_set_clear)))
4995
string_set cancelled_filenames = {};
4996
__attribute__((cleanup(cleanup_queue)))
4997
task_queue *queue = create_queue();
4998
g_assert_nonnull(queue);
5000
__attribute__((cleanup(cleanup_string)))
5001
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5002
g_assert_nonnull(tempfilename);
5003
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5004
g_assert_cmpint(questionfile, >, 0);
5005
FILE *qf = fdopen(questionfile, "w");
5006
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5007
g_assert_cmpint(fclose(qf), ==, 0);
5009
char *const filename = strdup(tempfilename);
5010
g_assert_nonnull(filename);
5011
task_context task = {
5012
.func=open_and_parse_question,
5013
.question_filename=filename,
5015
.password=(buffer[]){{}},
5017
.cancelled_filenames=&cancelled_filenames,
5018
.current_time=(mono_microsecs[]){0},
5019
.mandos_client_exited=(bool[]){false},
5020
.password_is_read=(bool[]){false},
5022
run_task_with_stderr_to_dev_null(task, queue);
5023
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5025
g_assert_cmpint(unlink(tempfilename), ==, 0);
5029
void test_open_and_parse_question_badpid(__attribute__((unused))
5030
test_fixture *fixture,
5031
__attribute__((unused))
5032
gconstpointer user_data){
5033
__attribute__((cleanup(cleanup_close)))
5034
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5035
g_assert_cmpint(epoll_fd, >=, 0);
5036
__attribute__((cleanup(string_set_clear)))
5037
string_set cancelled_filenames = {};
5038
__attribute__((cleanup(cleanup_queue)))
5039
task_queue *queue = create_queue();
5040
g_assert_nonnull(queue);
5042
__attribute__((cleanup(cleanup_string)))
5043
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5044
g_assert_nonnull(tempfilename);
5045
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5046
g_assert_cmpint(questionfile, >, 0);
5047
FILE *qf = fdopen(questionfile, "w");
5048
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5050
g_assert_cmpint(fclose(qf), ==, 0);
5052
char *const filename = strdup(tempfilename);
5053
g_assert_nonnull(filename);
5054
task_context task = {
5055
.func=open_and_parse_question,
5056
.question_filename=filename,
5058
.password=(buffer[]){{}},
5060
.cancelled_filenames=&cancelled_filenames,
5061
.current_time=(mono_microsecs[]){0},
5062
.mandos_client_exited=(bool[]){false},
5063
.password_is_read=(bool[]){false},
5065
run_task_with_stderr_to_dev_null(task, queue);
5066
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5068
g_assert_cmpint(unlink(tempfilename), ==, 0);
5072
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5073
test_fixture *fixture,
5074
__attribute__((unused))
5075
gconstpointer user_data){
5076
__attribute__((cleanup(cleanup_close)))
5077
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5078
g_assert_cmpint(epoll_fd, >=, 0);
5079
__attribute__((cleanup(string_set_clear)))
5080
string_set cancelled_filenames = {};
5081
buffer password = {};
5082
bool mandos_client_exited = false;
5083
bool password_is_read = false;
5084
__attribute__((cleanup(cleanup_queue)))
5085
task_queue *queue = create_queue();
5086
g_assert_nonnull(queue);
5087
const mono_microsecs current_time = 0;
5089
/* Find value of sysctl kernel.pid_max */
5090
uintmax_t pid_max = 0;
5091
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5092
g_assert_nonnull(sysctl_pid_max);
5093
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5095
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5097
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5098
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5100
__attribute__((cleanup(cleanup_string)))
5101
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5102
g_assert_nonnull(tempfilename);
5103
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5104
g_assert_cmpint(questionfile, >, 0);
5105
FILE *qf = fdopen(questionfile, "w");
5106
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5107
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5109
g_assert_cmpint(fclose(qf), ==, 0);
5111
char *const question_filename = strdup(tempfilename);
5112
g_assert_nonnull(question_filename);
5113
task_context task = {
5114
.func=open_and_parse_question,
5115
.question_filename=question_filename,
5117
.password=&password,
5118
.filename=question_filename,
5119
.cancelled_filenames=&cancelled_filenames,
5120
.current_time=¤t_time,
5121
.mandos_client_exited=&mandos_client_exited,
5122
.password_is_read=&password_is_read,
5124
run_task_with_stderr_to_dev_null(task, queue);
5125
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5127
g_assert_cmpint(unlink(tempfilename), ==, 0);
5131
test_open_and_parse_question_no_notafter(__attribute__((unused))
5132
test_fixture *fixture,
5133
__attribute__((unused))
5134
gconstpointer user_data){
5135
__attribute__((cleanup(cleanup_close)))
5136
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5137
g_assert_cmpint(epoll_fd, >=, 0);
5138
__attribute__((cleanup(string_set_clear)))
5139
string_set cancelled_filenames = {};
5140
buffer password = {};
5141
bool mandos_client_exited = false;
5142
bool password_is_read = false;
5143
__attribute__((cleanup(cleanup_queue)))
5144
task_queue *queue = create_queue();
5145
g_assert_nonnull(queue);
5146
const mono_microsecs current_time = 0;
5148
__attribute__((cleanup(cleanup_string)))
5149
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5150
g_assert_nonnull(tempfilename);
5151
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5152
g_assert_cmpint(questionfile, >, 0);
5153
FILE *qf = fdopen(questionfile, "w");
5154
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5155
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5156
g_assert_cmpint(fclose(qf), ==, 0);
5158
char *const filename = strdup(tempfilename);
5159
g_assert_nonnull(filename);
5160
task_context task = {
5161
.func=open_and_parse_question,
5162
.question_filename=filename,
5164
.password=&password,
5166
.cancelled_filenames=&cancelled_filenames,
5167
.current_time=¤t_time,
5168
.mandos_client_exited=&mandos_client_exited,
5169
.password_is_read=&password_is_read,
5171
task.func(task, queue);
5172
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5174
__attribute__((cleanup(cleanup_string)))
5175
char *socket_filename = strdup("/nonexistent");
5176
g_assert_nonnull(socket_filename);
5177
g_assert_nonnull(find_matching_task(queue, (task_context){
5178
.func=connect_question_socket,
5179
.question_filename=tempfilename,
5180
.filename=socket_filename,
5182
.password=&password,
5183
.current_time=¤t_time,
5184
.mandos_client_exited=&mandos_client_exited,
5185
.password_is_read=&password_is_read,
5188
g_assert_true(queue->next_run != 0);
5190
g_assert_cmpint(unlink(tempfilename), ==, 0);
5194
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5195
test_fixture *fixture,
5196
__attribute__((unused))
5197
gconstpointer user_data){
5198
__attribute__((cleanup(cleanup_close)))
5199
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5200
g_assert_cmpint(epoll_fd, >=, 0);
5201
__attribute__((cleanup(string_set_clear)))
5202
string_set cancelled_filenames = {};
5203
buffer password = {};
5204
bool mandos_client_exited = false;
5205
bool password_is_read = false;
5206
__attribute__((cleanup(cleanup_queue)))
5207
task_queue *queue = create_queue();
5208
g_assert_nonnull(queue);
5209
const mono_microsecs current_time = 0;
5211
__attribute__((cleanup(cleanup_string)))
5212
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5213
g_assert_nonnull(tempfilename);
5214
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5215
g_assert_cmpint(questionfile, >, 0);
5216
FILE *qf = fdopen(questionfile, "w");
5217
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5218
PRIuMAX "\nNotAfter=\n",
5219
(uintmax_t)getpid()), >, 0);
5220
g_assert_cmpint(fclose(qf), ==, 0);
5222
char *const filename = strdup(tempfilename);
5223
g_assert_nonnull(filename);
5224
task_context task = {
5225
.func=open_and_parse_question,
5226
.question_filename=filename,
5228
.password=&password,
5230
.cancelled_filenames=&cancelled_filenames,
5231
.current_time=¤t_time,
5232
.mandos_client_exited=&mandos_client_exited,
5233
.password_is_read=&password_is_read,
5235
run_task_with_stderr_to_dev_null(task, queue);
5236
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5238
__attribute__((cleanup(cleanup_string)))
5239
char *socket_filename = strdup("/nonexistent");
5240
g_assert_nonnull(find_matching_task(queue, (task_context){
5241
.func=connect_question_socket,
5242
.question_filename=tempfilename,
5243
.filename=socket_filename,
5245
.password=&password,
5246
.current_time=¤t_time,
5247
.mandos_client_exited=&mandos_client_exited,
5248
.password_is_read=&password_is_read,
5250
g_assert_true(queue->next_run != 0);
5252
g_assert_cmpint(unlink(tempfilename), ==, 0);
5256
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5258
const mono_microsecs
5260
const mono_microsecs
5262
__attribute__((cleanup(cleanup_close)))
5263
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5264
g_assert_cmpint(epoll_fd, >=, 0);
5265
__attribute__((cleanup(string_set_clear)))
5266
string_set cancelled_filenames = {};
5267
buffer password = {};
5268
bool mandos_client_exited = false;
5269
bool password_is_read = false;
5270
__attribute__((cleanup(cleanup_queue)))
5271
task_queue *queue = create_queue();
5272
g_assert_nonnull(queue);
5273
queue->next_run = next_queue_run;
5275
__attribute__((cleanup(cleanup_string)))
5276
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5277
g_assert_nonnull(tempfilename);
5278
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5279
g_assert_cmpint(questionfile, >, 0);
5280
FILE *qf = fdopen(questionfile, "w");
5281
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5282
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5283
(uintmax_t)getpid(), notafter), >, 0);
5284
g_assert_cmpint(fclose(qf), ==, 0);
5286
char *const filename = strdup(tempfilename);
5287
g_assert_nonnull(filename);
5288
task_context task = {
5289
.func=open_and_parse_question,
5290
.question_filename=filename,
5292
.password=&password,
5294
.cancelled_filenames=&cancelled_filenames,
5295
.current_time=¤t_time,
5296
.mandos_client_exited=&mandos_client_exited,
5297
.password_is_read=&password_is_read,
5299
task.func(task, queue);
5301
if(queue->length >= 1){
5302
__attribute__((cleanup(cleanup_string)))
5303
char *socket_filename = strdup("/nonexistent");
5304
g_assert_nonnull(find_matching_task(queue, (task_context){
5305
.func=connect_question_socket,
5306
.filename=socket_filename,
5308
.password=&password,
5309
.current_time=¤t_time,
5310
.cancelled_filenames=&cancelled_filenames,
5311
.mandos_client_exited=&mandos_client_exited,
5312
.password_is_read=&password_is_read,
5314
g_assert_true(queue->next_run != 0);
5318
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5319
} else if(current_time >= notafter) {
5320
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5322
g_assert_nonnull(find_matching_task(queue, (task_context){
5323
.func=cancel_old_question,
5324
.question_filename=tempfilename,
5325
.filename=tempfilename,
5327
.cancelled_filenames=&cancelled_filenames,
5328
.current_time=¤t_time,
5331
g_assert_true(queue->next_run == 1);
5333
g_assert_cmpint(unlink(tempfilename), ==, 0);
5337
test_open_and_parse_question_notafter_0(__attribute__((unused))
5338
test_fixture *fixture,
5339
__attribute__((unused))
5340
gconstpointer user_data){
5341
/* current_time, notafter, next_queue_run */
5342
assert_open_and_parse_question_with_notafter(0, 0, 0);
5346
test_open_and_parse_question_notafter_1(__attribute__((unused))
5347
test_fixture *fixture,
5348
__attribute__((unused))
5349
gconstpointer user_data){
5350
/* current_time, notafter, next_queue_run */
5351
assert_open_and_parse_question_with_notafter(0, 1, 0);
5355
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5356
test_fixture *fixture,
5357
__attribute__((unused))
5358
gconstpointer user_data){
5359
/* current_time, notafter, next_queue_run */
5360
assert_open_and_parse_question_with_notafter(0, 1, 1);
5364
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5365
test_fixture *fixture,
5366
__attribute__((unused))
5367
gconstpointer user_data){
5368
/* current_time, notafter, next_queue_run */
5369
assert_open_and_parse_question_with_notafter(0, 1, 2);
5373
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5374
test_fixture *fixture,
5375
__attribute__((unused))
5376
gconstpointer user_data){
5377
/* current_time, notafter, next_queue_run */
5378
assert_open_and_parse_question_with_notafter(1, 1, 0);
5382
test_open_and_parse_question_late_notafter(__attribute__((unused))
5383
test_fixture *fixture,
5384
__attribute__((unused))
5385
gconstpointer user_data){
5386
/* current_time, notafter, next_queue_run */
5387
assert_open_and_parse_question_with_notafter(2, 1, 0);
5390
static void assert_cancel_old_question_param(const mono_microsecs
5392
const mono_microsecs
5394
const mono_microsecs
5396
const mono_microsecs
5398
__attribute__((cleanup(string_set_clear)))
5399
string_set cancelled_filenames = {};
5400
__attribute__((cleanup(cleanup_queue)))
5401
task_queue *queue = create_queue();
5402
g_assert_nonnull(queue);
5403
queue->next_run = next_queue_run;
5405
char *const question_filename = strdup("/nonexistent");
5406
g_assert_nonnull(question_filename);
5407
task_context task = {
5408
.func=cancel_old_question,
5409
.question_filename=question_filename,
5410
.filename=question_filename,
5412
.cancelled_filenames=&cancelled_filenames,
5413
.current_time=¤t_time,
5415
task.func(task, queue);
5417
if(current_time >= notafter){
5418
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5419
g_assert_true(string_set_contains(cancelled_filenames,
5422
g_assert_nonnull(find_matching_task(queue, (task_context){
5423
.func=cancel_old_question,
5424
.question_filename=question_filename,
5425
.filename=question_filename,
5427
.cancelled_filenames=&cancelled_filenames,
5428
.current_time=¤t_time,
5431
g_assert_false(string_set_contains(cancelled_filenames,
5432
question_filename));
5434
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5435
(unsigned int)next_set_to);
5438
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5439
test_fixture *fixture,
5440
__attribute__((unused))
5441
gconstpointer user_data){
5442
/* next_queue_run unset,
5443
cancellation should happen because time has come,
5444
next_queue_run should be unchanged */
5445
/* next_queue_run, notafter, current_time, next_set_to */
5446
assert_cancel_old_question_param(0, 1, 2, 0);
5449
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5450
test_fixture *fixture,
5451
__attribute__((unused))
5452
gconstpointer user_data){
5453
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5454
and current_time is not yet notafter or greater,
5455
update value of next_queue_run to value of notafter */
5456
/* next_queue_run, notafter, current_time, next_set_to */
5457
assert_cancel_old_question_param(0, 2, 1, 2);
5460
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5461
test_fixture *fixture,
5462
__attribute__((unused))
5463
gconstpointer user_data){
5464
/* next_queue_run 1,
5465
cancellation should happen because time has come,
5466
next_queue_run should be unchanged */
5467
/* next_queue_run, notafter, current_time, next_set_to */
5468
assert_cancel_old_question_param(1, 2, 3, 1);
5471
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5472
test_fixture *fixture,
5473
__attribute__((unused))
5474
gconstpointer user_data){
5475
/* If next_queue_run is set,
5476
and current_time is not yet notafter or greater,
5477
and notafter is larger than next_queue_run
5478
next_queue_run should be unchanged */
5479
/* next_queue_run, notafter, current_time, next_set_to */
5480
assert_cancel_old_question_param(1, 3, 2, 1);
5483
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5484
test_fixture *fixture,
5485
__attribute__((unused))
5486
gconstpointer user_data){
5487
/* next_queue_run 2,
5488
cancellation should happen because time has come,
5489
next_queue_run should be unchanged */
5490
/* next_queue_run, notafter, current_time, next_set_to */
5491
assert_cancel_old_question_param(2, 1, 3, 2);
5494
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5495
test_fixture *fixture,
5496
__attribute__((unused))
5497
gconstpointer user_data){
5498
/* If next_queue_run is set,
5499
and current_time is not yet notafter or greater,
5500
and notafter is larger than next_queue_run
5501
next_queue_run should be unchanged */
5502
/* next_queue_run, notafter, current_time, next_set_to */
5503
assert_cancel_old_question_param(2, 3, 1, 2);
5506
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5507
test_fixture *fixture,
5508
__attribute__((unused))
5509
gconstpointer user_data){
5510
/* next_queue_run 3,
5511
cancellation should happen because time has come,
5512
next_queue_run should be unchanged */
5513
/* next_queue_run, notafter, current_time, next_set_to */
5514
assert_cancel_old_question_param(3, 1, 2, 3);
5517
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5518
test_fixture *fixture,
5519
__attribute__((unused))
5520
gconstpointer user_data){
5521
/* If next_queue_run is set,
5522
and current_time is not yet notafter or greater,
5523
and notafter is smaller than next_queue_run
5524
update value of next_queue_run to value of notafter */
5525
/* next_queue_run, notafter, current_time, next_set_to */
5526
assert_cancel_old_question_param(3, 2, 1, 2);
5530
test_connect_question_socket_name_too_long(__attribute__((unused))
5531
test_fixture *fixture,
5532
__attribute__((unused))
5533
gconstpointer user_data){
5534
__attribute__((cleanup(cleanup_close)))
5535
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5536
g_assert_cmpint(epoll_fd, >=, 0);
5537
const char question_filename[] = "/nonexistent/question";
5538
__attribute__((cleanup(string_set_clear)))
5539
string_set cancelled_filenames = {};
5540
__attribute__((cleanup(cleanup_queue)))
5541
task_queue *queue = create_queue();
5542
g_assert_nonnull(queue);
5543
__attribute__((cleanup(cleanup_string)))
5544
char *tempdir = make_temporary_directory();
5545
g_assert_nonnull(tempdir);
5546
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5547
char socket_name[sizeof(unix_socket.sun_path)];
5548
memset(socket_name, 'x', sizeof(socket_name));
5549
socket_name[sizeof(socket_name)-1] = '\0';
5550
char *filename = NULL;
5551
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5553
g_assert_nonnull(filename);
5555
task_context task = {
5556
.func=connect_question_socket,
5557
.question_filename=strdup(question_filename),
5559
.password=(buffer[]){{}},
5561
.cancelled_filenames=&cancelled_filenames,
5562
.mandos_client_exited=(bool[]){false},
5563
.password_is_read=(bool[]){false},
5564
.current_time=(mono_microsecs[]){0},
5566
g_assert_nonnull(task.question_filename);
5567
run_task_with_stderr_to_dev_null(task, queue);
5569
g_assert_true(string_set_contains(cancelled_filenames,
5570
question_filename));
5571
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5572
g_assert_true(queue->next_run == 0);
5574
g_assert_cmpint(rmdir(tempdir), ==, 0);
5578
void test_connect_question_socket_connect_fail(__attribute__((unused))
5579
test_fixture *fixture,
5580
__attribute__((unused))
5583
__attribute__((cleanup(cleanup_close)))
5584
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5585
g_assert_cmpint(epoll_fd, >=, 0);
5586
const char question_filename[] = "/nonexistent/question";
5587
__attribute__((cleanup(string_set_clear)))
5588
string_set cancelled_filenames = {};
5589
const mono_microsecs current_time = 3;
5590
__attribute__((cleanup(cleanup_queue)))
5591
task_queue *queue = create_queue();
5592
g_assert_nonnull(queue);
5593
__attribute__((cleanup(cleanup_string)))
5594
char *tempdir = make_temporary_directory();
5595
g_assert_nonnull(tempdir);
5596
char socket_name[] = "nonexistent";
5597
char *filename = NULL;
5598
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5600
g_assert_nonnull(filename);
5602
task_context task = {
5603
.func=connect_question_socket,
5604
.question_filename=strdup(question_filename),
5606
.password=(buffer[]){{}},
5608
.cancelled_filenames=&cancelled_filenames,
5609
.mandos_client_exited=(bool[]){false},
5610
.password_is_read=(bool[]){false},
5611
.current_time=¤t_time,
5613
g_assert_nonnull(task.question_filename);
5614
run_task_with_stderr_to_dev_null(task, queue);
5616
g_assert_nonnull(find_matching_task(queue, task));
5618
g_assert_false(string_set_contains(cancelled_filenames,
5619
question_filename));
5620
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5621
g_assert_true(queue->next_run == 1000000 + current_time);
5623
g_assert_cmpint(rmdir(tempdir), ==, 0);
5627
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5628
test_fixture *fixture,
5629
__attribute__((unused))
5630
gconstpointer user_data){
5631
__attribute__((cleanup(cleanup_close)))
5632
const int epoll_fd = open("/dev/null",
5633
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5634
__attribute__((cleanup(cleanup_string)))
5635
char *const question_filename = strdup("/nonexistent/question");
5636
g_assert_nonnull(question_filename);
5637
__attribute__((cleanup(string_set_clear)))
5638
string_set cancelled_filenames = {};
5639
const mono_microsecs current_time = 5;
5640
__attribute__((cleanup(cleanup_queue)))
5641
task_queue *queue = create_queue();
5642
g_assert_nonnull(queue);
5643
__attribute__((cleanup(cleanup_string)))
5644
char *tempdir = make_temporary_directory();
5645
g_assert_nonnull(tempdir);
5646
__attribute__((cleanup(cleanup_close)))
5647
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5648
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5649
g_assert_cmpint(sock_fd, >=, 0);
5650
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5651
const char socket_name[] = "socket_name";
5652
__attribute__((cleanup(cleanup_string)))
5653
char *filename = NULL;
5654
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5656
g_assert_nonnull(filename);
5657
g_assert_cmpint((int)strlen(filename), <,
5658
(int)sizeof(sock_name.sun_path));
5659
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5660
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5661
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5662
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5663
task_context task = {
5664
.func=connect_question_socket,
5665
.question_filename=strdup(question_filename),
5667
.password=(buffer[]){{}},
5668
.filename=strdup(filename),
5669
.cancelled_filenames=&cancelled_filenames,
5670
.mandos_client_exited=(bool[]){false},
5671
.password_is_read=(bool[]){false},
5672
.current_time=¤t_time,
5674
g_assert_nonnull(task.question_filename);
5675
run_task_with_stderr_to_dev_null(task, queue);
5677
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5678
const task_context *const added_task
5679
= find_matching_task(queue, task);
5680
g_assert_nonnull(added_task);
5681
g_assert_true(queue->next_run == 1000000 + current_time);
5683
g_assert_cmpint(unlink(filename), ==, 0);
5684
g_assert_cmpint(rmdir(tempdir), ==, 0);
5688
void test_connect_question_socket_usable(__attribute__((unused))
5689
test_fixture *fixture,
5690
__attribute__((unused))
5691
gconstpointer user_data){
5692
__attribute__((cleanup(cleanup_close)))
5693
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5694
g_assert_cmpint(epoll_fd, >=, 0);
5695
__attribute__((cleanup(cleanup_string)))
5696
char *const question_filename = strdup("/nonexistent/question");
5697
g_assert_nonnull(question_filename);
5698
__attribute__((cleanup(string_set_clear)))
5699
string_set cancelled_filenames = {};
5700
buffer password = {};
5701
bool mandos_client_exited = false;
5702
bool password_is_read = false;
5703
const mono_microsecs current_time = 0;
5704
__attribute__((cleanup(cleanup_queue)))
5705
task_queue *queue = create_queue();
5706
g_assert_nonnull(queue);
5707
__attribute__((cleanup(cleanup_string)))
5708
char *tempdir = make_temporary_directory();
5709
g_assert_nonnull(tempdir);
5710
__attribute__((cleanup(cleanup_close)))
5711
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5712
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5713
g_assert_cmpint(sock_fd, >=, 0);
5714
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5715
const char socket_name[] = "socket_name";
5716
__attribute__((cleanup(cleanup_string)))
5717
char *filename = NULL;
5718
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5720
g_assert_nonnull(filename);
5721
g_assert_cmpint((int)strlen(filename), <,
5722
(int)sizeof(sock_name.sun_path));
5723
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5724
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5725
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5726
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5727
task_context task = {
5728
.func=connect_question_socket,
5729
.question_filename=strdup(question_filename),
5731
.password=&password,
5732
.filename=strdup(filename),
5733
.cancelled_filenames=&cancelled_filenames,
5734
.mandos_client_exited=&mandos_client_exited,
5735
.password_is_read=&password_is_read,
5736
.current_time=¤t_time,
5738
g_assert_nonnull(task.question_filename);
5739
task.func(task, queue);
5741
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5742
const task_context *const added_task
5743
= find_matching_task(queue, (task_context){
5744
.func=send_password_to_socket,
5745
.question_filename=question_filename,
5748
.password=&password,
5749
.cancelled_filenames=&cancelled_filenames,
5750
.mandos_client_exited=&mandos_client_exited,
5751
.password_is_read=&password_is_read,
5752
.current_time=¤t_time,
5754
g_assert_nonnull(added_task);
5755
g_assert_cmpint(added_task->fd, >, 0);
5757
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5760
const int fd = added_task->fd;
5761
g_assert_cmpint(fd, >, 0);
5762
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5765
char write_data[PIPE_BUF];
5767
/* Construct test password buffer */
5768
/* Start with + since that is what the real procotol uses */
5769
write_data[0] = '+';
5770
/* Set a special character at string end just to mark the end */
5771
write_data[sizeof(write_data)-2] = 'y';
5772
/* Set NUL at buffer end, as suggested by the protocol */
5773
write_data[sizeof(write_data)-1] = '\0';
5774
/* Fill rest of password with 'x' */
5775
memset(write_data+1, 'x', sizeof(write_data)-3);
5776
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5777
MSG_NOSIGNAL), ==, sizeof(write_data));
5780
/* read from sock_fd */
5781
char read_data[sizeof(write_data)];
5782
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5783
==, sizeof(read_data));
5785
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5788
/* writing to sock_fd should fail */
5789
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5790
MSG_NOSIGNAL), <, 0);
5792
/* reading from fd should fail */
5793
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5794
MSG_NOSIGNAL), <, 0);
5796
g_assert_cmpint(unlink(filename), ==, 0);
5797
g_assert_cmpint(rmdir(tempdir), ==, 0);
5801
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5802
test_fixture *fixture,
5803
__attribute__((unused))
5806
__attribute__((cleanup(cleanup_close)))
5807
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5808
g_assert_cmpint(epoll_fd, >=, 0);
5809
__attribute__((cleanup(cleanup_string)))
5810
char *const question_filename = strdup("/nonexistent/question");
5811
g_assert_nonnull(question_filename);
5812
__attribute__((cleanup(cleanup_string)))
5813
char *const filename = strdup("/nonexistent/socket");
5814
g_assert_nonnull(filename);
5815
__attribute__((cleanup(string_set_clear)))
5816
string_set cancelled_filenames = {};
5817
buffer password = {};
5818
bool password_is_read = true;
5819
__attribute__((cleanup(cleanup_queue)))
5820
task_queue *queue = create_queue();
5821
g_assert_nonnull(queue);
5823
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5824
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5826
__attribute__((cleanup(cleanup_close)))
5827
const int read_socket = socketfds[0];
5828
const int write_socket = socketfds[1];
5829
task_context task = {
5830
.func=send_password_to_socket,
5831
.question_filename=strdup(question_filename),
5832
.filename=strdup(filename),
5835
.password=&password,
5836
.cancelled_filenames=&cancelled_filenames,
5837
.mandos_client_exited=(bool[]){false},
5838
.password_is_read=&password_is_read,
5839
.current_time=(mono_microsecs[]){0},
5841
g_assert_nonnull(task.question_filename);
5843
task.func(task, queue);
5845
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5847
const task_context *const added_task
5848
= find_matching_task(queue, task);
5849
g_assert_nonnull(added_task);
5850
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5851
g_assert_true(password_is_read);
5853
g_assert_cmpint(added_task->fd, >, 0);
5854
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5859
test_send_password_to_socket_password_not_read(__attribute__((unused))
5860
test_fixture *fixture,
5861
__attribute__((unused))
5864
__attribute__((cleanup(cleanup_close)))
5865
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5866
g_assert_cmpint(epoll_fd, >=, 0);
5867
__attribute__((cleanup(cleanup_string)))
5868
char *const question_filename = strdup("/nonexistent/question");
5869
g_assert_nonnull(question_filename);
5870
__attribute__((cleanup(cleanup_string)))
5871
char *const filename = strdup("/nonexistent/socket");
5872
__attribute__((cleanup(string_set_clear)))
5873
string_set cancelled_filenames = {};
5874
buffer password = {};
5875
__attribute__((cleanup(cleanup_queue)))
5876
task_queue *queue = create_queue();
5877
g_assert_nonnull(queue);
5879
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5880
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5882
__attribute__((cleanup(cleanup_close)))
5883
const int read_socket = socketfds[0];
5884
const int write_socket = socketfds[1];
5885
task_context task = {
5886
.func=send_password_to_socket,
5887
.question_filename=strdup(question_filename),
5888
.filename=strdup(filename),
5891
.password=&password,
5892
.cancelled_filenames=&cancelled_filenames,
5893
.mandos_client_exited=(bool[]){false},
5894
.password_is_read=(bool[]){false},
5895
.current_time=(mono_microsecs[]){0},
5897
g_assert_nonnull(task.question_filename);
5899
task.func(task, queue);
5901
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5903
const task_context *const added_task = find_matching_task(queue,
5905
g_assert_nonnull(added_task);
5906
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5907
g_assert_true(queue->next_run == 0);
5909
g_assert_cmpint(added_task->fd, >, 0);
5910
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5915
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5916
test_fixture *fixture,
5917
__attribute__((unused))
5918
gconstpointer user_data){
5919
__attribute__((cleanup(cleanup_close)))
5920
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5921
g_assert_cmpint(epoll_fd, >=, 0);
5922
const char question_filename[] = "/nonexistent/question";
5923
char *const filename = strdup("/nonexistent/socket");
5924
__attribute__((cleanup(string_set_clear)))
5925
string_set cancelled_filenames = {};
5926
const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5927
__attribute__((cleanup(cleanup_buffer)))
5929
.data=malloc(oversized),
5931
.allocated=oversized,
5933
g_assert_nonnull(password.data);
5934
if(mlock(password.data, password.allocated) != 0){
5935
g_assert_true(errno == EPERM or errno == ENOMEM);
5937
/* Construct test password buffer */
5938
/* Start with + since that is what the real procotol uses */
5939
password.data[0] = '+';
5940
/* Set a special character at string end just to mark the end */
5941
password.data[oversized-3] = 'y';
5942
/* Set NUL at buffer end, as suggested by the protocol */
5943
password.data[oversized-2] = '\0';
5944
/* Fill rest of password with 'x' */
5945
memset(password.data+1, 'x', oversized-3);
5947
__attribute__((cleanup(cleanup_queue)))
5948
task_queue *queue = create_queue();
5949
g_assert_nonnull(queue);
5951
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5952
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5954
__attribute__((cleanup(cleanup_close)))
5955
const int read_socket = socketfds[0];
5956
__attribute__((cleanup(cleanup_close)))
5957
const int write_socket = socketfds[1];
5958
task_context task = {
5959
.func=send_password_to_socket,
5960
.question_filename=strdup(question_filename),
5964
.password=&password,
5965
.cancelled_filenames=&cancelled_filenames,
5966
.mandos_client_exited=(bool[]){true},
5967
.password_is_read=(bool[]){true},
5968
.current_time=(mono_microsecs[]){0},
5970
g_assert_nonnull(task.question_filename);
5972
run_task_with_stderr_to_dev_null(task, queue);
5974
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5975
g_assert_true(string_set_contains(cancelled_filenames,
5976
question_filename));
5979
static void test_send_password_to_socket_retry(__attribute__((unused))
5980
test_fixture *fixture,
5981
__attribute__((unused))
5984
__attribute__((cleanup(cleanup_close)))
5985
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5986
g_assert_cmpint(epoll_fd, >=, 0);
5987
__attribute__((cleanup(cleanup_string)))
5988
char *const question_filename = strdup("/nonexistent/question");
5989
g_assert_nonnull(question_filename);
5990
__attribute__((cleanup(cleanup_string)))
5991
char *const filename = strdup("/nonexistent/socket");
5992
g_assert_nonnull(filename);
5993
__attribute__((cleanup(string_set_clear)))
5994
string_set cancelled_filenames = {};
5995
__attribute__((cleanup(cleanup_buffer)))
5996
buffer password = {};
5998
__attribute__((cleanup(cleanup_queue)))
5999
task_queue *queue = create_queue();
6000
g_assert_nonnull(queue);
6002
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6003
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6005
__attribute__((cleanup(cleanup_close)))
6006
const int read_socket = socketfds[0];
6007
const int write_socket = socketfds[1];
6008
/* Close the server side socket to force ECONNRESET on client */
6009
g_assert_cmpint(close(read_socket), ==, 0);
6010
task_context task = {
6011
.func=send_password_to_socket,
6012
.question_filename=strdup(question_filename),
6013
.filename=strdup(filename),
6016
.password=&password,
6017
.cancelled_filenames=&cancelled_filenames,
6018
.mandos_client_exited=(bool[]){true},
6019
.password_is_read=(bool[]){true},
6020
.current_time=(mono_microsecs[]){0},
6022
g_assert_nonnull(task.question_filename);
6024
task.func(task, queue);
6026
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6028
const task_context *const added_task = find_matching_task(queue,
6030
g_assert_nonnull(added_task);
6031
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6033
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6038
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6039
test_fixture *fixture,
6040
__attribute__((unused))
6041
gconstpointer user_data){
6042
__attribute__((cleanup(cleanup_close)))
6043
const int epoll_fd = open("/dev/null",
6044
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6045
__attribute__((cleanup(cleanup_string)))
6046
char *const question_filename = strdup("/nonexistent/question");
6047
g_assert_nonnull(question_filename);
6048
__attribute__((cleanup(cleanup_string)))
6049
char *const filename = strdup("/nonexistent/socket");
6050
g_assert_nonnull(filename);
6051
__attribute__((cleanup(string_set_clear)))
6052
string_set cancelled_filenames = {};
6053
__attribute__((cleanup(cleanup_buffer)))
6054
buffer password = {};
6056
const mono_microsecs current_time = 11;
6057
__attribute__((cleanup(cleanup_queue)))
6058
task_queue *queue = create_queue();
6059
g_assert_nonnull(queue);
6061
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6062
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6064
__attribute__((cleanup(cleanup_close)))
6065
const int read_socket = socketfds[0];
6066
const int write_socket = socketfds[1];
6067
/* Close the server side socket to force ECONNRESET on client */
6068
g_assert_cmpint(close(read_socket), ==, 0);
6069
task_context task = {
6070
.func=send_password_to_socket,
6071
.question_filename=strdup(question_filename),
6072
.filename=strdup(filename),
6075
.password=&password,
6076
.cancelled_filenames=&cancelled_filenames,
6077
.mandos_client_exited=(bool[]){true},
6078
.password_is_read=(bool[]){true},
6079
.current_time=¤t_time,
6081
g_assert_nonnull(task.question_filename);
6083
run_task_with_stderr_to_dev_null(task, queue);
6085
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6087
const task_context *const added_task = find_matching_task(queue,
6089
g_assert_nonnull(added_task);
6090
g_assert_true(queue->next_run == current_time + 1000000);
6091
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6094
static void assert_send_password_to_socket_password(buffer password){
6095
__attribute__((cleanup(cleanup_close)))
6096
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6097
g_assert_cmpint(epoll_fd, >=, 0);
6098
char *const question_filename = strdup("/nonexistent/question");
6099
g_assert_nonnull(question_filename);
6100
char *const filename = strdup("/nonexistent/socket");
6101
g_assert_nonnull(filename);
6102
__attribute__((cleanup(string_set_clear)))
6103
string_set cancelled_filenames = {};
6105
__attribute__((cleanup(cleanup_queue)))
6106
task_queue *queue = create_queue();
6107
g_assert_nonnull(queue);
6109
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6110
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6112
__attribute__((cleanup(cleanup_close)))
6113
const int read_socket = socketfds[0];
6114
const int write_socket = socketfds[1];
6115
task_context task = {
6116
.func=send_password_to_socket,
6117
.question_filename=question_filename,
6121
.password=&password,
6122
.cancelled_filenames=&cancelled_filenames,
6123
.mandos_client_exited=(bool[]){true},
6124
.password_is_read=(bool[]){true},
6125
.current_time=(mono_microsecs[]){0},
6128
char *expected_written_data = malloc(password.length + 2);
6129
g_assert_nonnull(expected_written_data);
6130
expected_written_data[0] = '+';
6131
expected_written_data[password.length + 1] = '\0';
6132
if(password.length > 0){
6133
g_assert_nonnull(password.data);
6134
memcpy(expected_written_data + 1, password.data, password.length);
6137
task.func(task, queue);
6140
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6141
(int)(password.length + 2));
6142
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6144
g_assert_true(memcmp(expected_written_data, buf,
6145
password.length + 2) == 0);
6147
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6149
free(expected_written_data);
6153
test_send_password_to_socket_null_password(__attribute__((unused))
6154
test_fixture *fixture,
6155
__attribute__((unused))
6156
gconstpointer user_data){
6157
__attribute__((cleanup(cleanup_buffer)))
6158
buffer password = {};
6159
assert_send_password_to_socket_password(password);
6163
test_send_password_to_socket_empty_password(__attribute__((unused))
6164
test_fixture *fixture,
6165
__attribute__((unused))
6166
gconstpointer user_data){
6167
__attribute__((cleanup(cleanup_buffer)))
6169
.data=malloc(1), /* because malloc(0) may return NULL */
6171
.allocated=0, /* deliberate lie */
6173
g_assert_nonnull(password.data);
6174
assert_send_password_to_socket_password(password);
6178
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6179
test_fixture *fixture,
6180
__attribute__((unused))
6181
gconstpointer user_data){
6182
__attribute__((cleanup(cleanup_buffer)))
6188
if(mlock(password.data, password.allocated) != 0){
6189
g_assert_true(errno == EPERM or errno == ENOMEM);
6191
assert_send_password_to_socket_password(password);
6195
test_send_password_to_socket_text_password(__attribute__((unused))
6196
test_fixture *fixture,
6197
__attribute__((unused))
6198
gconstpointer user_data){
6199
const char dummy_test_password[] = "dummy test password";
6200
__attribute__((cleanup(cleanup_buffer)))
6202
.data = strdup(dummy_test_password),
6203
.length = strlen(dummy_test_password),
6204
.allocated = sizeof(dummy_test_password),
6206
if(mlock(password.data, password.allocated) != 0){
6207
g_assert_true(errno == EPERM or errno == ENOMEM);
6209
assert_send_password_to_socket_password(password);
6213
test_send_password_to_socket_binary_password(__attribute__((unused))
6214
test_fixture *fixture,
6215
__attribute__((unused))
6216
gconstpointer user_data){
6217
__attribute__((cleanup(cleanup_buffer)))
6223
g_assert_nonnull(password.data);
6224
if(mlock(password.data, password.allocated) != 0){
6225
g_assert_true(errno == EPERM or errno == ENOMEM);
6227
char c = 1; /* Start at 1, avoiding NUL */
6228
for(int i=0; i < 255; i++){
6229
password.data[i] = c++;
6231
assert_send_password_to_socket_password(password);
6235
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6236
test_fixture *fixture,
6237
__attribute__((unused))
6240
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6241
__attribute__((cleanup(cleanup_buffer)))
6243
.data=malloc(sizeof(test_password)),
6244
.length=sizeof(test_password),
6245
.allocated=sizeof(test_password),
6247
g_assert_nonnull(password.data);
6248
if(mlock(password.data, password.allocated) !=0){
6249
g_assert_true(errno == EPERM or errno == ENOMEM);
6251
memcpy(password.data, test_password, password.allocated);
6252
assert_send_password_to_socket_password(password);
6255
static bool assert_add_existing_questions_to_devnull(task_queue
6268
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6269
test_fixture *fixture,
6270
__attribute__((unused))
6273
__attribute__((cleanup(cleanup_queue)))
6274
task_queue *queue = create_queue();
6275
g_assert_nonnull(queue);
6276
__attribute__((cleanup(cleanup_close)))
6277
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6278
g_assert_cmpint(epoll_fd, >=, 0);
6279
__attribute__((cleanup(string_set_clear)))
6280
string_set cancelled_filenames = {};
6282
g_assert_false(assert_add_existing_questions_to_devnull
6285
(buffer[]){{}}, /* password */
6286
&cancelled_filenames,
6287
(mono_microsecs[]){0}, /* current_time */
6288
(bool[]){false}, /* mandos_client_exited */
6289
(bool[]){false}, /* password_is_read */
6290
"/nonexistent")); /* dirname */
6292
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6296
bool assert_add_existing_questions_to_devnull(task_queue
6303
*cancelled_filenames,
6304
const mono_microsecs
6305
*const current_time,
6307
mandos_client_exited,
6312
__attribute__((cleanup(cleanup_close)))
6313
const int devnull_fd = open("/dev/null",
6314
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6315
g_assert_cmpint(devnull_fd, >=, 0);
6316
__attribute__((cleanup(cleanup_close)))
6317
const int real_stderr_fd = dup(STDERR_FILENO);
6318
g_assert_cmpint(real_stderr_fd, >=, 0);
6319
dup2(devnull_fd, STDERR_FILENO);
6320
const bool ret = add_existing_questions(queue, epoll_fd, password,
6321
cancelled_filenames,
6323
mandos_client_exited,
6324
password_is_read, dirname);
6325
dup2(real_stderr_fd, STDERR_FILENO);
6330
void test_add_existing_questions_no_questions(__attribute__((unused))
6331
test_fixture *fixture,
6332
__attribute__((unused))
6335
__attribute__((cleanup(cleanup_queue)))
6336
task_queue *queue = create_queue();
6337
g_assert_nonnull(queue);
6338
__attribute__((cleanup(cleanup_close)))
6339
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6340
g_assert_cmpint(epoll_fd, >=, 0);
6341
__attribute__((cleanup(string_set_clear)))
6342
string_set cancelled_filenames = {};
6343
__attribute__((cleanup(cleanup_string)))
6344
char *tempdir = make_temporary_directory();
6345
g_assert_nonnull(tempdir);
6347
g_assert_false(assert_add_existing_questions_to_devnull
6350
(buffer[]){{}}, /* password */
6351
&cancelled_filenames,
6352
(mono_microsecs[]){0}, /* current_time */
6353
(bool[]){false}, /* mandos_client_exited */
6354
(bool[]){false}, /* password_is_read */
6357
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6359
g_assert_cmpint(rmdir(tempdir), ==, 0);
6362
static char *make_question_file_in_directory(const char *const);
6365
void test_add_existing_questions_one_question(__attribute__((unused))
6366
test_fixture *fixture,
6367
__attribute__((unused))
6370
__attribute__((cleanup(cleanup_queue)))
6371
task_queue *queue = create_queue();
6372
g_assert_nonnull(queue);
6373
__attribute__((cleanup(cleanup_close)))
6374
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6375
g_assert_cmpint(epoll_fd, >=, 0);
6376
__attribute__((cleanup(cleanup_buffer)))
6377
buffer password = {};
6378
__attribute__((cleanup(string_set_clear)))
6379
string_set cancelled_filenames = {};
6380
const mono_microsecs current_time = 0;
6381
bool mandos_client_exited = false;
6382
bool password_is_read = false;
6383
__attribute__((cleanup(cleanup_string)))
6384
char *tempdir = make_temporary_directory();
6385
g_assert_nonnull(tempdir);
6386
__attribute__((cleanup(cleanup_string)))
6387
char *question_filename
6388
= make_question_file_in_directory(tempdir);
6389
g_assert_nonnull(question_filename);
6391
g_assert_true(assert_add_existing_questions_to_devnull
6395
&cancelled_filenames,
6397
&mandos_client_exited,
6401
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6403
g_assert_nonnull(find_matching_task(queue, (task_context){
6404
.func=open_and_parse_question,
6406
.filename=question_filename,
6407
.question_filename=question_filename,
6408
.password=&password,
6409
.cancelled_filenames=&cancelled_filenames,
6410
.current_time=¤t_time,
6411
.mandos_client_exited=&mandos_client_exited,
6412
.password_is_read=&password_is_read,
6415
g_assert_true(queue->next_run == 1);
6417
g_assert_cmpint(unlink(question_filename), ==, 0);
6418
g_assert_cmpint(rmdir(tempdir), ==, 0);
6421
static char *make_question_file_in_directory(const char
6423
return make_temporary_prefixed_file_in_directory("ask.", dir);
6427
void test_add_existing_questions_two_questions(__attribute__((unused))
6428
test_fixture *fixture,
6429
__attribute__((unused))
6432
__attribute__((cleanup(cleanup_queue)))
6433
task_queue *queue = create_queue();
6434
g_assert_nonnull(queue);
6435
__attribute__((cleanup(cleanup_close)))
6436
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6437
g_assert_cmpint(epoll_fd, >=, 0);
6438
__attribute__((cleanup(cleanup_buffer)))
6439
buffer password = {};
6440
__attribute__((cleanup(string_set_clear)))
6441
string_set cancelled_filenames = {};
6442
const mono_microsecs current_time = 0;
6443
bool mandos_client_exited = false;
6444
bool password_is_read = false;
6445
__attribute__((cleanup(cleanup_string)))
6446
char *tempdir = make_temporary_directory();
6447
g_assert_nonnull(tempdir);
6448
__attribute__((cleanup(cleanup_string)))
6449
char *question_filename1
6450
= make_question_file_in_directory(tempdir);
6451
g_assert_nonnull(question_filename1);
6452
__attribute__((cleanup(cleanup_string)))
6453
char *question_filename2
6454
= make_question_file_in_directory(tempdir);
6455
g_assert_nonnull(question_filename2);
6457
g_assert_true(assert_add_existing_questions_to_devnull
6461
&cancelled_filenames,
6463
&mandos_client_exited,
6467
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6469
g_assert_true(queue->next_run == 1);
6471
__attribute__((cleanup(string_set_clear)))
6472
string_set seen_questions = {};
6474
bool queue_contains_question_opener(char *const question_filename){
6475
return(find_matching_task(queue, (task_context){
6476
.func=open_and_parse_question,
6478
.question_filename=question_filename,
6479
.password=&password,
6480
.cancelled_filenames=&cancelled_filenames,
6481
.current_time=¤t_time,
6482
.mandos_client_exited=&mandos_client_exited,
6483
.password_is_read=&password_is_read,
6487
g_assert_true(queue_contains_question_opener(question_filename1));
6488
g_assert_true(queue_contains_question_opener(question_filename2));
6490
g_assert_true(queue->next_run == 1);
6492
g_assert_cmpint(unlink(question_filename1), ==, 0);
6493
g_assert_cmpint(unlink(question_filename2), ==, 0);
6494
g_assert_cmpint(rmdir(tempdir), ==, 0);
6498
test_add_existing_questions_non_questions(__attribute__((unused))
6499
test_fixture *fixture,
6500
__attribute__((unused))
6501
gconstpointer user_data){
6502
__attribute__((cleanup(cleanup_queue)))
6503
task_queue *queue = create_queue();
6504
g_assert_nonnull(queue);
6505
__attribute__((cleanup(cleanup_close)))
6506
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6507
g_assert_cmpint(epoll_fd, >=, 0);
6508
__attribute__((cleanup(string_set_clear)))
6509
string_set cancelled_filenames = {};
6510
__attribute__((cleanup(cleanup_string)))
6511
char *tempdir = make_temporary_directory();
6512
g_assert_nonnull(tempdir);
6513
__attribute__((cleanup(cleanup_string)))
6514
char *question_filename1
6515
= make_temporary_file_in_directory(tempdir);
6516
g_assert_nonnull(question_filename1);
6517
__attribute__((cleanup(cleanup_string)))
6518
char *question_filename2
6519
= make_temporary_file_in_directory(tempdir);
6520
g_assert_nonnull(question_filename2);
6522
g_assert_false(assert_add_existing_questions_to_devnull
6525
(buffer[]){{}}, /* password */
6526
&cancelled_filenames,
6527
(mono_microsecs[]){0}, /* current_time */
6528
(bool[]){false}, /* mandos_client_exited */
6529
(bool[]){false}, /* password_is_read */
6532
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6534
g_assert_cmpint(unlink(question_filename1), ==, 0);
6535
g_assert_cmpint(unlink(question_filename2), ==, 0);
6536
g_assert_cmpint(rmdir(tempdir), ==, 0);
6540
test_add_existing_questions_both_types(__attribute__((unused))
6541
test_fixture *fixture,
6542
__attribute__((unused))
6543
gconstpointer user_data){
6544
__attribute__((cleanup(cleanup_queue)))
6545
task_queue *queue = create_queue();
6546
g_assert_nonnull(queue);
6547
__attribute__((cleanup(cleanup_close)))
6548
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6549
g_assert_cmpint(epoll_fd, >=, 0);
6550
__attribute__((cleanup(cleanup_buffer)))
6551
buffer password = {};
6552
__attribute__((cleanup(string_set_clear)))
6553
string_set cancelled_filenames = {};
6554
const mono_microsecs current_time = 0;
6555
bool mandos_client_exited = false;
6556
bool password_is_read = false;
6557
__attribute__((cleanup(cleanup_string)))
6558
char *tempdir = make_temporary_directory();
6559
g_assert_nonnull(tempdir);
6560
__attribute__((cleanup(cleanup_string)))
6561
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6562
g_assert_nonnull(tempfilename1);
6563
__attribute__((cleanup(cleanup_string)))
6564
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6565
g_assert_nonnull(tempfilename2);
6566
__attribute__((cleanup(cleanup_string)))
6567
char *question_filename
6568
= make_question_file_in_directory(tempdir);
6569
g_assert_nonnull(question_filename);
6571
g_assert_true(assert_add_existing_questions_to_devnull
6575
&cancelled_filenames,
6577
&mandos_client_exited,
6581
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6583
g_assert_nonnull(find_matching_task(queue, (task_context){
6584
.func=open_and_parse_question,
6586
.filename=question_filename,
6587
.question_filename=question_filename,
6588
.password=&password,
6589
.cancelled_filenames=&cancelled_filenames,
6590
.current_time=¤t_time,
6591
.mandos_client_exited=&mandos_client_exited,
6592
.password_is_read=&password_is_read,
6595
g_assert_true(queue->next_run == 1);
6597
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6598
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6599
g_assert_cmpint(unlink(question_filename), ==, 0);
6600
g_assert_cmpint(rmdir(tempdir), ==, 0);
6603
static void test_wait_for_event_timeout(__attribute__((unused))
6604
test_fixture *fixture,
6605
__attribute__((unused))
6606
gconstpointer user_data){
6607
__attribute__((cleanup(cleanup_close)))
6608
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6609
g_assert_cmpint(epoll_fd, >=, 0);
6611
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6614
static void test_wait_for_event_event(__attribute__((unused))
6615
test_fixture *fixture,
6616
__attribute__((unused))
6617
gconstpointer user_data){
6618
__attribute__((cleanup(cleanup_close)))
6619
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6620
g_assert_cmpint(epoll_fd, >=, 0);
6622
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6623
__attribute__((cleanup(cleanup_close)))
6624
const int read_pipe = pipefds[0];
6625
__attribute__((cleanup(cleanup_close)))
6626
const int write_pipe = pipefds[1];
6627
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6628
&(struct epoll_event)
6629
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6630
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6632
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6635
static void test_wait_for_event_sigchld(test_fixture *fixture,
6636
__attribute__((unused))
6637
gconstpointer user_data){
6638
const pid_t pid = fork();
6639
if(pid == 0){ /* Child */
6640
if(not restore_signal_handler(&fixture->orig_sigaction)){
6641
_exit(EXIT_FAILURE);
6643
if(not restore_sigmask(&fixture->orig_sigmask)){
6644
_exit(EXIT_FAILURE);
6648
g_assert_true(pid != -1);
6649
__attribute__((cleanup(cleanup_close)))
6650
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6651
g_assert_cmpint(epoll_fd, >=, 0);
6653
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6656
g_assert_true(waitpid(pid, &status, 0) == pid);
6657
g_assert_true(WIFEXITED(status));
6658
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6661
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6662
test_fixture *fixture,
6663
__attribute__((unused))
6664
gconstpointer user_data){
6665
__attribute__((cleanup(cleanup_queue)))
6666
task_queue *queue = create_queue();
6667
g_assert_nonnull(queue);
6668
queue->next_run = 1;
6669
__attribute__((cleanup(cleanup_close)))
6670
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6671
__attribute__((cleanup(string_set_clear)))
6672
string_set cancelled_filenames = {};
6673
bool quit_now = false;
6675
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6676
g_assert_false(quit_now);
6677
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6681
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6682
test_fixture *fixture,
6683
__attribute__((unused))
6686
__attribute__((cleanup(cleanup_queue)))
6687
task_queue *queue = create_queue();
6688
g_assert_nonnull(queue);
6689
__attribute__((cleanup(string_set_clear)))
6690
string_set cancelled_filenames = {};
6691
bool quit_now = false;
6692
const char question_filename[] = "/nonexistent/question_filename";
6693
g_assert_true(string_set_add(&cancelled_filenames,
6694
question_filename));
6696
g_assert_true(add_to_queue(queue,
6697
(task_context){ .func=dummy_func }));
6699
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6700
g_assert_false(quit_now);
6701
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6702
g_assert_false(string_set_contains(cancelled_filenames,
6703
question_filename));
6707
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6708
test_fixture *fixture,
6709
__attribute__((unused))
6712
__attribute__((cleanup(cleanup_queue)))
6713
task_queue *queue = create_queue();
6714
g_assert_nonnull(queue);
6715
__attribute__((cleanup(string_set_clear)))
6716
string_set cancelled_filenames = {};
6717
bool quit_now = false;
6719
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6720
__attribute__((cleanup(cleanup_close)))
6721
const int read_pipe = pipefds[0];
6722
g_assert_cmpint(close(pipefds[1]), ==, 0);
6723
const char question_filename[] = "/nonexistent/question_filename";
6724
g_assert_true(string_set_add(&cancelled_filenames,
6725
question_filename));
6726
__attribute__((nonnull))
6727
void quit_func(const task_context task,
6728
__attribute__((unused)) task_queue *const q){
6729
g_assert_nonnull(task.quit_now);
6730
*task.quit_now = true;
6732
task_context task = {
6734
.question_filename=strdup(question_filename),
6735
.quit_now=&quit_now,
6738
g_assert_nonnull(task.question_filename);
6740
g_assert_true(add_to_queue(queue, task));
6742
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6743
g_assert_false(quit_now);
6745
/* read_pipe should be closed already */
6747
bool read_pipe_closed = (close(read_pipe) == -1);
6748
read_pipe_closed &= (errno == EBADF);
6749
g_assert_true(read_pipe_closed);
6752
static void test_run_queue_one_task(__attribute__((unused))
6753
test_fixture *fixture,
6754
__attribute__((unused))
6755
gconstpointer user_data){
6756
__attribute__((cleanup(cleanup_queue)))
6757
task_queue *queue = create_queue();
6758
g_assert_nonnull(queue);
6759
__attribute__((cleanup(string_set_clear)))
6760
string_set cancelled_filenames = {};
6761
bool quit_now = false;
6763
__attribute__((nonnull))
6764
void next_run_func(__attribute__((unused))
6765
const task_context task,
6766
task_queue *const q){
6770
task_context task = {
6771
.func=next_run_func,
6773
g_assert_true(add_to_queue(queue, task));
6775
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6776
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6777
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6780
static void test_run_queue_two_tasks(__attribute__((unused))
6781
test_fixture *fixture,
6782
__attribute__((unused))
6783
gconstpointer user_data){
6784
__attribute__((cleanup(cleanup_queue)))
6785
task_queue *queue = create_queue();
6786
g_assert_nonnull(queue);
6787
queue->next_run = 1;
6788
__attribute__((cleanup(string_set_clear)))
6789
string_set cancelled_filenames = {};
6790
bool quit_now = false;
6791
bool mandos_client_exited = false;
6793
__attribute__((nonnull))
6794
void next_run_func(__attribute__((unused))
6795
const task_context task,
6796
task_queue *const q){
6800
__attribute__((nonnull))
6801
void exited_func(const task_context task,
6802
__attribute__((unused)) task_queue *const q){
6803
*task.mandos_client_exited = true;
6806
task_context task1 = {
6807
.func=next_run_func,
6809
g_assert_true(add_to_queue(queue, task1));
6811
task_context task2 = {
6813
.mandos_client_exited=&mandos_client_exited,
6815
g_assert_true(add_to_queue(queue, task2));
6817
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6818
g_assert_false(quit_now);
6819
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6820
g_assert_true(mandos_client_exited);
6821
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6824
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6825
test_fixture *fixture,
6826
__attribute__((unused))
6827
gconstpointer user_data){
6828
__attribute__((cleanup(cleanup_queue)))
6829
task_queue *queue = create_queue();
6830
g_assert_nonnull(queue);
6831
__attribute__((cleanup(string_set_clear)))
6832
string_set cancelled_filenames = {};
6833
bool quit_now = false;
6834
bool mandos_client_exited = false;
6835
bool password_is_read = false;
6837
__attribute__((nonnull))
6838
void set_exited_func(const task_context task,
6839
__attribute__((unused)) task_queue *const q){
6840
*task.mandos_client_exited = true;
6841
*task.quit_now = true;
6843
task_context task1 = {
6844
.func=set_exited_func,
6845
.quit_now=&quit_now,
6846
.mandos_client_exited=&mandos_client_exited,
6848
g_assert_true(add_to_queue(queue, task1));
6850
__attribute__((nonnull))
6851
void set_read_func(const task_context task,
6852
__attribute__((unused)) task_queue *const q){
6853
*task.quit_now = true;
6854
*task.password_is_read = true;
6856
task_context task2 = {
6857
.func=set_read_func,
6858
.quit_now=&quit_now,
6859
.password_is_read=&password_is_read,
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);
6865
g_assert_true(mandos_client_exited xor password_is_read);
6866
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6869
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6870
test_fixture *fixture,
6871
__attribute__((unused))
6872
gconstpointer user_data){
6873
__attribute__((cleanup(cleanup_queue)))
6874
task_queue *queue = create_queue();
6875
g_assert_nonnull(queue);
6876
__attribute__((cleanup(string_set_clear)))
6877
string_set cancelled_filenames = {};
6879
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6880
__attribute__((cleanup(cleanup_close)))
6881
const int read_pipe = pipefds[0];
6882
__attribute__((cleanup(cleanup_close)))
6883
const int write_pipe = pipefds[1];
6884
bool quit_now = false;
6886
__attribute__((nonnull))
6887
void read_func(const task_context task,
6888
__attribute__((unused)) task_queue *const q){
6889
*task.quit_now = true;
6891
task_context task1 = {
6893
.quit_now=&quit_now,
6896
g_assert_true(add_to_queue(queue, task1));
6898
__attribute__((nonnull))
6899
void write_func(const task_context task,
6900
__attribute__((unused)) task_queue *const q){
6901
*task.quit_now = true;
6903
task_context task2 = {
6905
.quit_now=&quit_now,
6908
g_assert_true(add_to_queue(queue, task2));
6910
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6911
g_assert_true(quit_now);
6913
/* Either read_pipe or write_pipe should be closed already */
6915
bool close_read_pipe = (close(read_pipe) == -1);
6916
close_read_pipe &= (errno == EBADF);
6918
bool close_write_pipe = (close(write_pipe) == -1);
6919
close_write_pipe &= (errno == EBADF);
6920
g_assert_true(close_read_pipe xor close_write_pipe);
6921
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6924
static void test_setup_signal_handler(__attribute__((unused))
6925
test_fixture *fixture,
6926
__attribute__((unused))
6927
gconstpointer user_data){
6928
/* Save current SIGCHLD action, whatever it is */
6929
struct sigaction expected_sigchld_action;
6930
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6933
/* Act; i.e. run the setup_signal_handler() function */
6934
struct sigaction actual_old_sigchld_action;
6935
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6937
/* Check that the function correctly set "actual_old_sigchld_action"
6938
to the same values as the previously saved
6939
"expected_sigchld_action" */
6940
/* Check member sa_handler */
6941
g_assert_true(actual_old_sigchld_action.sa_handler
6942
== expected_sigchld_action.sa_handler);
6943
/* Check member sa_mask */
6944
for(int signum = 1; signum < NSIG; signum++){
6945
const int expected_old_block_state
6946
= sigismember(&expected_sigchld_action.sa_mask, signum);
6947
g_assert_cmpint(expected_old_block_state, >=, 0);
6948
const int actual_old_block_state
6949
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
6950
g_assert_cmpint(actual_old_block_state, >=, 0);
6951
g_assert_cmpint(actual_old_block_state,
6952
==, expected_old_block_state);
6954
/* Check member sa_flags */
6955
g_assert_true((actual_old_sigchld_action.sa_flags
6956
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6957
== (expected_sigchld_action.sa_flags
6958
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6960
/* Retrieve the current signal handler for SIGCHLD as set by
6961
setup_signal_handler() */
6962
struct sigaction actual_new_sigchld_action;
6963
g_assert_cmpint(sigaction(SIGCHLD, NULL,
6964
&actual_new_sigchld_action), ==, 0);
6965
/* Check that the signal handler (member sa_handler) is correctly
6966
set to the "handle_sigchld" function */
6967
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6968
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6969
g_assert_true(actual_new_sigchld_action.sa_handler
6971
/* Check (in member sa_mask) that at least a handful of signals are
6972
actually blocked during the signal handler */
6973
for(int signum = 1; signum < NSIG; signum++){
6974
int actual_new_block_state;
6980
actual_new_block_state
6981
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
6982
g_assert_cmpint(actual_new_block_state, ==, 1);
6984
case SIGKILL: /* non-blockable */
6985
case SIGSTOP: /* non-blockable */
6986
case SIGCHLD: /* always blocked */
6991
/* Check member sa_flags */
6992
g_assert_true((actual_new_sigchld_action.sa_flags
6993
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6994
== (SA_NOCLDSTOP | SA_RESTART));
6996
/* Restore signal handler */
6997
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7001
static void test_restore_signal_handler(__attribute__((unused))
7002
test_fixture *fixture,
7003
__attribute__((unused))
7004
gconstpointer user_data){
7005
/* Save current SIGCHLD action, whatever it is */
7006
struct sigaction expected_sigchld_action;
7007
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7009
/* Since we haven't established a signal handler yet, there should
7010
not be one established. But another test may have relied on
7011
restore_signal_handler() to restore the signal handler, and if
7012
restore_signal_handler() is buggy (which we should be prepared
7013
for in this test) the signal handler may not have been restored
7014
properly; check for this: */
7015
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7017
/* Establish a signal handler */
7018
struct sigaction sigchld_action = {
7019
.sa_handler=handle_sigchld,
7020
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7022
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7023
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7025
/* Act; i.e. run the restore_signal_handler() function */
7026
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7028
/* Retrieve the restored signal handler data */
7029
struct sigaction actual_restored_sigchld_action;
7030
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7031
&actual_restored_sigchld_action), ==, 0);
7033
/* Check that the function correctly restored the signal action, as
7034
saved in "actual_restored_sigchld_action", to the same values as
7035
the previously saved "expected_sigchld_action" */
7036
/* Check member sa_handler */
7037
g_assert_true(actual_restored_sigchld_action.sa_handler
7038
== expected_sigchld_action.sa_handler);
7039
/* Check member sa_mask */
7040
for(int signum = 1; signum < NSIG; signum++){
7041
const int expected_old_block_state
7042
= sigismember(&expected_sigchld_action.sa_mask, signum);
7043
g_assert_cmpint(expected_old_block_state, >=, 0);
7044
const int actual_restored_block_state
7045
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7046
g_assert_cmpint(actual_restored_block_state, >=, 0);
7047
g_assert_cmpint(actual_restored_block_state,
7048
==, expected_old_block_state);
7050
/* Check member sa_flags */
7051
g_assert_true((actual_restored_sigchld_action.sa_flags
7052
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7053
== (expected_sigchld_action.sa_flags
7054
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7057
static void test_block_sigchld(__attribute__((unused))
7058
test_fixture *fixture,
7059
__attribute__((unused))
7060
gconstpointer user_data){
7061
/* Save original signal mask */
7062
sigset_t expected_sigmask;
7063
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7066
/* Make sure SIGCHLD is unblocked for this test */
7067
sigset_t sigchld_sigmask;
7068
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7069
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7070
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7073
/* Act; i.e. run the block_sigchld() function */
7074
sigset_t actual_old_sigmask;
7075
g_assert_true(block_sigchld(&actual_old_sigmask));
7077
/* Check the actual_old_sigmask; it should be the same as the
7078
previously saved signal mask "expected_sigmask". */
7079
for(int signum = 1; signum < NSIG; signum++){
7080
const int expected_old_block_state
7081
= sigismember(&expected_sigmask, signum);
7082
g_assert_cmpint(expected_old_block_state, >=, 0);
7083
const int actual_old_block_state
7084
= sigismember(&actual_old_sigmask, signum);
7085
g_assert_cmpint(actual_old_block_state, >=, 0);
7086
g_assert_cmpint(actual_old_block_state,
7087
==, expected_old_block_state);
7090
/* Retrieve the newly set signal mask */
7091
sigset_t actual_sigmask;
7092
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7094
/* SIGCHLD should be blocked */
7095
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7097
/* Restore signal mask */
7098
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7102
static void test_restore_sigmask(__attribute__((unused))
7103
test_fixture *fixture,
7104
__attribute__((unused))
7105
gconstpointer user_data){
7106
/* Save original signal mask */
7107
sigset_t orig_sigmask;
7108
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7110
/* Make sure SIGCHLD is blocked for this test */
7111
sigset_t sigchld_sigmask;
7112
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7113
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7114
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7117
/* Act; i.e. run the restore_sigmask() function */
7118
g_assert_true(restore_sigmask(&orig_sigmask));
7120
/* Retrieve the newly restored signal mask */
7121
sigset_t restored_sigmask;
7122
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7125
/* Check the restored_sigmask; it should be the same as the
7126
previously saved signal mask "orig_sigmask". */
7127
for(int signum = 1; signum < NSIG; signum++){
7128
const int orig_block_state = sigismember(&orig_sigmask, signum);
7129
g_assert_cmpint(orig_block_state, >=, 0);
7130
const int restored_block_state = sigismember(&restored_sigmask,
7132
g_assert_cmpint(restored_block_state, >=, 0);
7133
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7136
/* Restore signal mask */
7137
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7141
static void test_parse_arguments_noargs(__attribute__((unused))
7142
test_fixture *fixture,
7143
__attribute__((unused))
7144
gconstpointer user_data){
7148
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7150
char *agent_directory = NULL;
7151
char *helper_directory = NULL;
7154
char *mandos_argz = NULL;
7155
size_t mandos_argz_length = 0;
7157
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7158
&helper_directory, &user, &group,
7159
&mandos_argz, &mandos_argz_length));
7160
g_assert_null(agent_directory);
7161
g_assert_null(helper_directory);
7162
g_assert_true(user == 0);
7163
g_assert_true(group == 0);
7164
g_assert_null(mandos_argz);
7165
g_assert_true(mandos_argz_length == 0);
7167
for(char **arg = argv; *arg != NULL; arg++){
7172
__attribute__((nonnull))
7173
static bool parse_arguments_devnull(int argc, char *argv[],
7174
const bool exit_failure,
7175
char **agent_directory,
7176
char **helper_directory,
7180
size_t *mandos_argz_length){
7182
FILE *real_stderr = stderr;
7183
FILE *devnull = fopen("/dev/null", "we");
7184
g_assert_nonnull(devnull);
7187
const bool ret = parse_arguments(argc, argv, exit_failure,
7189
helper_directory, user, group,
7190
mandos_argz, mandos_argz_length);
7191
const error_t saved_errno = errno;
7193
stderr = real_stderr;
7194
g_assert_cmpint(fclose(devnull), ==, 0);
7196
errno = saved_errno;
7201
static void test_parse_arguments_invalid(__attribute__((unused))
7202
test_fixture *fixture,
7203
__attribute__((unused))
7204
gconstpointer user_data){
7207
strdup("--invalid"),
7209
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7211
char *agent_directory = NULL;
7212
char *helper_directory = NULL;
7215
char *mandos_argz = NULL;
7216
size_t mandos_argz_length = 0;
7218
g_assert_false(parse_arguments_devnull(argc, argv, false,
7220
&helper_directory, &user,
7221
&group, &mandos_argz,
7222
&mandos_argz_length));
7224
g_assert_true(errno == EINVAL);
7225
g_assert_null(agent_directory);
7226
g_assert_null(helper_directory);
7227
g_assert_null(mandos_argz);
7228
g_assert_true(mandos_argz_length == 0);
7230
for(char **arg = argv; *arg != NULL; arg++){
7235
static void test_parse_arguments_long_dir(__attribute__((unused))
7236
test_fixture *fixture,
7237
__attribute__((unused))
7238
gconstpointer user_data){
7241
strdup("--agent-directory"),
7244
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7246
__attribute__((cleanup(cleanup_string)))
7247
char *agent_directory = NULL;
7248
char *helper_directory = NULL;
7251
__attribute__((cleanup(cleanup_string)))
7252
char *mandos_argz = NULL;
7253
size_t mandos_argz_length = 0;
7255
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7256
&helper_directory, &user, &group,
7257
&mandos_argz, &mandos_argz_length));
7259
g_assert_cmpstr(agent_directory, ==, "/tmp");
7260
g_assert_null(helper_directory);
7261
g_assert_true(user == 0);
7262
g_assert_true(group == 0);
7263
g_assert_null(mandos_argz);
7264
g_assert_true(mandos_argz_length == 0);
7266
for(char **arg = argv; *arg != NULL; arg++){
7271
static void test_parse_arguments_short_dir(__attribute__((unused))
7272
test_fixture *fixture,
7273
__attribute__((unused))
7274
gconstpointer user_data){
7280
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7282
__attribute__((cleanup(cleanup_string)))
7283
char *agent_directory = NULL;
7284
char *helper_directory = NULL;
7287
__attribute__((cleanup(cleanup_string)))
7288
char *mandos_argz = NULL;
7289
size_t mandos_argz_length = 0;
7291
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7292
&helper_directory, &user, &group,
7293
&mandos_argz, &mandos_argz_length));
7295
g_assert_cmpstr(agent_directory, ==, "/tmp");
7296
g_assert_null(helper_directory);
7297
g_assert_true(user == 0);
7298
g_assert_true(group == 0);
7299
g_assert_null(mandos_argz);
7300
g_assert_true(mandos_argz_length == 0);
7302
for(char **arg = argv; *arg != NULL; arg++){
7308
void test_parse_arguments_helper_directory(__attribute__((unused))
7309
test_fixture *fixture,
7310
__attribute__((unused))
7311
gconstpointer user_data){
7314
strdup("--helper-directory"),
7317
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7319
char *agent_directory = NULL;
7320
__attribute__((cleanup(cleanup_string)))
7321
char *helper_directory = NULL;
7324
__attribute__((cleanup(cleanup_string)))
7325
char *mandos_argz = NULL;
7326
size_t mandos_argz_length = 0;
7328
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7329
&helper_directory, &user, &group,
7330
&mandos_argz, &mandos_argz_length));
7332
g_assert_cmpstr(helper_directory, ==, "/tmp");
7333
g_assert_null(agent_directory);
7334
g_assert_true(user == 0);
7335
g_assert_true(group == 0);
7336
g_assert_null(mandos_argz);
7337
g_assert_true(mandos_argz_length == 0);
7339
for(char **arg = argv; *arg != NULL; arg++){
7345
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7346
test_fixture *fixture,
7347
__attribute__((unused))
7348
gconstpointer user_data){
7351
strdup("--plugin-helper-dir"),
7354
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7356
char *agent_directory = NULL;
7357
__attribute__((cleanup(cleanup_string)))
7358
char *helper_directory = NULL;
7361
__attribute__((cleanup(cleanup_string)))
7362
char *mandos_argz = NULL;
7363
size_t mandos_argz_length = 0;
7365
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7366
&helper_directory, &user, &group,
7367
&mandos_argz, &mandos_argz_length));
7369
g_assert_cmpstr(helper_directory, ==, "/tmp");
7370
g_assert_null(agent_directory);
7371
g_assert_true(user == 0);
7372
g_assert_true(group == 0);
7373
g_assert_null(mandos_argz);
7374
g_assert_true(mandos_argz_length == 0);
7376
for(char **arg = argv; *arg != NULL; arg++){
7381
static void test_parse_arguments_user(__attribute__((unused))
7382
test_fixture *fixture,
7383
__attribute__((unused))
7384
gconstpointer user_data){
7390
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7392
char *agent_directory = NULL;
7393
__attribute__((cleanup(cleanup_string)))
7394
char *helper_directory = NULL;
7397
__attribute__((cleanup(cleanup_string)))
7398
char *mandos_argz = NULL;
7399
size_t mandos_argz_length = 0;
7401
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7402
&helper_directory, &user, &group,
7403
&mandos_argz, &mandos_argz_length));
7405
g_assert_null(helper_directory);
7406
g_assert_null(agent_directory);
7407
g_assert_cmpuint((unsigned int)user, ==, 1000);
7408
g_assert_true(group == 0);
7409
g_assert_null(mandos_argz);
7410
g_assert_true(mandos_argz_length == 0);
7412
for(char **arg = argv; *arg != NULL; arg++){
7417
static void test_parse_arguments_user_invalid(__attribute__((unused))
7418
test_fixture *fixture,
7419
__attribute__((unused))
7427
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7429
char *agent_directory = NULL;
7430
__attribute__((cleanup(cleanup_string)))
7431
char *helper_directory = NULL;
7434
__attribute__((cleanup(cleanup_string)))
7435
char *mandos_argz = NULL;
7436
size_t mandos_argz_length = 0;
7438
g_assert_false(parse_arguments_devnull(argc, argv, false,
7440
&helper_directory, &user,
7441
&group, &mandos_argz,
7442
&mandos_argz_length));
7444
g_assert_null(helper_directory);
7445
g_assert_null(agent_directory);
7446
g_assert_cmpuint((unsigned int)user, ==, 0);
7447
g_assert_true(group == 0);
7448
g_assert_null(mandos_argz);
7449
g_assert_true(mandos_argz_length == 0);
7451
for(char **arg = argv; *arg != NULL; arg++){
7457
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7458
test_fixture *fixture,
7459
__attribute__((unused))
7460
gconstpointer user_data){
7466
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7468
char *agent_directory = NULL;
7469
__attribute__((cleanup(cleanup_string)))
7470
char *helper_directory = NULL;
7473
__attribute__((cleanup(cleanup_string)))
7474
char *mandos_argz = NULL;
7475
size_t mandos_argz_length = 0;
7477
g_assert_false(parse_arguments_devnull(argc, argv, false,
7479
&helper_directory, &user,
7480
&group, &mandos_argz,
7481
&mandos_argz_length));
7483
g_assert_null(helper_directory);
7484
g_assert_null(agent_directory);
7485
g_assert_cmpuint((unsigned int)user, ==, 0);
7486
g_assert_true(group == 0);
7487
g_assert_null(mandos_argz);
7488
g_assert_true(mandos_argz_length == 0);
7490
for(char **arg = argv; *arg != NULL; arg++){
7495
static void test_parse_arguments_group(__attribute__((unused))
7496
test_fixture *fixture,
7497
__attribute__((unused))
7498
gconstpointer user_data){
7504
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7506
char *agent_directory = NULL;
7507
__attribute__((cleanup(cleanup_string)))
7508
char *helper_directory = NULL;
7511
__attribute__((cleanup(cleanup_string)))
7512
char *mandos_argz = NULL;
7513
size_t mandos_argz_length = 0;
7515
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7516
&helper_directory, &user, &group,
7517
&mandos_argz, &mandos_argz_length));
7519
g_assert_null(helper_directory);
7520
g_assert_null(agent_directory);
7521
g_assert_true(user == 0);
7522
g_assert_cmpuint((unsigned int)group, ==, 1000);
7523
g_assert_null(mandos_argz);
7524
g_assert_true(mandos_argz_length == 0);
7526
for(char **arg = argv; *arg != NULL; arg++){
7531
static void test_parse_arguments_group_invalid(__attribute__((unused))
7532
test_fixture *fixture,
7533
__attribute__((unused))
7541
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7543
char *agent_directory = NULL;
7544
__attribute__((cleanup(cleanup_string)))
7545
char *helper_directory = NULL;
7548
__attribute__((cleanup(cleanup_string)))
7549
char *mandos_argz = NULL;
7550
size_t mandos_argz_length = 0;
7552
g_assert_false(parse_arguments_devnull(argc, argv, false,
7554
&helper_directory, &user,
7555
&group, &mandos_argz,
7556
&mandos_argz_length));
7558
g_assert_null(helper_directory);
7559
g_assert_null(agent_directory);
7560
g_assert_true(user == 0);
7561
g_assert_true(group == 0);
7562
g_assert_null(mandos_argz);
7563
g_assert_true(mandos_argz_length == 0);
7565
for(char **arg = argv; *arg != NULL; arg++){
7571
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7572
test_fixture *fixture,
7573
__attribute__((unused))
7574
gconstpointer user_data){
7580
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7582
char *agent_directory = NULL;
7583
__attribute__((cleanup(cleanup_string)))
7584
char *helper_directory = NULL;
7587
__attribute__((cleanup(cleanup_string)))
7588
char *mandos_argz = NULL;
7589
size_t mandos_argz_length = 0;
7591
g_assert_false(parse_arguments_devnull(argc, argv, false,
7593
&helper_directory, &user,
7594
&group, &mandos_argz,
7595
&mandos_argz_length));
7597
g_assert_null(helper_directory);
7598
g_assert_null(agent_directory);
7599
g_assert_cmpuint((unsigned int)group, ==, 0);
7600
g_assert_true(group == 0);
7601
g_assert_null(mandos_argz);
7602
g_assert_true(mandos_argz_length == 0);
7604
for(char **arg = argv; *arg != NULL; arg++){
7609
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7610
test_fixture *fixture,
7611
__attribute__((unused))
7616
strdup("mandos-client"),
7618
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7620
__attribute__((cleanup(cleanup_string)))
7621
char *agent_directory = NULL;
7622
__attribute__((cleanup(cleanup_string)))
7623
char *helper_directory = NULL;
7626
__attribute__((cleanup(cleanup_string)))
7627
char *mandos_argz = NULL;
7628
size_t mandos_argz_length = 0;
7630
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7631
&helper_directory, &user, &group,
7632
&mandos_argz, &mandos_argz_length));
7634
g_assert_null(agent_directory);
7635
g_assert_null(helper_directory);
7636
g_assert_true(user == 0);
7637
g_assert_true(group == 0);
7638
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7639
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7640
mandos_argz_length),
7643
for(char **arg = argv; *arg != NULL; arg++){
7648
static void test_parse_arguments_mandos_args(__attribute__((unused))
7649
test_fixture *fixture,
7650
__attribute__((unused))
7651
gconstpointer user_data){
7654
strdup("mandos-client"),
7659
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7661
__attribute__((cleanup(cleanup_string)))
7662
char *agent_directory = NULL;
7663
__attribute__((cleanup(cleanup_string)))
7664
char *helper_directory = NULL;
7667
__attribute__((cleanup(cleanup_string)))
7668
char *mandos_argz = NULL;
7669
size_t mandos_argz_length = 0;
7671
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7672
&helper_directory, &user, &group,
7673
&mandos_argz, &mandos_argz_length));
7675
g_assert_null(agent_directory);
7676
g_assert_null(helper_directory);
7677
g_assert_true(user == 0);
7678
g_assert_true(group == 0);
7679
char *marg = mandos_argz;
7680
g_assert_cmpstr(marg, ==, "mandos-client");
7681
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7682
g_assert_cmpstr(marg, ==, "one");
7683
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7684
g_assert_cmpstr(marg, ==, "two");
7685
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7686
g_assert_cmpstr(marg, ==, "three");
7687
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7688
mandos_argz_length),
7691
for(char **arg = argv; *arg != NULL; arg++){
7696
static void test_parse_arguments_all_args(__attribute__((unused))
7697
test_fixture *fixture,
7698
__attribute__((unused))
7699
gconstpointer user_data){
7702
strdup("--agent-directory"),
7704
strdup("--helper-directory"),
7710
strdup("mandos-client"),
7715
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7717
__attribute__((cleanup(cleanup_string)))
7718
char *agent_directory = NULL;
7719
__attribute__((cleanup(cleanup_string)))
7720
char *helper_directory = NULL;
7723
__attribute__((cleanup(cleanup_string)))
7724
char *mandos_argz = NULL;
7725
size_t mandos_argz_length = 0;
7727
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7728
&helper_directory, &user, &group,
7729
&mandos_argz, &mandos_argz_length));
7731
g_assert_cmpstr(agent_directory, ==, "/tmp");
7732
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7733
g_assert_true(user == 1);
7734
g_assert_true(group == 2);
7735
char *marg = mandos_argz;
7736
g_assert_cmpstr(marg, ==, "mandos-client");
7737
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7738
g_assert_cmpstr(marg, ==, "one");
7739
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7740
g_assert_cmpstr(marg, ==, "two");
7741
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7742
g_assert_cmpstr(marg, ==, "three");
7743
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7744
mandos_argz_length),
7747
for(char **arg = argv; *arg != NULL; arg++){
7752
static void test_parse_arguments_mixed(__attribute__((unused))
7753
test_fixture *fixture,
7754
__attribute__((unused))
7755
gconstpointer user_data){
7758
strdup("mandos-client"),
7762
strdup("--agent-directory"),
7766
strdup("--helper-directory=/var/tmp"),
7768
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7770
__attribute__((cleanup(cleanup_string)))
7771
char *agent_directory = NULL;
7772
__attribute__((cleanup(cleanup_string)))
7773
char *helper_directory = NULL;
7776
__attribute__((cleanup(cleanup_string)))
7777
char *mandos_argz = NULL;
7778
size_t mandos_argz_length = 0;
7780
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7781
&helper_directory, &user, &group,
7782
&mandos_argz, &mandos_argz_length));
7784
g_assert_cmpstr(agent_directory, ==, "/tmp");
7785
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7786
g_assert_true(user == 1);
7787
g_assert_true(group == 0);
7788
char *marg = mandos_argz;
7789
g_assert_cmpstr(marg, ==, "mandos-client");
7790
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7791
g_assert_cmpstr(marg, ==, "one");
7792
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7793
g_assert_cmpstr(marg, ==, "two");
7794
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7795
g_assert_cmpstr(marg, ==, "three");
7796
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7797
mandos_argz_length),
7800
for(char **arg = argv; *arg != NULL; arg++){
7805
/* End of tests section */
7807
/* Test boilerplate section; New tests should be added to the test
7808
suite definition here, in the "run_tests" function.
7810
Finally, this section also contains the should_only_run_tests()
7811
function used by main() for deciding if tests should be run or to
7814
__attribute__((cold))
7815
static bool run_tests(int argc, char *argv[]){
7816
g_test_init(&argc, &argv, NULL);
7818
/* A macro to add a test with no setup or teardown functions */
7819
#define test_add(testpath, testfunc) \
7821
g_test_add((testpath), test_fixture, NULL, NULL, \
7822
(testfunc), NULL); \
7825
/* Test the signal-related functions first, since some other tests
7826
depend on these functions in their setups and teardowns */
7827
test_add("/signal-handling/setup", test_setup_signal_handler);
7828
test_add("/signal-handling/restore", test_restore_signal_handler);
7829
test_add("/signal-handling/block", test_block_sigchld);
7830
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7832
/* Regular non-signal-related tests; these use no setups or
7834
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7835
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7836
test_add("/parse_arguments/long-dir",
7837
test_parse_arguments_long_dir);
7838
test_add("/parse_arguments/short-dir",
7839
test_parse_arguments_short_dir);
7840
test_add("/parse_arguments/helper-directory",
7841
test_parse_arguments_helper_directory);
7842
test_add("/parse_arguments/plugin-helper-dir",
7843
test_parse_arguments_plugin_helper_dir);
7844
test_add("/parse_arguments/user", test_parse_arguments_user);
7845
test_add("/parse_arguments/user-invalid",
7846
test_parse_arguments_user_invalid);
7847
test_add("/parse_arguments/user-zero-invalid",
7848
test_parse_arguments_user_zero_invalid);
7849
test_add("/parse_arguments/group", test_parse_arguments_group);
7850
test_add("/parse_arguments/group-invalid",
7851
test_parse_arguments_group_invalid);
7852
test_add("/parse_arguments/group-zero-invalid",
7853
test_parse_arguments_group_zero_invalid);
7854
test_add("/parse_arguments/mandos-noargs",
7855
test_parse_arguments_mandos_noargs);
7856
test_add("/parse_arguments/mandos-args",
7857
test_parse_arguments_mandos_args);
7858
test_add("/parse_arguments/all-args",
7859
test_parse_arguments_all_args);
7860
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7861
test_add("/queue/create", test_create_queue);
7862
test_add("/queue/add", test_add_to_queue);
7863
test_add("/queue/has_question/empty",
7864
test_queue_has_question_empty);
7865
test_add("/queue/has_question/false",
7866
test_queue_has_question_false);
7867
test_add("/queue/has_question/true", test_queue_has_question_true);
7868
test_add("/queue/has_question/false2",
7869
test_queue_has_question_false2);
7870
test_add("/queue/has_question/true2",
7871
test_queue_has_question_true2);
7872
test_add("/buffer/cleanup", test_cleanup_buffer);
7873
test_add("/string_set/net-set-contains-nothing",
7874
test_string_set_new_set_contains_nothing);
7875
test_add("/string_set/with-added-string-contains-it",
7876
test_string_set_with_added_string_contains_it);
7877
test_add("/string_set/cleared-does-not-contain-string",
7878
test_string_set_cleared_does_not_contain_str);
7879
test_add("/string_set/swap/one-with-empty",
7880
test_string_set_swap_one_with_empty);
7881
test_add("/string_set/swap/empty-with-one",
7882
test_string_set_swap_empty_with_one);
7883
test_add("/string_set/swap/one-with-one",
7884
test_string_set_swap_one_with_one);
7886
/* A macro to add a test using the setup and teardown functions */
7887
#define test_add_st(path, func) \
7889
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7893
/* Signal-related tests; these use setups and teardowns which
7894
establish, during each test run, a signal handler for, and a
7895
signal mask blocking, the SIGCHLD signal, just like main() */
7896
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7897
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7898
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7899
test_add_st("/run_queue/zeroes-next-run",
7900
test_run_queue_zeroes_next_run);
7901
test_add_st("/run_queue/clears-cancelled_filenames",
7902
test_run_queue_clears_cancelled_filenames);
7903
test_add_st("/run_queue/skips-cancelled-filenames",
7904
test_run_queue_skips_cancelled_filenames);
7905
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7906
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7907
test_add_st("/run_queue/two-tasks/quit",
7908
test_run_queue_two_tasks_quit);
7909
test_add_st("/run_queue/two-tasks-cleanup",
7910
test_run_queue_two_tasks_cleanup);
7911
test_add_st("/task-creators/start_mandos_client",
7912
test_start_mandos_client);
7913
test_add_st("/task-creators/start_mandos_client/execv",
7914
test_start_mandos_client_execv);
7915
test_add_st("/task-creators/start_mandos_client/suid/euid",
7916
test_start_mandos_client_suid_euid);
7917
test_add_st("/task-creators/start_mandos_client/suid/egid",
7918
test_start_mandos_client_suid_egid);
7919
test_add_st("/task-creators/start_mandos_client/suid/ruid",
7920
test_start_mandos_client_suid_ruid);
7921
test_add_st("/task-creators/start_mandos_client/suid/rgid",
7922
test_start_mandos_client_suid_rgid);
7923
test_add_st("/task-creators/start_mandos_client/read",
7924
test_start_mandos_client_read);
7925
test_add_st("/task-creators/start_mandos_client/helper-directory",
7926
test_start_mandos_client_helper_directory);
7927
test_add_st("/task-creators/start_mandos_client/sigmask",
7928
test_start_mandos_client_sigmask);
7929
test_add_st("/task/wait_for_mandos_client_exit/badpid",
7930
test_wait_for_mandos_client_exit_badpid);
7931
test_add_st("/task/wait_for_mandos_client_exit/noexit",
7932
test_wait_for_mandos_client_exit_noexit);
7933
test_add_st("/task/wait_for_mandos_client_exit/success",
7934
test_wait_for_mandos_client_exit_success);
7935
test_add_st("/task/wait_for_mandos_client_exit/failure",
7936
test_wait_for_mandos_client_exit_failure);
7937
test_add_st("/task/wait_for_mandos_client_exit/killed",
7938
test_wait_for_mandos_client_exit_killed);
7939
test_add_st("/task/read_mandos_client_output/readerror",
7940
test_read_mandos_client_output_readerror);
7941
test_add_st("/task/read_mandos_client_output/nodata",
7942
test_read_mandos_client_output_nodata);
7943
test_add_st("/task/read_mandos_client_output/eof",
7944
test_read_mandos_client_output_eof);
7945
test_add_st("/task/read_mandos_client_output/once",
7946
test_read_mandos_client_output_once);
7947
test_add_st("/task/read_mandos_client_output/malloc",
7948
test_read_mandos_client_output_malloc);
7949
test_add_st("/task/read_mandos_client_output/append",
7950
test_read_mandos_client_output_append);
7951
test_add_st("/task-creators/add_inotify_dir_watch",
7952
test_add_inotify_dir_watch);
7953
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7954
test_add_inotify_dir_watch_fail);
7955
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7956
test_add_inotify_dir_watch_nondir);
7957
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7958
test_add_inotify_dir_watch_EAGAIN);
7959
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7960
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7961
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7962
test_add_inotify_dir_watch_IN_MOVED_TO);
7963
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7964
test_add_inotify_dir_watch_IN_MOVED_FROM);
7965
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7966
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7967
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7968
test_add_inotify_dir_watch_IN_DELETE);
7969
test_add_st("/task/read_inotify_event/readerror",
7970
test_read_inotify_event_readerror);
7971
test_add_st("/task/read_inotify_event/bad-epoll",
7972
test_read_inotify_event_bad_epoll);
7973
test_add_st("/task/read_inotify_event/nodata",
7974
test_read_inotify_event_nodata);
7975
test_add_st("/task/read_inotify_event/eof",
7976
test_read_inotify_event_eof);
7977
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7978
test_read_inotify_event_IN_CLOSE_WRITE);
7979
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7980
test_read_inotify_event_IN_MOVED_TO);
7981
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7982
test_read_inotify_event_IN_MOVED_FROM);
7983
test_add_st("/task/read_inotify_event/IN_DELETE",
7984
test_read_inotify_event_IN_DELETE);
7985
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7986
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7987
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7988
test_read_inotify_event_IN_MOVED_TO_badname);
7989
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7990
test_read_inotify_event_IN_MOVED_FROM_badname);
7991
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7992
test_read_inotify_event_IN_DELETE_badname);
7993
test_add_st("/task/open_and_parse_question/ENOENT",
7994
test_open_and_parse_question_ENOENT);
7995
test_add_st("/task/open_and_parse_question/EIO",
7996
test_open_and_parse_question_EIO);
7997
test_add_st("/task/open_and_parse_question/parse-error",
7998
test_open_and_parse_question_parse_error);
7999
test_add_st("/task/open_and_parse_question/nosocket",
8000
test_open_and_parse_question_nosocket);
8001
test_add_st("/task/open_and_parse_question/badsocket",
8002
test_open_and_parse_question_badsocket);
8003
test_add_st("/task/open_and_parse_question/nopid",
8004
test_open_and_parse_question_nopid);
8005
test_add_st("/task/open_and_parse_question/badpid",
8006
test_open_and_parse_question_badpid);
8007
test_add_st("/task/open_and_parse_question/noexist_pid",
8008
test_open_and_parse_question_noexist_pid);
8009
test_add_st("/task/open_and_parse_question/no-notafter",
8010
test_open_and_parse_question_no_notafter);
8011
test_add_st("/task/open_and_parse_question/bad-notafter",
8012
test_open_and_parse_question_bad_notafter);
8013
test_add_st("/task/open_and_parse_question/notafter-0",
8014
test_open_and_parse_question_notafter_0);
8015
test_add_st("/task/open_and_parse_question/notafter-1",
8016
test_open_and_parse_question_notafter_1);
8017
test_add_st("/task/open_and_parse_question/notafter-1-1",
8018
test_open_and_parse_question_notafter_1_1);
8019
test_add_st("/task/open_and_parse_question/notafter-1-2",
8020
test_open_and_parse_question_notafter_1_2);
8021
test_add_st("/task/open_and_parse_question/equal-notafter",
8022
test_open_and_parse_question_equal_notafter);
8023
test_add_st("/task/open_and_parse_question/late-notafter",
8024
test_open_and_parse_question_late_notafter);
8025
test_add_st("/task/cancel_old_question/0-1-2",
8026
test_cancel_old_question_0_1_2);
8027
test_add_st("/task/cancel_old_question/0-2-1",
8028
test_cancel_old_question_0_2_1);
8029
test_add_st("/task/cancel_old_question/1-2-3",
8030
test_cancel_old_question_1_2_3);
8031
test_add_st("/task/cancel_old_question/1-3-2",
8032
test_cancel_old_question_1_3_2);
8033
test_add_st("/task/cancel_old_question/2-1-3",
8034
test_cancel_old_question_2_1_3);
8035
test_add_st("/task/cancel_old_question/2-3-1",
8036
test_cancel_old_question_2_3_1);
8037
test_add_st("/task/cancel_old_question/3-1-2",
8038
test_cancel_old_question_3_1_2);
8039
test_add_st("/task/cancel_old_question/3-2-1",
8040
test_cancel_old_question_3_2_1);
8041
test_add_st("/task/connect_question_socket/name-too-long",
8042
test_connect_question_socket_name_too_long);
8043
test_add_st("/task/connect_question_socket/connect-fail",
8044
test_connect_question_socket_connect_fail);
8045
test_add_st("/task/connect_question_socket/bad-epoll",
8046
test_connect_question_socket_bad_epoll);
8047
test_add_st("/task/connect_question_socket/usable",
8048
test_connect_question_socket_usable);
8049
test_add_st("/task/send_password_to_socket/client-not-exited",
8050
test_send_password_to_socket_client_not_exited);
8051
test_add_st("/task/send_password_to_socket/password-not-read",
8052
test_send_password_to_socket_password_not_read);
8053
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8054
test_send_password_to_socket_EMSGSIZE);
8055
test_add_st("/task/send_password_to_socket/retry",
8056
test_send_password_to_socket_retry);
8057
test_add_st("/task/send_password_to_socket/bad-epoll",
8058
test_send_password_to_socket_bad_epoll);
8059
test_add_st("/task/send_password_to_socket/null-password",
8060
test_send_password_to_socket_null_password);
8061
test_add_st("/task/send_password_to_socket/empty-password",
8062
test_send_password_to_socket_empty_password);
8063
test_add_st("/task/send_password_to_socket/empty-str-password",
8064
test_send_password_to_socket_empty_str_pass);
8065
test_add_st("/task/send_password_to_socket/text-password",
8066
test_send_password_to_socket_text_password);
8067
test_add_st("/task/send_password_to_socket/binary-password",
8068
test_send_password_to_socket_binary_password);
8069
test_add_st("/task/send_password_to_socket/nuls-in-password",
8070
test_send_password_to_socket_nuls_in_password);
8071
test_add_st("/task-creators/add_existing_questions/ENOENT",
8072
test_add_existing_questions_ENOENT);
8073
test_add_st("/task-creators/add_existing_questions/no-questions",
8074
test_add_existing_questions_no_questions);
8075
test_add_st("/task-creators/add_existing_questions/one-question",
8076
test_add_existing_questions_one_question);
8077
test_add_st("/task-creators/add_existing_questions/two-questions",
8078
test_add_existing_questions_two_questions);
8079
test_add_st("/task-creators/add_existing_questions/non-questions",
8080
test_add_existing_questions_non_questions);
8081
test_add_st("/task-creators/add_existing_questions/both-types",
8082
test_add_existing_questions_both_types);
8084
return g_test_run() == 0;
8087
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8088
GOptionContext *context = g_option_context_new("");
8090
g_option_context_set_help_enabled(context, FALSE);
8091
g_option_context_set_ignore_unknown_options(context, TRUE);
8093
gboolean run_tests = FALSE;
8094
GOptionEntry entries[] = {
8095
{ "test", 0, 0, G_OPTION_ARG_NONE,
8096
&run_tests, "Run tests", NULL },
8099
g_option_context_add_main_entries(context, entries, NULL);
8101
GError *error = NULL;
8103
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8104
g_option_context_free(context);
8105
g_error("Failed to parse options: %s", error->message);
8108
g_option_context_free(context);
8109
return run_tests != FALSE;