1
/* -*- coding: utf-8; lexical-binding: t -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019-2022 Teddy Hogeborn
6
* Copyright © 2019-2022 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>.
26
#define _GNU_SOURCE /* pipe2(), O_CLOEXEC, setresgid(),
27
setresuid(), asprintf(), getline(),
29
#include <inttypes.h> /* uintmax_t, strtoumax(), PRIuMAX,
30
PRIdMAX, intmax_t, uint32_t,
31
SCNx32, SCNuMAX, SCNxMAX */
32
#include <stddef.h> /* size_t, NULL */
33
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
35
#include <stdbool.h> /* bool, true, false */
36
#include <signal.h> /* struct sigaction, sigset_t,
37
sigemptyset(), sigaddset(),
38
SIGCHLD, pthread_sigmask(),
39
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
40
SA_NOCLDSTOP, sigfillset(), kill(),
41
SIGTERM, sigdelset(), SIGKILL,
42
NSIG, sigismember(), SA_ONSTACK,
43
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
44
SIGHUP, SIGSTOP, SIG_UNBLOCK */
45
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
46
fork(), _exit(), dup2(),
47
STDOUT_FILENO, setresgid(),
48
setresuid(), execv(), ssize_t,
49
read(), dup3(), getuid(), dup(),
50
STDERR_FILENO, pause(), write(),
51
rmdir(), unlink(), getpid() */
52
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
53
malloc(), free(), realloc(),
54
setenv(), calloc(), mkdtemp(),
56
#include <iso646.h> /* not, or, and, xor */
57
#include <error.h> /* error() */
58
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
59
#include <errno.h> /* errno, error_t, EACCES,
60
ENAMETOOLONG, ENOENT, ENOTDIR,
61
ENOMEM, EEXIST, ECHILD, EPERM,
62
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
63
ECONNREFUSED, ECONNRESET,
64
ETOOMANYREFS, EMSGSIZE, EBADF,
66
#include <string.h> /* strdup(), memcpy(),
67
explicit_bzero(), memset(),
68
strcmp(), strlen(), strncpy(),
69
memcmp(), basename(), strerror() */
70
#include <argz.h> /* argz_create(), argz_count(),
71
argz_extract(), argz_next(),
73
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
74
epoll_ctl(), EPOLL_CTL_ADD,
75
struct epoll_event, EPOLLIN,
78
#include <time.h> /* struct timespec, clock_gettime(),
80
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
81
OPTION_ALIAS, struct argp_state,
82
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
83
struct argp, argp_parse(),
85
#include <stdint.h> /* SIZE_MAX, uint32_t */
86
#include <sys/mman.h> /* munlock(), mlock() */
87
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
F_GETFD, F_GETFL, FD_CLOEXEC,
89
open(), O_WRONLY, O_NOCTTY,
90
O_RDONLY, O_NOFOLLOW */
91
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
93
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
94
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
95
IN_CLOEXEC, inotify_add_watch(),
96
IN_CLOSE_WRITE, IN_MOVED_TO,
97
IN_MOVED_FROM, IN_DELETE,
98
IN_EXCL_UNLINK, IN_ONLYDIR,
99
struct inotify_event */
100
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
101
#include <stdio.h> /* asprintf(), FILE, stderr, fopen(),
102
fclose(), getline(), sscanf(),
103
feof(), ferror(), rename(),
104
fdopen(), fprintf(), fscanf() */
105
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
GError, g_key_file_load_from_file(),
107
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
108
g_key_file_get_string(), guint64,
109
g_key_file_get_uint64(),
110
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
111
g_assert_true(), g_assert_nonnull(),
112
g_assert_null(), g_assert_false(),
113
g_assert_cmpint(), g_assert_cmpuint(),
114
g_test_skip(), g_assert_cmpstr(),
115
g_test_message(), g_test_init(), g_test_add(),
116
g_test_run(), GOptionContext,
117
g_option_context_new(),
118
g_option_context_set_help_enabled(), FALSE,
119
g_option_context_set_ignore_unknown_options(),
120
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
121
g_option_context_add_main_entries(),
122
g_option_context_parse(),
123
g_option_context_free(), g_error() */
124
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
125
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
126
SOCK_DGRAM, SOCK_NONBLOCK,
127
SOCK_CLOEXEC, connect(),
128
struct sockaddr, socklen_t,
129
shutdown(), SHUT_RD, send(),
130
MSG_NOSIGNAL, bind(), recv(),
132
#include <glob.h> /* globfree(), glob_t, glob(),
133
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
134
GLOB_ABORTED, GLOB_NOMATCH,
137
/* End of includes */
139
/* Start of declarations of private types and functions */
141
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
142
typedef uintmax_t mono_microsecs;
144
/* "task_queue" - A queue of tasks to be run */
146
struct task_struct *tasks; /* Tasks in this queue */
147
size_t length; /* Number of tasks */
148
/* Memory allocated for "tasks", in bytes */
150
/* Time when this queue should be run, at the latest */
151
mono_microsecs next_run;
152
} __attribute__((designated_init)) task_queue;
154
/* "task_func" - A function type for task functions
156
I.e. functions for the code which runs when a task is run, all have
158
typedef void (task_func) (const struct task_struct,
160
__attribute__((nonnull));
162
/* "buffer" - A data buffer for a growing array of bytes
164
Used for the "password" variable */
169
} __attribute__((designated_init)) buffer;
171
/* "string_set" - A set type which can contain strings
173
Used by the "cancelled_filenames" variable */
175
char *argz; /* Do not access these except in */
176
size_t argz_len; /* the string_set_* functions */
177
} __attribute__((designated_init)) string_set;
179
/* "task_context" - local variables for tasks
181
This data structure distinguishes between different tasks which are
182
using the same function. This data structure is passed to every
183
task function when each task is run.
185
Note that not every task uses every struct member. */
186
typedef struct task_struct {
187
task_func *const func; /* The function run by this task */
188
char *const question_filename; /* The question file */
189
const pid_t pid; /* Mandos client process ID */
190
const int epoll_fd; /* The epoll set file descriptor */
191
bool *const quit_now; /* Set to true on fatal errors */
192
const int fd; /* General purpose file descriptor */
193
bool *const mandos_client_exited; /* Set true when client exits */
194
buffer *const password; /* As read from client process */
195
bool *const password_is_read; /* "password" is done growing */
196
char *filename; /* General purpose file name */
197
/* A set of strings of all the file names of questions which have
198
been cancelled for any reason; tasks pertaining to these question
199
files should not be run */
200
string_set *const cancelled_filenames;
201
const mono_microsecs notafter; /* "NotAfter" from question file */
202
/* Updated before each queue run; is compared with queue.next_run */
203
const mono_microsecs *const current_time;
204
} __attribute__((designated_init)) task_context;
206
/* Declare all our functions here so we can define them in any order
207
below. Note: test functions are *not* declared here, they are
208
declared in the test section. */
209
__attribute__((warn_unused_result))
210
static bool should_only_run_tests(int *, char **[]);
211
__attribute__((warn_unused_result, cold))
212
static bool run_tests(int, char *[]);
213
static void handle_sigchld(__attribute__((unused)) int sig){}
214
__attribute__((warn_unused_result, malloc))
215
task_queue *create_queue(void);
216
__attribute__((nonnull, warn_unused_result))
217
bool add_to_queue(task_queue *const, const task_context);
218
__attribute__((nonnull))
219
void cleanup_task(const task_context *const);
220
__attribute__((nonnull))
221
void cleanup_queue(task_queue *const *const);
222
__attribute__((pure, nonnull, warn_unused_result))
223
bool queue_has_question(const task_queue *const);
224
__attribute__((nonnull))
225
void cleanup_close(const int *const);
226
__attribute__((nonnull))
227
void cleanup_string(char *const *const);
228
__attribute__((nonnull))
229
void cleanup_buffer(buffer *const);
230
__attribute__((pure, nonnull, warn_unused_result))
231
bool string_set_contains(const string_set, const char *const);
232
__attribute__((nonnull, warn_unused_result))
233
bool string_set_add(string_set *const, const char *const);
234
__attribute__((nonnull))
235
void string_set_clear(string_set *);
236
void string_set_swap(string_set *const, string_set *const);
237
__attribute__((nonnull, warn_unused_result))
238
bool start_mandos_client(task_queue *const, const int, bool *const,
239
bool *const, buffer *const, bool *const,
240
const struct sigaction *const,
241
const sigset_t, const char *const,
242
const uid_t, const gid_t,
243
const char *const *const);
244
__attribute__((nonnull))
245
task_func wait_for_mandos_client_exit;
246
__attribute__((nonnull))
247
task_func read_mandos_client_output;
248
__attribute__((warn_unused_result))
249
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
250
buffer *const, const char *const,
251
string_set *, const mono_microsecs *const,
252
bool *const, bool *const);
253
__attribute__((nonnull))
254
task_func read_inotify_event;
255
__attribute__((nonnull))
256
task_func open_and_parse_question;
257
__attribute__((nonnull))
258
task_func cancel_old_question;
259
__attribute__((nonnull))
260
task_func connect_question_socket;
261
__attribute__((nonnull))
262
task_func send_password_to_socket;
263
__attribute__((warn_unused_result))
264
bool add_existing_questions(task_queue *const, const int,
265
buffer *const, string_set *,
266
const mono_microsecs *const,
267
bool *const, bool *const,
269
__attribute__((nonnull, warn_unused_result))
270
bool wait_for_event(const int, const mono_microsecs,
271
const mono_microsecs);
272
bool run_queue(task_queue **const, string_set *const, bool *const);
273
bool clear_all_fds_from_epoll_set(const int);
274
mono_microsecs get_current_time(void);
275
__attribute__((nonnull, warn_unused_result))
276
bool setup_signal_handler(struct sigaction *const);
277
__attribute__((nonnull))
278
bool restore_signal_handler(const struct sigaction *const);
279
__attribute__((nonnull, warn_unused_result))
280
bool block_sigchld(sigset_t *const);
281
__attribute__((nonnull))
282
bool restore_sigmask(const sigset_t *const);
283
__attribute__((nonnull))
284
bool parse_arguments(int, char *[], const bool, char **, char **,
285
uid_t *const , gid_t *const, char **, size_t *);
287
/* End of declarations of private types and functions */
289
/* Start of "main" section; this section LACKS TESTS!
291
Code here should be as simple as possible. */
293
/* These are required to be global by Argp */
294
const char *argp_program_version = "password-agent " VERSION;
295
const char *argp_program_bug_address = "<mandos@recompile.se>";
297
int main(int argc, char *argv[]){
299
/* If the --test option is passed, skip all normal operations and
300
instead only run the run_tests() function, which also does all
301
its own option parsing, so we don't have to do anything here. */
302
if(should_only_run_tests(&argc, &argv)){
303
if(run_tests(argc, argv)){
304
return EXIT_SUCCESS; /* All tests successful */
306
return EXIT_FAILURE; /* Some test(s) failed */
309
__attribute__((cleanup(cleanup_string)))
310
char *agent_directory = NULL;
312
__attribute__((cleanup(cleanup_string)))
313
char *helper_directory = NULL;
318
__attribute__((cleanup(cleanup_string)))
319
char *mandos_argz = NULL;
320
size_t mandos_argz_length = 0;
322
if(not parse_arguments(argc, argv, true, &agent_directory,
323
&helper_directory, &user, &group,
324
&mandos_argz, &mandos_argz_length)){
325
/* This should never happen, since "true" is passed as the third
326
argument to parse_arguments() above, which should make
327
argp_parse() call exit() if any parsing error occurs. */
328
error(EX_USAGE, errno, "Failed to parse arguments");
331
const char default_agent_directory[] = "/run/systemd/ask-password";
332
const char default_helper_directory[]
333
= "/lib/mandos/plugin-helpers";
334
const char *const default_argv[]
335
= {"/lib/mandos/plugins.d/mandos-client", NULL };
337
/* Set variables to default values if unset */
338
if(agent_directory == NULL){
339
agent_directory = strdup(default_agent_directory);
340
if(agent_directory == NULL){
341
error(EX_OSERR, errno, "Failed strdup()");
344
if(helper_directory == NULL){
345
helper_directory = strdup(default_helper_directory);
346
if(helper_directory == NULL){
347
error(EX_OSERR, errno, "Failed strdup()");
351
user = 65534; /* nobody */
354
group = 65534; /* nogroup */
356
/* If parse_opt did not create an argz vector, create one with
358
if(mandos_argz == NULL){
360
#pragma GCC diagnostic push
361
/* argz_create() takes a non-const argv for some unknown reason -
362
argz_create() isn't modifying the strings, just copying them.
363
Therefore, this cast to non-const should be safe. */
364
#pragma GCC diagnostic ignored "-Wcast-qual"
366
errno = argz_create((char *const *)default_argv, &mandos_argz,
367
&mandos_argz_length);
369
#pragma GCC diagnostic pop
372
error(EX_OSERR, errno, "Failed argz_create()");
375
/* Use argz vector to create a normal argv, usable by execv() */
377
char **mandos_argv = malloc((argz_count(mandos_argz,
379
+ 1) * sizeof(char *));
380
if(mandos_argv == NULL){
381
error_t saved_errno = errno;
383
error(EX_OSERR, saved_errno, "Failed malloc()");
385
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
387
sigset_t orig_sigmask;
388
if(not block_sigchld(&orig_sigmask)){
392
struct sigaction old_sigchld_action;
393
if(not setup_signal_handler(&old_sigchld_action)){
397
mono_microsecs current_time = 0;
399
bool mandos_client_exited = false;
400
bool quit_now = false;
401
__attribute__((cleanup(cleanup_close)))
402
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
404
error(EX_OSERR, errno, "Failed to create epoll set fd");
406
__attribute__((cleanup(cleanup_queue)))
407
task_queue *queue = create_queue();
409
error(EX_OSERR, errno, "Failed to create task queue");
412
__attribute__((cleanup(cleanup_buffer)))
413
buffer password = {};
414
bool password_is_read = false;
416
__attribute__((cleanup(string_set_clear)))
417
string_set cancelled_filenames = {};
419
/* Add tasks to queue */
420
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
421
&quit_now, &password, &password_is_read,
422
&old_sigchld_action, orig_sigmask,
423
helper_directory, user, group,
424
(const char *const *)mandos_argv)){
425
return EX_OSERR; /* Error has already been printed */
427
/* These variables were only for start_mandos_client() and are not
432
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
433
agent_directory, &cancelled_filenames,
434
¤t_time, &mandos_client_exited,
436
switch(errno){ /* Error has already been printed */
446
if(not add_existing_questions(queue, epoll_fd, &password,
447
&cancelled_filenames, ¤t_time,
448
&mandos_client_exited,
449
&password_is_read, agent_directory)){
450
return EXIT_FAILURE; /* Error has already been printed */
455
current_time = get_current_time();
456
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
457
const error_t saved_errno = errno;
458
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
462
current_time = get_current_time();
463
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
464
const error_t saved_errno = errno;
465
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
468
/* When no tasks about questions are left in the queue, break out
469
of the loop (and implicitly exit the program) */
470
} while(queue_has_question(queue));
472
restore_signal_handler(&old_sigchld_action);
473
restore_sigmask(&orig_sigmask);
478
__attribute__((warn_unused_result))
479
mono_microsecs get_current_time(void){
480
struct timespec currtime;
481
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
482
error(0, errno, "Failed to get current time");
485
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
486
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
489
/* End of "main" section */
491
/* Start of regular code section; ALL this code has tests */
493
__attribute__((nonnull))
494
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
495
char **agent_directory, char **helper_directory,
496
uid_t *const user, gid_t *const group,
497
char **mandos_argz, size_t *mandos_argz_length){
499
const struct argp_option options[] = {
500
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
501
.doc="Systemd password agent directory" },
502
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
503
.doc="Mandos Client password helper directory" },
504
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
505
.flags=OPTION_HIDDEN | OPTION_ALIAS },
506
{ .name="user", .key='u', .arg="USERID",
507
.doc="User ID the Mandos Client will use as its unprivileged"
509
{ .name="userid", .key=130, /* From plugin--runner */
510
.flags=OPTION_HIDDEN | OPTION_ALIAS },
511
{ .name="group", .key='g', .arg="GROUPID",
512
.doc="Group ID the Mandos Client will use as its unprivileged"
514
{ .name="groupid", .key=131, /* From plugin--runner */
515
.flags=OPTION_HIDDEN | OPTION_ALIAS },
516
{ .name="test", .key=255, /* See should_only_run_tests() */
517
.doc="Skip normal operation, and only run self-tests. See"
518
" --test --help.", .group=10, },
522
__attribute__((nonnull(3)))
523
error_t parse_opt(int key, char *arg, struct argp_state *state){
526
case 'd': /* --agent-directory */
527
*agent_directory = strdup(arg);
529
case 128: /* --helper-directory */
530
case 129: /* --plugin-helper-dir */
531
*helper_directory = strdup(arg);
533
case 'u': /* --user */
534
case 130: /* --userid */
537
uintmax_t tmp_id = 0;
539
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
540
if(errno != 0 or tmp == arg or *tmp != '\0'
541
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
542
return ARGP_ERR_UNKNOWN;
544
*user = (uid_t)tmp_id;
548
case 'g': /* --group */
549
case 131: /* --groupid */
552
uintmax_t tmp_id = 0;
554
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
555
if(errno != 0 or tmp == arg or *tmp != '\0'
556
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
557
return ARGP_ERR_UNKNOWN;
559
*group = (gid_t)tmp_id;
564
/* Copy arguments into argz vector */
565
return argz_create(state->argv + state->next, mandos_argz,
568
return ARGP_ERR_UNKNOWN;
573
const struct argp argp = {
576
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
577
.doc = "Mandos password agent -- runs Mandos client as a"
578
" systemd password agent",
581
errno = argp_parse(&argp, argc, argv,
582
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
587
__attribute__((nonnull, warn_unused_result))
588
bool block_sigchld(sigset_t *const orig_sigmask){
589
sigset_t sigchld_sigmask;
590
if(sigemptyset(&sigchld_sigmask) < 0){
591
error(0, errno, "Failed to empty signal set");
594
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
595
error(0, errno, "Failed to add SIGCHLD to signal set");
598
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
599
error(0, errno, "Failed to block SIGCHLD signal");
605
__attribute__((nonnull, warn_unused_result, const))
606
bool restore_sigmask(const sigset_t *const orig_sigmask){
607
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
608
error(0, errno, "Failed to restore blocked signals");
614
__attribute__((nonnull, warn_unused_result))
615
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
616
struct sigaction sigchld_action = {
617
.sa_handler=handle_sigchld,
618
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
620
/* Set all signals in "sa_mask" struct member; this makes all
621
signals automatically blocked during signal handler */
622
if(sigfillset(&sigchld_action.sa_mask) != 0){
623
error(0, errno, "Failed to do sigfillset()");
626
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
627
error(0, errno, "Failed to set SIGCHLD signal handler");
633
__attribute__((nonnull, warn_unused_result))
634
bool restore_signal_handler(const struct sigaction *const
636
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
637
error(0, errno, "Failed to restore signal handler");
643
__attribute__((warn_unused_result, malloc))
644
task_queue *create_queue(void){
645
task_queue *queue = malloc(sizeof(task_queue));
649
queue->allocated = 0;
655
__attribute__((nonnull, warn_unused_result))
656
bool add_to_queue(task_queue *const queue, const task_context task){
657
if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
659
error(0, ENOMEM, "Failed to allocate %" PRIuMAX
660
" tasks for queue->tasks", (uintmax_t)(queue->length + 1));
664
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
if(needed_size > (queue->allocated)){
666
task_context *const new_tasks = realloc(queue->tasks,
668
if(new_tasks == NULL){
669
error(0, errno, "Failed to allocate %" PRIuMAX
670
" bytes for queue->tasks", (uintmax_t)needed_size);
673
queue->tasks = new_tasks;
674
queue->allocated = needed_size;
676
/* Using memcpy here is necessary because doing */
677
/* queue->tasks[queue->length++] = task; */
678
/* would violate const-ness of task members */
679
memcpy(&(queue->tasks[queue->length++]), &task,
680
sizeof(task_context));
684
__attribute__((nonnull))
685
void cleanup_task(const task_context *const task){
686
const error_t saved_errno = errno;
687
/* free and close all task data */
688
free(task->question_filename);
689
if(task->filename != task->question_filename){
690
free(task->filename);
693
kill(task->pid, SIGTERM);
701
__attribute__((nonnull))
702
void free_queue(task_queue *const queue){
707
__attribute__((nonnull))
708
void cleanup_queue(task_queue *const *const queue){
712
for(size_t i = 0; i < (*queue)->length; i++){
713
const task_context *const task = ((*queue)->tasks)+i;
719
__attribute__((pure, nonnull, warn_unused_result))
720
bool queue_has_question(const task_queue *const queue){
721
for(size_t i=0; i < queue->length; i++){
722
if(queue->tasks[i].question_filename != NULL){
729
__attribute__((nonnull))
730
void cleanup_close(const int *const fd){
731
const error_t saved_errno = errno;
736
__attribute__((nonnull))
737
void cleanup_string(char *const *const ptr){
741
__attribute__((nonnull))
742
void cleanup_buffer(buffer *buf){
743
if(buf->allocated > 0){
744
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
745
explicit_bzero(buf->data, buf->allocated);
747
memset(buf->data, '\0', buf->allocated);
750
if(buf->data != NULL){
751
if(munlock(buf->data, buf->allocated) != 0){
752
error(0, errno, "Failed to unlock memory of old buffer");
761
__attribute__((pure, nonnull, warn_unused_result))
762
bool string_set_contains(const string_set set, const char *const str){
763
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
764
s = argz_next(set.argz, set.argz_len, s)){
765
if(strcmp(s, str) == 0){
772
__attribute__((nonnull, warn_unused_result))
773
bool string_set_add(string_set *const set, const char *const str){
774
if(string_set_contains(*set, str)){
777
error_t error = argz_add(&set->argz, &set->argz_len, str);
785
__attribute__((nonnull))
786
void string_set_clear(string_set *set){
792
__attribute__((nonnull))
793
void string_set_swap(string_set *const set1, string_set *const set2){
794
/* Swap contents of two string sets */
796
char *const tmp_argz = set1->argz;
797
set1->argz = set2->argz;
798
set2->argz = tmp_argz;
801
const size_t tmp_argz_len = set1->argz_len;
802
set1->argz_len = set2->argz_len;
803
set2->argz_len = tmp_argz_len;
807
__attribute__((nonnull, warn_unused_result))
808
bool start_mandos_client(task_queue *const queue,
810
bool *const mandos_client_exited,
811
bool *const quit_now, buffer *const password,
812
bool *const password_is_read,
813
const struct sigaction *const
814
old_sigchld_action, const sigset_t sigmask,
815
const char *const helper_directory,
816
const uid_t user, const gid_t group,
817
const char *const *const argv){
819
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
820
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
824
const pid_t pid = fork();
826
if(not restore_signal_handler(old_sigchld_action)){
829
if(not restore_sigmask(&sigmask)){
832
if(close(pipefds[0]) != 0){
833
error(0, errno, "Failed to close() parent pipe fd");
836
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
837
error(0, errno, "Failed to dup2() pipe fd to stdout");
840
if(close(pipefds[1]) != 0){
841
error(0, errno, "Failed to close() old child pipe fd");
844
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
845
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
846
" \"%s\", 1)", helper_directory);
849
if(group != 0 and setresgid(group, 0, 0) == -1){
850
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
851
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
854
if(user != 0 and setresuid(user, 0, 0) == -1){
855
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
856
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
860
#pragma GCC diagnostic push
861
/* For historical reasons, the "argv" argument to execv() is not
862
const, but it is safe to override this. */
863
#pragma GCC diagnostic ignored "-Wcast-qual"
865
execv(argv[0], (char **)argv);
867
#pragma GCC diagnostic pop
869
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
875
error(0, errno, "Failed to fork()");
880
if(not add_to_queue(queue, (task_context){
881
.func=wait_for_mandos_client_exit,
883
.mandos_client_exited=mandos_client_exited,
886
error(0, errno, "Failed to add wait_for_mandos_client to queue");
891
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
892
&(struct epoll_event)
893
{ .events=EPOLLIN | EPOLLRDHUP });
894
if(ret != 0 and errno != EEXIST){
895
error(0, errno, "Failed to add file descriptor to epoll set");
900
return add_to_queue(queue, (task_context){
901
.func=read_mandos_client_output,
906
.password_is_read=password_is_read,
910
__attribute__((nonnull))
911
void wait_for_mandos_client_exit(const task_context task,
912
task_queue *const queue){
913
const pid_t pid = task.pid;
914
bool *const mandos_client_exited = task.mandos_client_exited;
915
bool *const quit_now = task.quit_now;
918
switch(waitpid(pid, &status, WNOHANG)){
919
case 0: /* Not exited yet */
920
if(not add_to_queue(queue, task)){
921
error(0, errno, "Failed to add myself to queue");
926
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
932
default: /* Has exited */
933
*mandos_client_exited = true;
934
if((not WIFEXITED(status))
935
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
936
error(0, 0, "Mandos client failed or was killed");
942
__attribute__((nonnull))
943
void read_mandos_client_output(const task_context task,
944
task_queue *const queue){
945
buffer *const password = task.password;
946
bool *const quit_now = task.quit_now;
947
bool *const password_is_read = task.password_is_read;
948
const int fd = task.fd;
949
const int epoll_fd = task.epoll_fd;
951
const size_t new_potential_size = (password->length + PIPE_BUF);
952
if(password->allocated < new_potential_size){
953
char *const new_buffer = calloc(new_potential_size, 1);
954
if(new_buffer == NULL){
955
error(0, errno, "Failed to allocate %" PRIuMAX
956
" bytes for password", (uintmax_t)new_potential_size);
961
if(mlock(new_buffer, new_potential_size) != 0){
962
/* Warn but do not treat as fatal error */
963
if(errno != EPERM and errno != ENOMEM){
964
error(0, errno, "Failed to lock memory for password");
967
if(password->length > 0){
968
memcpy(new_buffer, password->data, password->length);
969
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
970
explicit_bzero(password->data, password->allocated);
972
memset(password->data, '\0', password->allocated);
975
if(password->data != NULL){
976
if(munlock(password->data, password->allocated) != 0){
977
error(0, errno, "Failed to unlock memory of old buffer");
979
free(password->data);
981
password->data = new_buffer;
982
password->allocated = new_potential_size;
985
const ssize_t read_length = read(fd, password->data
986
+ password->length, PIPE_BUF);
988
if(read_length == 0){ /* EOF */
989
*password_is_read = true;
993
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
994
error(0, errno, "Failed to read password from Mandos client");
999
if(read_length > 0){ /* Data has been read */
1000
password->length += (size_t)read_length;
1003
/* Either data was read, or EAGAIN was indicated, meaning no data
1006
/* Re-add the fd to the epoll set */
1007
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1008
&(struct epoll_event)
1009
{ .events=EPOLLIN | EPOLLRDHUP });
1010
if(ret != 0 and errno != EEXIST){
1011
error(0, errno, "Failed to re-add file descriptor to epoll set");
1017
/* Re-add myself to the queue */
1018
if(not add_to_queue(queue, task)){
1019
error(0, errno, "Failed to add myself to queue");
1025
__attribute__((nonnull, warn_unused_result))
1026
bool add_inotify_dir_watch(task_queue *const queue,
1027
const int epoll_fd, bool *const quit_now,
1028
buffer *const password,
1029
const char *const dir,
1030
string_set *cancelled_filenames,
1031
const mono_microsecs *const current_time,
1032
bool *const mandos_client_exited,
1033
bool *const password_is_read){
1034
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1036
error(0, errno, "Failed to create inotify instance");
1040
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1041
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1044
error(0, errno, "Failed to create inotify watch on %s", dir);
1048
/* Add the inotify fd to the epoll set */
1049
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1050
&(struct epoll_event)
1051
{ .events=EPOLLIN | EPOLLRDHUP });
1052
if(ret != 0 and errno != EEXIST){
1053
error(0, errno, "Failed to add file descriptor to epoll set");
1058
const task_context read_inotify_event_task = {
1059
.func=read_inotify_event,
1064
.filename=strdup(dir),
1065
.cancelled_filenames=cancelled_filenames,
1066
.current_time=current_time,
1067
.mandos_client_exited=mandos_client_exited,
1068
.password_is_read=password_is_read,
1070
if(read_inotify_event_task.filename == NULL){
1071
error(0, errno, "Failed to strdup(\"%s\")", dir);
1076
return add_to_queue(queue, read_inotify_event_task);
1079
__attribute__((nonnull))
1080
void read_inotify_event(const task_context task,
1081
task_queue *const queue){
1082
const int fd = task.fd;
1083
const int epoll_fd = task.epoll_fd;
1084
char *const filename = task.filename;
1085
bool *quit_now = task.quit_now;
1086
buffer *const password = task.password;
1087
string_set *const cancelled_filenames = task.cancelled_filenames;
1088
const mono_microsecs *const current_time = task.current_time;
1089
bool *const mandos_client_exited = task.mandos_client_exited;
1090
bool *const password_is_read = task.password_is_read;
1092
/* "sufficient to read at least one event." - inotify(7) */
1093
const size_t ievent_size = (sizeof(struct inotify_event)
1096
struct inotify_event event;
1097
char name_buffer[NAME_MAX + 1];
1099
struct inotify_event *const ievent = &ievent_buffer.event;
1101
#if defined(__GNUC__) and __GNUC__ >= 7
1102
#pragma GCC diagnostic push
1103
/* ievent is pointing into a struct which is of sufficient size */
1104
#pragma GCC diagnostic ignored "-Wstringop-overflow"
1106
const ssize_t read_length = read(fd, ievent, ievent_size);
1107
#if defined(__GNUC__) and __GNUC__ >= 7
1108
#pragma GCC diagnostic pop
1110
if(read_length == 0){ /* EOF */
1111
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1113
cleanup_task(&task);
1116
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1117
error(0, errno, "Failed to read from inotify fd for directory %s",
1120
cleanup_task(&task);
1123
if(read_length > 0 /* Data has been read */
1124
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1125
char *question_filename = NULL;
1126
const ssize_t question_filename_length
1127
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1128
if(question_filename_length < 0){
1129
error(0, errno, "Failed to create file name from directory name"
1130
" %s and file name %s", filename, ievent->name);
1132
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1133
if(not add_to_queue(queue, (task_context){
1134
.func=open_and_parse_question,
1136
.question_filename=question_filename,
1137
.filename=question_filename,
1139
.cancelled_filenames=cancelled_filenames,
1140
.current_time=current_time,
1141
.mandos_client_exited=mandos_client_exited,
1142
.password_is_read=password_is_read,
1144
error(0, errno, "Failed to add open_and_parse_question task"
1145
" for file name %s to queue", filename);
1147
/* Force the added task (open_and_parse_question) to run
1149
queue->next_run = 1;
1151
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1152
if(not string_set_add(cancelled_filenames,
1153
question_filename)){
1154
error(0, errno, "Could not add question %s to"
1155
" cancelled_questions", question_filename);
1157
free(question_filename);
1158
cleanup_task(&task);
1161
free(question_filename);
1166
/* Either data was read, or EAGAIN was indicated, meaning no data
1169
/* Re-add myself to the queue */
1170
if(not add_to_queue(queue, task)){
1171
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1172
" queue", filename);
1174
cleanup_task(&task);
1178
/* Re-add the fd to the epoll set */
1179
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1180
&(struct epoll_event)
1181
{ .events=EPOLLIN | EPOLLRDHUP });
1182
if(ret != 0 and errno != EEXIST){
1183
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1184
" directory %s to epoll set", fd, filename);
1185
/* Force the added task (read_inotify_event) to run again, at most
1186
one second from now */
1187
if((queue->next_run == 0)
1188
or (queue->next_run > (*current_time + 1000000))){
1189
queue->next_run = *current_time + 1000000;
1194
__attribute__((nonnull))
1195
void open_and_parse_question(const task_context task,
1196
task_queue *const queue){
1197
__attribute__((cleanup(cleanup_string)))
1198
char *question_filename = task.question_filename;
1199
const int epoll_fd = task.epoll_fd;
1200
buffer *const password = task.password;
1201
string_set *const cancelled_filenames = task.cancelled_filenames;
1202
const mono_microsecs *const current_time = task.current_time;
1203
bool *const mandos_client_exited = task.mandos_client_exited;
1204
bool *const password_is_read = task.password_is_read;
1206
/* We use the GLib "Key-value file parser" functions to parse the
1207
question file. See <https://systemd.io/PASSWORD_AGENTS/> for
1208
specification of contents */
1209
__attribute__((nonnull))
1210
void cleanup_g_key_file(GKeyFile **key_file){
1211
if(*key_file != NULL){
1212
g_key_file_free(*key_file);
1216
__attribute__((cleanup(cleanup_g_key_file)))
1217
GKeyFile *key_file = g_key_file_new();
1218
if(key_file == NULL){
1219
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1223
GError *glib_error = NULL;
1224
if(g_key_file_load_from_file(key_file, question_filename,
1225
G_KEY_FILE_NONE, &glib_error) != TRUE){
1226
/* If a file was removed, we should ignore it, so */
1227
/* only show error message if file actually existed */
1228
if(glib_error->code != G_FILE_ERROR_NOENT){
1229
error(0, 0, "Failed to load question data from file \"%s\": %s",
1230
question_filename, glib_error->message);
1235
__attribute__((cleanup(cleanup_string)))
1236
char *socket_name = g_key_file_get_string(key_file, "Ask",
1239
if(socket_name == NULL){
1240
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1241
question_filename, glib_error->message);
1245
if(strlen(socket_name) == 0){
1246
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1251
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1253
if(glib_error != NULL){
1254
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1255
question_filename, glib_error->message);
1259
if((pid != (guint64)((pid_t)pid))
1260
or (kill((pid_t)pid, 0) != 0)){
1261
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1262
" does not exist", (uintmax_t)pid, question_filename);
1266
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1267
"NotAfter", &glib_error);
1268
if(glib_error != NULL){
1269
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1270
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1271
" %s", question_filename, glib_error->message);
1276
if(queue->next_run == 0 or (queue->next_run > notafter)){
1277
queue->next_run = notafter;
1279
if(*current_time >= notafter){
1284
const task_context connect_question_socket_task = {
1285
.func=connect_question_socket,
1286
.question_filename=strdup(question_filename),
1289
.filename=strdup(socket_name),
1290
.cancelled_filenames=task.cancelled_filenames,
1291
.mandos_client_exited=mandos_client_exited,
1292
.password_is_read=password_is_read,
1293
.current_time=current_time,
1295
if(connect_question_socket_task.question_filename == NULL
1296
or connect_question_socket_task.filename == NULL
1297
or not add_to_queue(queue, connect_question_socket_task)){
1298
error(0, errno, "Failed to add connect_question_socket for socket"
1299
" %s (from \"%s\") to queue", socket_name,
1301
cleanup_task(&connect_question_socket_task);
1304
/* Force the added task (connect_question_socket) to run
1306
queue->next_run = 1;
1309
char *const dup_filename = strdup(question_filename);
1310
const task_context cancel_old_question_task = {
1311
.func=cancel_old_question,
1312
.question_filename=dup_filename,
1314
.filename=dup_filename,
1315
.cancelled_filenames=cancelled_filenames,
1316
.current_time=current_time,
1318
if(cancel_old_question_task.question_filename == NULL
1319
or not add_to_queue(queue, cancel_old_question_task)){
1320
error(0, errno, "Failed to add cancel_old_question for file "
1321
"\"%s\" to queue", question_filename);
1322
cleanup_task(&cancel_old_question_task);
1328
__attribute__((nonnull))
1329
void cancel_old_question(const task_context task,
1330
task_queue *const queue){
1331
char *const question_filename = task.question_filename;
1332
string_set *const cancelled_filenames = task.cancelled_filenames;
1333
const mono_microsecs notafter = task.notafter;
1334
const mono_microsecs *const current_time = task.current_time;
1336
if(*current_time >= notafter){
1337
if(not string_set_add(cancelled_filenames, question_filename)){
1338
error(0, errno, "Failed to cancel question for file %s",
1341
cleanup_task(&task);
1345
if(not add_to_queue(queue, task)){
1346
error(0, errno, "Failed to add cancel_old_question for file "
1347
"%s to queue", question_filename);
1348
cleanup_task(&task);
1352
if((queue->next_run == 0) or (queue->next_run > notafter)){
1353
queue->next_run = notafter;
1357
__attribute__((nonnull))
1358
void connect_question_socket(const task_context task,
1359
task_queue *const queue){
1360
char *const question_filename = task.question_filename;
1361
char *const filename = task.filename;
1362
const int epoll_fd = task.epoll_fd;
1363
buffer *const password = task.password;
1364
string_set *const cancelled_filenames = task.cancelled_filenames;
1365
bool *const mandos_client_exited = task.mandos_client_exited;
1366
bool *const password_is_read = task.password_is_read;
1367
const mono_microsecs *const current_time = task.current_time;
1369
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1371
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1372
error(0, 0, "Socket filename is larger than"
1373
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1374
(uintmax_t)sizeof(sock_name.sun_path), filename);
1375
if(not string_set_add(cancelled_filenames, question_filename)){
1376
error(0, errno, "Failed to cancel question for file %s",
1379
cleanup_task(&task);
1383
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1384
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1387
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1388
if(not add_to_queue(queue, task)){
1389
error(0, errno, "Failed to add connect_question_socket for file"
1390
" \"%s\" and socket \"%s\" to queue", question_filename,
1392
cleanup_task(&task);
1394
/* Force the added task (connect_question_socket) to run
1396
queue->next_run = 1;
1401
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1402
if(connect(fd, (struct sockaddr *)&sock_name,
1403
(socklen_t)SUN_LEN(&sock_name)) != 0){
1404
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1405
if(not add_to_queue(queue, task)){
1406
error(0, errno, "Failed to add connect_question_socket for file"
1407
" \"%s\" and socket \"%s\" to queue", question_filename,
1409
cleanup_task(&task);
1411
/* Force the added task (connect_question_socket) to run again,
1412
at most one second from now */
1413
if((queue->next_run == 0)
1414
or (queue->next_run > (*current_time + 1000000))){
1415
queue->next_run = *current_time + 1000000;
1421
/* Not necessary, but we can try, and merely warn on failure */
1422
if(shutdown(fd, SHUT_RD) != 0){
1423
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1427
/* Add the fd to the epoll set */
1428
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1429
&(struct epoll_event){ .events=EPOLLOUT })
1431
error(0, errno, "Failed to add inotify file descriptor %d for"
1432
" socket %s to epoll set", fd, filename);
1433
if(not add_to_queue(queue, task)){
1434
error(0, errno, "Failed to add connect_question_socket for file"
1435
" \"%s\" and socket \"%s\" to queue", question_filename,
1437
cleanup_task(&task);
1439
/* Force the added task (connect_question_socket) to run again,
1440
at most one second from now */
1441
if((queue->next_run == 0)
1442
or (queue->next_run > (*current_time + 1000000))){
1443
queue->next_run = *current_time + 1000000;
1449
/* add task send_password_to_socket to queue */
1450
const task_context send_password_to_socket_task = {
1451
.func=send_password_to_socket,
1452
.question_filename=question_filename,
1457
.cancelled_filenames=cancelled_filenames,
1458
.mandos_client_exited=mandos_client_exited,
1459
.password_is_read=password_is_read,
1460
.current_time=current_time,
1463
if(not add_to_queue(queue, send_password_to_socket_task)){
1464
error(0, errno, "Failed to add send_password_to_socket for"
1465
" file \"%s\" and socket \"%s\" to queue",
1466
question_filename, filename);
1467
cleanup_task(&send_password_to_socket_task);
1471
__attribute__((nonnull))
1472
void send_password_to_socket(const task_context task,
1473
task_queue *const queue){
1474
char *const question_filename=task.question_filename;
1475
char *const filename=task.filename;
1476
const int epoll_fd=task.epoll_fd;
1477
const int fd=task.fd;
1478
buffer *const password=task.password;
1479
string_set *const cancelled_filenames=task.cancelled_filenames;
1480
bool *const mandos_client_exited = task.mandos_client_exited;
1481
bool *const password_is_read = task.password_is_read;
1482
const mono_microsecs *const current_time = task.current_time;
1484
if(*mandos_client_exited and *password_is_read){
1486
const size_t send_buffer_length = password->length + 2;
1487
char *send_buffer = malloc(send_buffer_length);
1488
if(send_buffer == NULL){
1489
error(0, errno, "Failed to allocate send_buffer");
1491
if(mlock(send_buffer, send_buffer_length) != 0){
1492
/* Warn but do not treat as fatal error */
1493
if(errno != EPERM and errno != ENOMEM){
1494
error(0, errno, "Failed to lock memory for password"
1498
/* “[…] send a single datagram to the socket consisting of the
1499
password string either prefixed with "+" or with "-"
1500
depending on whether the password entry was successful or
1501
not. You may but don't have to include a final NUL byte in
1504
— <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1507
send_buffer[0] = '+'; /* Prefix with "+" */
1508
/* Always add an extra NUL */
1509
send_buffer[password->length + 1] = '\0';
1510
if(password->length > 0){
1511
memcpy(send_buffer + 1, password->data, password->length);
1514
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1516
const error_t saved_errno = (ssret < 0) ? errno : 0;
1517
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1518
explicit_bzero(send_buffer, send_buffer_length);
1520
memset(send_buffer, '\0', send_buffer_length);
1522
if(munlock(send_buffer, send_buffer_length) != 0){
1523
error(0, errno, "Failed to unlock memory of send buffer");
1526
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1527
switch(saved_errno){
1540
error(0, saved_errno, "Password of size %" PRIuMAX
1541
" is too big", (uintmax_t)password->length);
1545
__attribute__((fallthrough));
1548
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1549
error(0, 0, "Password only partially sent to socket %s: %"
1550
PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1551
(uintmax_t)ssret, (uintmax_t)send_buffer_length);
1556
__attribute__((fallthrough));
1559
error(0, saved_errno, "Failed to send() to socket %s",
1561
if(not string_set_add(cancelled_filenames,
1562
question_filename)){
1563
error(0, errno, "Failed to cancel question for file %s",
1566
cleanup_task(&task);
1571
cleanup_task(&task);
1577
/* We failed or are not ready yet; retry later */
1579
if(not add_to_queue(queue, task)){
1580
error(0, errno, "Failed to add send_password_to_socket for"
1581
" file %s and socket %s to queue", question_filename,
1583
cleanup_task(&task);
1586
/* Add the fd to the epoll set */
1587
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1588
&(struct epoll_event){ .events=EPOLLOUT })
1590
error(0, errno, "Failed to add socket file descriptor %d for"
1591
" socket %s to epoll set", fd, filename);
1592
/* Force the added task (send_password_to_socket) to run again, at
1593
most one second from now */
1594
if((queue->next_run == 0)
1595
or (queue->next_run > (*current_time + 1000000))){
1596
queue->next_run = *current_time + 1000000;
1601
__attribute__((warn_unused_result))
1602
bool add_existing_questions(task_queue *const queue,
1604
buffer *const password,
1605
string_set *cancelled_filenames,
1606
const mono_microsecs *const current_time,
1607
bool *const mandos_client_exited,
1608
bool *const password_is_read,
1609
const char *const dirname){
1610
__attribute__((cleanup(cleanup_string)))
1611
char *dir_pattern = NULL;
1612
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1613
if(ret < 0 or dir_pattern == NULL){
1614
error(0, errno, "Could not create glob pattern for directory %s",
1618
__attribute__((cleanup(globfree)))
1619
glob_t question_filenames = {};
1620
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1621
NULL, &question_filenames)){
1624
error(0, errno, "Failed to open directory %s", dirname);
1627
error(0, errno, "There are no question files in %s", dirname);
1630
error(0, errno, "Could not allocate memory for question file"
1631
" names in %s", dirname);
1635
__attribute__((fallthrough));
1638
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1639
char *const question_filename = strdup(question_filenames
1641
const task_context task = {
1642
.func=open_and_parse_question,
1644
.question_filename=question_filename,
1645
.filename=question_filename,
1647
.cancelled_filenames=cancelled_filenames,
1648
.current_time=current_time,
1649
.mandos_client_exited=mandos_client_exited,
1650
.password_is_read=password_is_read,
1653
if(question_filename == NULL
1654
or not add_to_queue(queue, task)){
1655
error(0, errno, "Failed to add open_and_parse_question for"
1656
" file %s to queue",
1657
question_filenames.gl_pathv[i]);
1658
free(question_filename);
1660
queue->next_run = 1;
1667
__attribute__((nonnull, warn_unused_result))
1668
bool wait_for_event(const int epoll_fd,
1669
const mono_microsecs queue_next_run,
1670
const mono_microsecs current_time){
1671
__attribute__((const))
1672
int milliseconds_to_wait(const mono_microsecs currtime,
1673
const mono_microsecs nextrun){
1674
if(currtime >= nextrun){
1677
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1678
if(wait_time_ms > (uintmax_t)INT_MAX){
1681
return (int)wait_time_ms;
1684
const int wait_time_ms = milliseconds_to_wait(current_time,
1687
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1688
sigset_t temporary_unblocked_sigmask;
1689
/* Get current signal mask */
1690
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1693
/* Remove SIGCHLD from the signal mask */
1694
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1697
struct epoll_event events[8]; /* Ignored */
1698
int ret = epoll_pwait(epoll_fd, events,
1699
sizeof(events) / sizeof(struct epoll_event),
1700
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1701
&temporary_unblocked_sigmask);
1702
if(ret < 0 and errno != EINTR){
1703
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1705
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1708
return clear_all_fds_from_epoll_set(epoll_fd);
1711
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1712
/* Create a new empty epoll set */
1713
__attribute__((cleanup(cleanup_close)))
1714
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1715
if(new_epoll_fd < 0){
1718
/* dup3() the new epoll set fd over the old one, replacing it */
1719
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1725
__attribute__((nonnull, warn_unused_result))
1726
bool run_queue(task_queue **const queue,
1727
string_set *const cancelled_filenames,
1728
bool *const quit_now){
1730
task_queue *new_queue = create_queue();
1731
if(new_queue == NULL){
1735
__attribute__((cleanup(string_set_clear)))
1736
string_set old_cancelled_filenames = {};
1737
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1739
/* Declare i outside the for loop, since we might need i after the
1740
loop in case we aborted in the middle */
1742
for(i=0; i < (*queue)->length and not *quit_now; i++){
1743
task_context *const task = &((*queue)->tasks[i]);
1744
const char *const question_filename = task->question_filename;
1745
/* Skip any task referencing a cancelled question filename */
1746
if(question_filename != NULL
1747
and string_set_contains(old_cancelled_filenames,
1748
question_filename)){
1752
task->func(*task, new_queue);
1756
/* we might be in the middle of the queue, so clean up any
1757
remaining tasks in the current queue */
1758
for(; i < (*queue)->length; i++){
1759
cleanup_task(&((*queue)->tasks[i]));
1773
/* End of regular code section */
1775
/* Start of tests section; here are the tests for the above code */
1777
/* This "fixture" data structure is used by the test setup and
1778
teardown functions */
1780
struct sigaction orig_sigaction;
1781
sigset_t orig_sigmask;
1784
static void test_setup(test_fixture *fixture,
1785
__attribute__((unused))
1786
gconstpointer user_data){
1787
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1788
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1791
static void test_teardown(test_fixture *fixture,
1792
__attribute__((unused))
1793
gconstpointer user_data){
1794
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1795
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1798
/* Utility function used by tests to search queue for matching task */
1799
__attribute__((pure, nonnull, warn_unused_result))
1800
static task_context *find_matching_task(const task_queue *const queue,
1801
const task_context task){
1802
/* The argument "task" structure is a pattern to match; 0 in any
1803
member means any value matches, otherwise the value must match.
1804
The filename strings are compared by strcmp(), not by pointer. */
1805
for(size_t i = 0; i < queue->length; i++){
1806
task_context *const current_task = queue->tasks+i;
1807
/* Check all members of task_context, if set to a non-zero value.
1808
If a member does not match, continue to next task in queue */
1810
/* task_func *const func */
1811
if(task.func != NULL and current_task->func != task.func){
1814
/* char *const question_filename; */
1815
if(task.question_filename != NULL
1816
and (current_task->question_filename == NULL
1817
or strcmp(current_task->question_filename,
1818
task.question_filename) != 0)){
1821
/* const pid_t pid; */
1822
if(task.pid != 0 and current_task->pid != task.pid){
1825
/* const int epoll_fd; */
1826
if(task.epoll_fd != 0
1827
and current_task->epoll_fd != task.epoll_fd){
1830
/* bool *const quit_now; */
1831
if(task.quit_now != NULL
1832
and current_task->quit_now != task.quit_now){
1836
if(task.fd != 0 and current_task->fd != task.fd){
1839
/* bool *const mandos_client_exited; */
1840
if(task.mandos_client_exited != NULL
1841
and current_task->mandos_client_exited
1842
!= task.mandos_client_exited){
1845
/* buffer *const password; */
1846
if(task.password != NULL
1847
and current_task->password != task.password){
1850
/* bool *const password_is_read; */
1851
if(task.password_is_read != NULL
1852
and current_task->password_is_read != task.password_is_read){
1855
/* char *filename; */
1856
if(task.filename != NULL
1857
and (current_task->filename == NULL
1858
or strcmp(current_task->filename, task.filename) != 0)){
1861
/* string_set *const cancelled_filenames; */
1862
if(task.cancelled_filenames != NULL
1863
and current_task->cancelled_filenames
1864
!= task.cancelled_filenames){
1867
/* const mono_microsecs notafter; */
1868
if(task.notafter != 0
1869
and current_task->notafter != task.notafter){
1872
/* const mono_microsecs *const current_time; */
1873
if(task.current_time != NULL
1874
and current_task->current_time != task.current_time){
1877
/* Current task matches all members; return it */
1878
return current_task;
1880
/* No task in queue matches passed pattern task */
1884
static void test_create_queue(__attribute__((unused))
1885
test_fixture *fixture,
1886
__attribute__((unused))
1887
gconstpointer user_data){
1888
__attribute__((cleanup(cleanup_queue)))
1889
task_queue *const queue = create_queue();
1890
g_assert_nonnull(queue);
1891
g_assert_null(queue->tasks);
1892
g_assert_true(queue->length == 0);
1893
g_assert_true(queue->next_run == 0);
1896
static task_func dummy_func;
1898
static void test_add_to_queue(__attribute__((unused))
1899
test_fixture *fixture,
1900
__attribute__((unused))
1901
gconstpointer user_data){
1902
__attribute__((cleanup(cleanup_queue)))
1903
task_queue *queue = create_queue();
1904
g_assert_nonnull(queue);
1906
g_assert_true(add_to_queue(queue,
1907
(task_context){ .func=dummy_func }));
1908
g_assert_true(queue->length == 1);
1909
g_assert_nonnull(queue->tasks);
1910
g_assert_true(queue->tasks[0].func == dummy_func);
1913
static void test_add_to_queue_overflow(__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
g_assert_true(queue->length == 0);
1921
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1923
FILE *real_stderr = stderr;
1924
FILE *devnull = fopen("/dev/null", "we");
1925
g_assert_nonnull(devnull);
1927
const bool ret = add_to_queue(queue,
1928
(task_context){ .func=dummy_func });
1929
g_assert_true(errno == ENOMEM);
1930
g_assert_false(ret);
1931
stderr = real_stderr;
1932
g_assert_cmpint(fclose(devnull), ==, 0);
1933
queue->length = 0; /* Restore real size */
1936
static void dummy_func(__attribute__((unused))
1937
const task_context task,
1938
__attribute__((unused))
1939
task_queue *const queue){
1942
static void test_queue_has_question_empty(__attribute__((unused))
1943
test_fixture *fixture,
1944
__attribute__((unused))
1945
gconstpointer user_data){
1946
__attribute__((cleanup(cleanup_queue)))
1947
task_queue *queue = create_queue();
1948
g_assert_nonnull(queue);
1949
g_assert_false(queue_has_question(queue));
1952
static void test_queue_has_question_false(__attribute__((unused))
1953
test_fixture *fixture,
1954
__attribute__((unused))
1955
gconstpointer user_data){
1956
__attribute__((cleanup(cleanup_queue)))
1957
task_queue *queue = create_queue();
1958
g_assert_nonnull(queue);
1959
g_assert_true(add_to_queue(queue,
1960
(task_context){ .func=dummy_func }));
1961
g_assert_false(queue_has_question(queue));
1964
static void test_queue_has_question_true(__attribute__((unused))
1965
test_fixture *fixture,
1966
__attribute__((unused))
1967
gconstpointer user_data){
1968
__attribute__((cleanup(cleanup_queue)))
1969
task_queue *queue = create_queue();
1970
g_assert_nonnull(queue);
1971
char *const question_filename
1972
= strdup("/nonexistent/question_filename");
1973
g_assert_nonnull(question_filename);
1974
task_context task = {
1976
.question_filename=question_filename,
1978
g_assert_true(add_to_queue(queue, task));
1979
g_assert_true(queue_has_question(queue));
1982
static void test_queue_has_question_false2(__attribute__((unused))
1983
test_fixture *fixture,
1984
__attribute__((unused))
1985
gconstpointer user_data){
1986
__attribute__((cleanup(cleanup_queue)))
1987
task_queue *queue = create_queue();
1988
g_assert_nonnull(queue);
1989
task_context task = { .func=dummy_func };
1990
g_assert_true(add_to_queue(queue, task));
1991
g_assert_true(add_to_queue(queue, task));
1992
g_assert_cmpint((int)queue->length, ==, 2);
1993
g_assert_false(queue_has_question(queue));
1996
static void test_queue_has_question_true2(__attribute__((unused))
1997
test_fixture *fixture,
1998
__attribute__((unused))
1999
gconstpointer user_data){
2000
__attribute__((cleanup(cleanup_queue)))
2001
task_queue *queue = create_queue();
2002
g_assert_nonnull(queue);
2003
task_context task1 = { .func=dummy_func };
2004
g_assert_true(add_to_queue(queue, task1));
2005
char *const question_filename
2006
= strdup("/nonexistent/question_filename");
2007
g_assert_nonnull(question_filename);
2008
task_context task2 = {
2010
.question_filename=question_filename,
2012
g_assert_true(add_to_queue(queue, task2));
2013
g_assert_cmpint((int)queue->length, ==, 2);
2014
g_assert_true(queue_has_question(queue));
2017
static void test_cleanup_buffer(__attribute__((unused))
2018
test_fixture *fixture,
2019
__attribute__((unused))
2020
gconstpointer user_data){
2023
const size_t buffersize = 10;
2025
buf.data = malloc(buffersize);
2026
g_assert_nonnull(buf.data);
2027
if(mlock(buf.data, buffersize) != 0){
2028
g_assert_true(errno == EPERM or errno == ENOMEM);
2031
cleanup_buffer(&buf);
2032
g_assert_null(buf.data);
2036
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2037
test_fixture *fixture,
2038
__attribute__((unused))
2041
__attribute__((cleanup(string_set_clear)))
2042
string_set set = {};
2043
g_assert_false(string_set_contains(set, "")); /* Empty string */
2044
g_assert_false(string_set_contains(set, "test_string"));
2048
test_string_set_with_added_string_contains_it(__attribute__((unused))
2049
test_fixture *fixture,
2050
__attribute__((unused))
2053
__attribute__((cleanup(string_set_clear)))
2054
string_set set = {};
2055
g_assert_true(string_set_add(&set, "test_string"));
2056
g_assert_true(string_set_contains(set, "test_string"));
2060
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2061
test_fixture *fixture,
2062
__attribute__((unused))
2063
gconstpointer user_data){
2064
__attribute__((cleanup(string_set_clear)))
2065
string_set set = {};
2066
g_assert_true(string_set_add(&set, "test_string"));
2067
string_set_clear(&set);
2068
g_assert_false(string_set_contains(set, "test_string"));
2072
void test_string_set_swap_one_with_empty(__attribute__((unused))
2073
test_fixture *fixture,
2074
__attribute__((unused))
2075
gconstpointer user_data){
2076
__attribute__((cleanup(string_set_clear)))
2077
string_set set1 = {};
2078
__attribute__((cleanup(string_set_clear)))
2079
string_set set2 = {};
2080
g_assert_true(string_set_add(&set1, "test_string1"));
2081
string_set_swap(&set1, &set2);
2082
g_assert_false(string_set_contains(set1, "test_string1"));
2083
g_assert_true(string_set_contains(set2, "test_string1"));
2087
void test_string_set_swap_empty_with_one(__attribute__((unused))
2088
test_fixture *fixture,
2089
__attribute__((unused))
2090
gconstpointer user_data){
2091
__attribute__((cleanup(string_set_clear)))
2092
string_set set1 = {};
2093
__attribute__((cleanup(string_set_clear)))
2094
string_set set2 = {};
2095
g_assert_true(string_set_add(&set2, "test_string2"));
2096
string_set_swap(&set1, &set2);
2097
g_assert_true(string_set_contains(set1, "test_string2"));
2098
g_assert_false(string_set_contains(set2, "test_string2"));
2101
static void test_string_set_swap_one_with_one(__attribute__((unused))
2102
test_fixture *fixture,
2103
__attribute__((unused))
2106
__attribute__((cleanup(string_set_clear)))
2107
string_set set1 = {};
2108
__attribute__((cleanup(string_set_clear)))
2109
string_set set2 = {};
2110
g_assert_true(string_set_add(&set1, "test_string1"));
2111
g_assert_true(string_set_add(&set2, "test_string2"));
2112
string_set_swap(&set1, &set2);
2113
g_assert_false(string_set_contains(set1, "test_string1"));
2114
g_assert_true(string_set_contains(set1, "test_string2"));
2115
g_assert_false(string_set_contains(set2, "test_string2"));
2116
g_assert_true(string_set_contains(set2, "test_string1"));
2119
static bool fd_has_cloexec_and_nonblock(const int);
2121
static bool epoll_set_contains(int, int, uint32_t);
2123
static void test_start_mandos_client(test_fixture *fixture,
2124
__attribute__((unused))
2125
gconstpointer user_data){
2127
bool mandos_client_exited = false;
2128
bool quit_now = false;
2129
__attribute__((cleanup(cleanup_close)))
2130
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2131
g_assert_cmpint(epoll_fd, >=, 0);
2132
__attribute__((cleanup(cleanup_queue)))
2133
task_queue *queue = create_queue();
2134
g_assert_nonnull(queue);
2135
buffer password = {};
2136
bool password_is_read = false;
2137
const char helper_directory[] = "/nonexistent";
2138
const char *const argv[] = { "/bin/true", NULL };
2140
g_assert_true(start_mandos_client(queue, epoll_fd,
2141
&mandos_client_exited, &quit_now,
2142
&password, &password_is_read,
2143
&fixture->orig_sigaction,
2144
fixture->orig_sigmask,
2145
helper_directory, 0, 0, argv));
2147
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2149
const task_context *const added_wait_task
2150
= find_matching_task(queue, (task_context){
2151
.func=wait_for_mandos_client_exit,
2152
.mandos_client_exited=&mandos_client_exited,
2153
.quit_now=&quit_now,
2155
g_assert_nonnull(added_wait_task);
2156
g_assert_cmpint(added_wait_task->pid, >, 0);
2157
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2158
waitpid(added_wait_task->pid, NULL, 0);
2160
const task_context *const added_read_task
2161
= find_matching_task(queue, (task_context){
2162
.func=read_mandos_client_output,
2164
.password=&password,
2165
.password_is_read=&password_is_read,
2166
.quit_now=&quit_now,
2168
g_assert_nonnull(added_read_task);
2169
g_assert_cmpint(added_read_task->fd, >, 2);
2170
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2171
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2172
EPOLLIN | EPOLLRDHUP));
2175
static bool fd_has_cloexec_and_nonblock(const int fd){
2176
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2177
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2178
return ((socket_fd_flags >= 0)
2179
and (socket_fd_flags & FD_CLOEXEC)
2180
and (socket_file_flags >= 0)
2181
and (socket_file_flags & O_NONBLOCK));
2184
__attribute__((const))
2185
bool is_privileged(void){
2186
uid_t user = getuid() + 1;
2187
if(user == 0){ /* Overflow check */
2190
gid_t group = getuid() + 1;
2191
if(group == 0){ /* Overflow check */
2194
const pid_t pid = fork();
2195
if(pid == 0){ /* Child */
2196
if(setresgid((uid_t)-1, group, group) == -1){
2198
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2199
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2203
if(setresuid((uid_t)-1, user, user) == -1){
2205
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2206
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2213
error(EXIT_FAILURE, errno, "Failed to fork()");
2217
waitpid(pid, &status, 0);
2218
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2224
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2225
/* Only scan for events in this eventmask */
2226
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2227
__attribute__((cleanup(cleanup_string)))
2228
char *fdinfo_name = NULL;
2229
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2230
g_assert_cmpint(ret, >, 0);
2231
g_assert_nonnull(fdinfo_name);
2233
FILE *fdinfo = fopen(fdinfo_name, "r");
2234
g_assert_nonnull(fdinfo);
2235
uint32_t reported_events;
2240
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2243
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2244
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2245
&found_fd, &reported_events) == 2){
2250
} while(not feof(fdinfo) and not ferror(fdinfo));
2251
g_assert_cmpint(fclose(fdinfo), ==, 0);
2258
/* Don't check events if none are given */
2261
return (reported_events & eventmask) == (events & eventmask);
2264
static void test_start_mandos_client_execv(test_fixture *fixture,
2265
__attribute__((unused))
2266
gconstpointer user_data){
2267
bool mandos_client_exited = false;
2268
bool quit_now = false;
2269
__attribute__((cleanup(cleanup_close)))
2270
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2271
g_assert_cmpint(epoll_fd, >=, 0);
2272
__attribute__((cleanup(cleanup_queue)))
2273
task_queue *queue = create_queue();
2274
g_assert_nonnull(queue);
2275
__attribute__((cleanup(cleanup_buffer)))
2276
buffer password = {};
2277
const char helper_directory[] = "/nonexistent";
2278
/* Can't execv("/", ...), so this should fail */
2279
const char *const argv[] = { "/", NULL };
2282
__attribute__((cleanup(cleanup_close)))
2283
const int devnull_fd = open("/dev/null",
2284
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2285
g_assert_cmpint(devnull_fd, >=, 0);
2286
__attribute__((cleanup(cleanup_close)))
2287
const int real_stderr_fd = dup(STDERR_FILENO);
2288
g_assert_cmpint(real_stderr_fd, >=, 0);
2289
dup2(devnull_fd, STDERR_FILENO);
2291
const bool success = start_mandos_client(queue, epoll_fd,
2292
&mandos_client_exited,
2296
&fixture->orig_sigaction,
2297
fixture->orig_sigmask,
2298
helper_directory, 0, 0,
2300
dup2(real_stderr_fd, STDERR_FILENO);
2301
g_assert_true(success);
2303
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2305
struct timespec starttime, currtime;
2306
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2308
queue->next_run = 0;
2309
string_set cancelled_filenames = {};
2312
__attribute__((cleanup(cleanup_close)))
2313
const int devnull_fd = open("/dev/null",
2314
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2315
g_assert_cmpint(devnull_fd, >=, 0);
2316
__attribute__((cleanup(cleanup_close)))
2317
const int real_stderr_fd = dup(STDERR_FILENO);
2318
g_assert_cmpint(real_stderr_fd, >=, 0);
2319
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2320
dup2(devnull_fd, STDERR_FILENO);
2321
const bool success = run_queue(&queue, &cancelled_filenames,
2323
dup2(real_stderr_fd, STDERR_FILENO);
2328
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2329
} while(((queue->length) > 0)
2331
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2333
g_assert_true(quit_now);
2334
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2335
g_assert_true(mandos_client_exited);
2338
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2339
__attribute__((unused))
2342
if(not is_privileged()){
2343
g_test_skip("Not privileged");
2347
bool mandos_client_exited = false;
2348
bool quit_now = false;
2349
__attribute__((cleanup(cleanup_close)))
2350
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2351
g_assert_cmpint(epoll_fd, >=, 0);
2352
__attribute__((cleanup(cleanup_queue)))
2353
task_queue *queue = create_queue();
2354
g_assert_nonnull(queue);
2355
__attribute__((cleanup(cleanup_buffer)))
2356
buffer password = {};
2357
bool password_is_read = false;
2358
const char helper_directory[] = "/nonexistent";
2359
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2363
const bool success = start_mandos_client(queue, epoll_fd,
2364
&mandos_client_exited,
2365
&quit_now, &password,
2367
&fixture->orig_sigaction,
2368
fixture->orig_sigmask,
2369
helper_directory, user,
2371
g_assert_true(success);
2372
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2374
struct timespec starttime, currtime;
2375
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2377
queue->next_run = 0;
2378
string_set cancelled_filenames = {};
2379
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2380
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2381
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2382
} while(((queue->length) > 0)
2384
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2386
g_assert_false(quit_now);
2387
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2388
g_assert_true(mandos_client_exited);
2390
g_assert_true(password_is_read);
2391
g_assert_nonnull(password.data);
2394
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2396
g_assert_true((uid_t)id == id);
2398
g_assert_cmpuint((unsigned int)id, ==, 0);
2401
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2402
__attribute__((unused))
2405
if(not is_privileged()){
2406
g_test_skip("Not privileged");
2410
bool mandos_client_exited = false;
2411
bool quit_now = false;
2412
__attribute__((cleanup(cleanup_close)))
2413
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2414
g_assert_cmpint(epoll_fd, >=, 0);
2415
__attribute__((cleanup(cleanup_queue)))
2416
task_queue *queue = create_queue();
2417
g_assert_nonnull(queue);
2418
__attribute__((cleanup(cleanup_buffer)))
2419
buffer password = {};
2420
bool password_is_read = false;
2421
const char helper_directory[] = "/nonexistent";
2422
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2426
const bool success = start_mandos_client(queue, epoll_fd,
2427
&mandos_client_exited,
2428
&quit_now, &password,
2430
&fixture->orig_sigaction,
2431
fixture->orig_sigmask,
2432
helper_directory, user,
2434
g_assert_true(success);
2435
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2437
struct timespec starttime, currtime;
2438
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2440
queue->next_run = 0;
2441
string_set cancelled_filenames = {};
2442
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2443
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2444
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2445
} while(((queue->length) > 0)
2447
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2449
g_assert_false(quit_now);
2450
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2451
g_assert_true(mandos_client_exited);
2453
g_assert_true(password_is_read);
2454
g_assert_nonnull(password.data);
2457
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2459
g_assert_true((gid_t)id == id);
2461
g_assert_cmpuint((unsigned int)id, ==, 0);
2464
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2465
__attribute__((unused))
2468
if(not is_privileged()){
2469
g_test_skip("Not privileged");
2473
bool mandos_client_exited = false;
2474
bool quit_now = false;
2475
__attribute__((cleanup(cleanup_close)))
2476
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2477
g_assert_cmpint(epoll_fd, >=, 0);
2478
__attribute__((cleanup(cleanup_queue)))
2479
task_queue *queue = create_queue();
2480
g_assert_nonnull(queue);
2481
__attribute__((cleanup(cleanup_buffer)))
2482
buffer password = {};
2483
bool password_is_read = false;
2484
const char helper_directory[] = "/nonexistent";
2485
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2490
const bool success = start_mandos_client(queue, epoll_fd,
2491
&mandos_client_exited,
2492
&quit_now, &password,
2494
&fixture->orig_sigaction,
2495
fixture->orig_sigmask,
2496
helper_directory, user,
2498
g_assert_true(success);
2499
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2501
struct timespec starttime, currtime;
2502
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2504
queue->next_run = 0;
2505
string_set cancelled_filenames = {};
2506
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2507
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2508
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2509
} while(((queue->length) > 0)
2511
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2513
g_assert_false(quit_now);
2514
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2515
g_assert_true(mandos_client_exited);
2517
g_assert_true(password_is_read);
2518
g_assert_nonnull(password.data);
2521
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2523
g_assert_true((uid_t)id == id);
2525
g_assert_cmpuint((unsigned int)id, ==, user);
2528
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2529
__attribute__((unused))
2532
if(not is_privileged()){
2533
g_test_skip("Not privileged");
2537
bool mandos_client_exited = false;
2538
bool quit_now = false;
2539
__attribute__((cleanup(cleanup_close)))
2540
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2541
g_assert_cmpint(epoll_fd, >=, 0);
2542
__attribute__((cleanup(cleanup_queue)))
2543
task_queue *queue = create_queue();
2544
g_assert_nonnull(queue);
2545
__attribute__((cleanup(cleanup_buffer)))
2546
buffer password = {};
2547
bool password_is_read = false;
2548
const char helper_directory[] = "/nonexistent";
2549
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2554
const bool success = start_mandos_client(queue, epoll_fd,
2555
&mandos_client_exited,
2556
&quit_now, &password,
2558
&fixture->orig_sigaction,
2559
fixture->orig_sigmask,
2560
helper_directory, user,
2562
g_assert_true(success);
2563
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2565
struct timespec starttime, currtime;
2566
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2568
queue->next_run = 0;
2569
string_set cancelled_filenames = {};
2570
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2571
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2572
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2573
} while(((queue->length) > 0)
2575
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2577
g_assert_false(quit_now);
2578
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2579
g_assert_true(mandos_client_exited);
2581
g_assert_true(password_is_read);
2582
g_assert_nonnull(password.data);
2585
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2587
g_assert_true((gid_t)id == id);
2589
g_assert_cmpuint((unsigned int)id, ==, group);
2592
static void test_start_mandos_client_read(test_fixture *fixture,
2593
__attribute__((unused))
2594
gconstpointer user_data){
2595
bool mandos_client_exited = false;
2596
bool quit_now = false;
2597
__attribute__((cleanup(cleanup_close)))
2598
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2599
g_assert_cmpint(epoll_fd, >=, 0);
2600
__attribute__((cleanup(cleanup_queue)))
2601
task_queue *queue = create_queue();
2602
g_assert_nonnull(queue);
2603
__attribute__((cleanup(cleanup_buffer)))
2604
buffer password = {};
2605
bool password_is_read = false;
2606
const char dummy_test_password[] = "dummy test password";
2607
const char helper_directory[] = "/nonexistent";
2608
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
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(dummy_test_password)-1);
2641
g_assert_nonnull(password.data);
2642
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2643
sizeof(dummy_test_password)-1), ==, 0);
2647
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2648
__attribute__((unused))
2651
bool mandos_client_exited = false;
2652
bool quit_now = false;
2653
__attribute__((cleanup(cleanup_close)))
2654
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2655
g_assert_cmpint(epoll_fd, >=, 0);
2656
__attribute__((cleanup(cleanup_queue)))
2657
task_queue *queue = create_queue();
2658
g_assert_nonnull(queue);
2659
__attribute__((cleanup(cleanup_buffer)))
2660
buffer password = {};
2661
bool password_is_read = false;
2662
const char helper_directory[] = "/nonexistent";
2663
const char *const argv[] = { "/bin/sh", "-c",
2664
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2666
const bool success = start_mandos_client(queue, epoll_fd,
2667
&mandos_client_exited,
2668
&quit_now, &password,
2670
&fixture->orig_sigaction,
2671
fixture->orig_sigmask,
2672
helper_directory, 0, 0,
2674
g_assert_true(success);
2675
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2677
struct timespec starttime, currtime;
2678
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2680
queue->next_run = 0;
2681
string_set cancelled_filenames = {};
2682
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2683
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2684
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2685
} while(((queue->length) > 0)
2687
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2689
g_assert_false(quit_now);
2690
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2691
g_assert_true(mandos_client_exited);
2693
g_assert_true(password_is_read);
2694
g_assert_cmpint((int)password.length, ==,
2695
sizeof(helper_directory)-1);
2696
g_assert_nonnull(password.data);
2697
g_assert_cmpint(memcmp(helper_directory, password.data,
2698
sizeof(helper_directory)-1), ==, 0);
2701
__attribute__((nonnull, warn_unused_result))
2702
static bool proc_status_sigblk_to_sigset(const char *const,
2705
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2706
__attribute__((unused))
2707
gconstpointer user_data){
2708
bool mandos_client_exited = false;
2709
bool quit_now = false;
2710
__attribute__((cleanup(cleanup_close)))
2711
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2712
g_assert_cmpint(epoll_fd, >=, 0);
2713
__attribute__((cleanup(cleanup_queue)))
2714
task_queue *queue = create_queue();
2715
g_assert_nonnull(queue);
2716
__attribute__((cleanup(cleanup_buffer)))
2717
buffer password = {};
2718
bool password_is_read = false;
2719
const char helper_directory[] = "/nonexistent";
2720
/* see proc(5) for format of /proc/self/status */
2721
const char *const argv[] = { "/usr/bin/awk",
2722
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2724
g_assert_true(start_mandos_client(queue, epoll_fd,
2725
&mandos_client_exited, &quit_now,
2726
&password, &password_is_read,
2727
&fixture->orig_sigaction,
2728
fixture->orig_sigmask,
2729
helper_directory, 0, 0, argv));
2731
struct timespec starttime, currtime;
2732
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2734
queue->next_run = 0;
2735
string_set cancelled_filenames = {};
2736
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2737
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2738
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2739
} while((not (mandos_client_exited and password_is_read))
2741
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2742
g_assert_true(mandos_client_exited);
2743
g_assert_true(password_is_read);
2745
sigset_t parsed_sigmask;
2746
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2749
for(int signum = 1; signum < NSIG; signum++){
2750
const bool has_signal = sigismember(&parsed_sigmask, signum);
2751
if(sigismember(&fixture->orig_sigmask, signum)){
2752
g_assert_true(has_signal);
2754
g_assert_false(has_signal);
2759
__attribute__((nonnull, warn_unused_result))
2760
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2761
sigset_t *const sigmask){
2762
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2763
uintmax_t scanned_sigmask;
2764
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2767
if(sigemptyset(sigmask) != 0){
2770
for(int signum = 1; signum < NSIG; signum++){
2771
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2772
if(sigaddset(sigmask, signum) != 0){
2780
static void run_task_with_stderr_to_dev_null(const task_context task,
2781
task_queue *const queue);
2784
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2785
test_fixture *fixture,
2786
__attribute__((unused))
2787
gconstpointer user_data){
2789
bool mandos_client_exited = false;
2790
bool quit_now = false;
2792
__attribute__((cleanup(cleanup_queue)))
2793
task_queue *queue = create_queue();
2794
g_assert_nonnull(queue);
2795
const task_context task = {
2796
.func=wait_for_mandos_client_exit,
2798
.mandos_client_exited=&mandos_client_exited,
2799
.quit_now=&quit_now,
2801
run_task_with_stderr_to_dev_null(task, queue);
2803
g_assert_false(mandos_client_exited);
2804
g_assert_true(quit_now);
2805
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2808
static void run_task_with_stderr_to_dev_null(const task_context task,
2809
task_queue *const queue){
2810
FILE *real_stderr = stderr;
2811
FILE *devnull = fopen("/dev/null", "we");
2812
g_assert_nonnull(devnull);
2815
task.func(task, queue);
2816
stderr = real_stderr;
2818
g_assert_cmpint(fclose(devnull), ==, 0);
2822
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2823
__attribute__((unused))
2824
gconstpointer user_data){
2825
bool mandos_client_exited = false;
2826
bool quit_now = false;
2828
pid_t create_eternal_process(void){
2829
const pid_t pid = fork();
2830
if(pid == 0){ /* Child */
2831
if(not restore_signal_handler(&fixture->orig_sigaction)){
2832
_exit(EXIT_FAILURE);
2834
if(not restore_sigmask(&fixture->orig_sigmask)){
2835
_exit(EXIT_FAILURE);
2843
pid_t pid = create_eternal_process();
2844
g_assert_true(pid != -1);
2846
__attribute__((cleanup(cleanup_queue)))
2847
task_queue *queue = create_queue();
2848
g_assert_nonnull(queue);
2849
const task_context task = {
2850
.func=wait_for_mandos_client_exit,
2852
.mandos_client_exited=&mandos_client_exited,
2853
.quit_now=&quit_now,
2855
task.func(task, queue);
2857
g_assert_false(mandos_client_exited);
2858
g_assert_false(quit_now);
2859
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2861
g_assert_nonnull(find_matching_task(queue, (task_context){
2862
.func=wait_for_mandos_client_exit,
2864
.mandos_client_exited=&mandos_client_exited,
2865
.quit_now=&quit_now,
2870
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2871
__attribute__((unused))
2874
bool mandos_client_exited = false;
2875
bool quit_now = false;
2877
pid_t create_successful_process(void){
2878
const pid_t pid = fork();
2879
if(pid == 0){ /* Child */
2880
if(not restore_signal_handler(&fixture->orig_sigaction)){
2881
_exit(EXIT_FAILURE);
2883
if(not restore_sigmask(&fixture->orig_sigmask)){
2884
_exit(EXIT_FAILURE);
2890
const pid_t pid = create_successful_process();
2891
g_assert_true(pid != -1);
2893
__attribute__((cleanup(cleanup_queue)))
2894
task_queue *queue = create_queue();
2895
g_assert_nonnull(queue);
2896
const task_context initial_task = {
2897
.func=wait_for_mandos_client_exit,
2899
.mandos_client_exited=&mandos_client_exited,
2900
.quit_now=&quit_now,
2902
g_assert_true(add_to_queue(queue, initial_task));
2904
struct timespec starttime, currtime;
2905
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2906
__attribute__((cleanup(cleanup_close)))
2907
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2909
queue->next_run = 0;
2910
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2911
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2912
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2913
} while((not mandos_client_exited)
2915
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2917
g_assert_true(mandos_client_exited);
2918
g_assert_false(quit_now);
2919
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2923
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2924
__attribute__((unused))
2927
bool mandos_client_exited = false;
2928
bool quit_now = false;
2930
pid_t create_failing_process(void){
2931
const pid_t pid = fork();
2932
if(pid == 0){ /* Child */
2933
if(not restore_signal_handler(&fixture->orig_sigaction)){
2934
_exit(EXIT_FAILURE);
2936
if(not restore_sigmask(&fixture->orig_sigmask)){
2937
_exit(EXIT_FAILURE);
2943
const pid_t pid = create_failing_process();
2944
g_assert_true(pid != -1);
2946
__attribute__((cleanup(string_set_clear)))
2947
string_set cancelled_filenames = {};
2948
__attribute__((cleanup(cleanup_close)))
2949
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2950
g_assert_cmpint(epoll_fd, >=, 0);
2951
__attribute__((cleanup(cleanup_queue)))
2952
task_queue *queue = create_queue();
2953
g_assert_nonnull(queue);
2954
g_assert_true(add_to_queue(queue, (task_context){
2955
.func=wait_for_mandos_client_exit,
2957
.mandos_client_exited=&mandos_client_exited,
2958
.quit_now=&quit_now,
2961
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2963
__attribute__((cleanup(cleanup_close)))
2964
const int devnull_fd = open("/dev/null",
2965
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2966
g_assert_cmpint(devnull_fd, >=, 0);
2967
__attribute__((cleanup(cleanup_close)))
2968
const int real_stderr_fd = dup(STDERR_FILENO);
2969
g_assert_cmpint(real_stderr_fd, >=, 0);
2971
struct timespec starttime, currtime;
2972
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2974
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2975
dup2(devnull_fd, STDERR_FILENO);
2976
const bool success = run_queue(&queue, &cancelled_filenames,
2978
dup2(real_stderr_fd, STDERR_FILENO);
2983
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2984
} while((not mandos_client_exited)
2986
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2988
g_assert_true(quit_now);
2989
g_assert_true(mandos_client_exited);
2990
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2994
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2995
__attribute__((unused))
2996
gconstpointer user_data){
2997
bool mandos_client_exited = false;
2998
bool quit_now = false;
3000
pid_t create_killed_process(void){
3001
const pid_t pid = fork();
3002
if(pid == 0){ /* Child */
3003
if(not restore_signal_handler(&fixture->orig_sigaction)){
3004
_exit(EXIT_FAILURE);
3006
if(not restore_sigmask(&fixture->orig_sigmask)){
3007
_exit(EXIT_FAILURE);
3016
const pid_t pid = create_killed_process();
3017
g_assert_true(pid != -1);
3019
__attribute__((cleanup(string_set_clear)))
3020
string_set cancelled_filenames = {};
3021
__attribute__((cleanup(cleanup_close)))
3022
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3023
g_assert_cmpint(epoll_fd, >=, 0);
3024
__attribute__((cleanup(cleanup_queue)))
3025
task_queue *queue = create_queue();
3026
g_assert_nonnull(queue);
3027
g_assert_true(add_to_queue(queue, (task_context){
3028
.func=wait_for_mandos_client_exit,
3030
.mandos_client_exited=&mandos_client_exited,
3031
.quit_now=&quit_now,
3034
__attribute__((cleanup(cleanup_close)))
3035
const int devnull_fd = open("/dev/null",
3036
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3037
g_assert_cmpint(devnull_fd, >=, 0);
3038
__attribute__((cleanup(cleanup_close)))
3039
const int real_stderr_fd = dup(STDERR_FILENO);
3040
g_assert_cmpint(real_stderr_fd, >=, 0);
3042
struct timespec starttime, currtime;
3043
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3045
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3046
dup2(devnull_fd, STDERR_FILENO);
3047
const bool success = run_queue(&queue, &cancelled_filenames,
3049
dup2(real_stderr_fd, STDERR_FILENO);
3054
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3055
} while((not mandos_client_exited)
3057
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3059
g_assert_true(mandos_client_exited);
3060
g_assert_true(quit_now);
3061
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3064
static bool epoll_set_does_not_contain(int, int);
3067
void test_read_mandos_client_output_readerror(__attribute__((unused))
3068
test_fixture *fixture,
3069
__attribute__((unused))
3072
__attribute__((cleanup(cleanup_close)))
3073
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3074
g_assert_cmpint(epoll_fd, >=, 0);
3076
__attribute__((cleanup(cleanup_buffer)))
3077
buffer password = {};
3079
/* Reading /proc/self/mem from offset 0 will always give EIO */
3080
const int fd = open("/proc/self/mem",
3081
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3083
bool password_is_read = false;
3084
bool quit_now = false;
3085
__attribute__((cleanup(cleanup_queue)))
3086
task_queue *queue = create_queue();
3087
g_assert_nonnull(queue);
3089
task_context task = {
3090
.func=read_mandos_client_output,
3093
.password=&password,
3094
.password_is_read=&password_is_read,
3095
.quit_now=&quit_now,
3097
run_task_with_stderr_to_dev_null(task, queue);
3098
g_assert_false(password_is_read);
3099
g_assert_cmpint((int)password.length, ==, 0);
3100
g_assert_true(quit_now);
3101
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3103
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3105
g_assert_cmpint(close(fd), ==, -1);
3108
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3109
return not epoll_set_contains(epoll_fd, fd, 0);
3113
void test_read_mandos_client_output_nodata(__attribute__((unused))
3114
test_fixture *fixture,
3115
__attribute__((unused))
3116
gconstpointer user_data){
3117
__attribute__((cleanup(cleanup_close)))
3118
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3119
g_assert_cmpint(epoll_fd, >=, 0);
3122
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3124
__attribute__((cleanup(cleanup_buffer)))
3125
buffer password = {};
3127
bool password_is_read = false;
3128
bool quit_now = false;
3129
__attribute__((cleanup(cleanup_queue)))
3130
task_queue *queue = create_queue();
3131
g_assert_nonnull(queue);
3133
task_context task = {
3134
.func=read_mandos_client_output,
3137
.password=&password,
3138
.password_is_read=&password_is_read,
3139
.quit_now=&quit_now,
3141
task.func(task, queue);
3142
g_assert_false(password_is_read);
3143
g_assert_cmpint((int)password.length, ==, 0);
3144
g_assert_false(quit_now);
3145
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3147
g_assert_nonnull(find_matching_task(queue, (task_context){
3148
.func=read_mandos_client_output,
3151
.password=&password,
3152
.password_is_read=&password_is_read,
3153
.quit_now=&quit_now,
3156
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3157
EPOLLIN | EPOLLRDHUP));
3159
g_assert_cmpint(close(pipefds[1]), ==, 0);
3162
static void test_read_mandos_client_output_eof(__attribute__((unused))
3163
test_fixture *fixture,
3164
__attribute__((unused))
3167
__attribute__((cleanup(cleanup_close)))
3168
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3169
g_assert_cmpint(epoll_fd, >=, 0);
3172
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3173
g_assert_cmpint(close(pipefds[1]), ==, 0);
3175
__attribute__((cleanup(cleanup_buffer)))
3176
buffer password = {};
3178
bool password_is_read = false;
3179
bool quit_now = false;
3180
__attribute__((cleanup(cleanup_queue)))
3181
task_queue *queue = create_queue();
3182
g_assert_nonnull(queue);
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);
3193
g_assert_true(password_is_read);
3194
g_assert_cmpint((int)password.length, ==, 0);
3195
g_assert_false(quit_now);
3196
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3198
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3200
g_assert_cmpint(close(pipefds[0]), ==, -1);
3204
void test_read_mandos_client_output_once(__attribute__((unused))
3205
test_fixture *fixture,
3206
__attribute__((unused))
3207
gconstpointer user_data){
3208
__attribute__((cleanup(cleanup_close)))
3209
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3210
g_assert_cmpint(epoll_fd, >=, 0);
3213
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3215
const char dummy_test_password[] = "dummy test password";
3216
/* Start with a pre-allocated buffer */
3217
__attribute__((cleanup(cleanup_buffer)))
3219
.data=malloc(sizeof(dummy_test_password)),
3221
.allocated=sizeof(dummy_test_password),
3223
g_assert_nonnull(password.data);
3224
if(mlock(password.data, password.allocated) != 0){
3225
g_assert_true(errno == EPERM or errno == ENOMEM);
3228
bool password_is_read = false;
3229
bool quit_now = false;
3230
__attribute__((cleanup(cleanup_queue)))
3231
task_queue *queue = create_queue();
3232
g_assert_nonnull(queue);
3234
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3235
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3236
sizeof(dummy_test_password)),
3237
==, (int)sizeof(dummy_test_password));
3239
task_context task = {
3240
.func=read_mandos_client_output,
3243
.password=&password,
3244
.password_is_read=&password_is_read,
3245
.quit_now=&quit_now,
3247
task.func(task, queue);
3249
g_assert_false(password_is_read);
3250
g_assert_cmpint((int)password.length, ==,
3251
(int)sizeof(dummy_test_password));
3252
g_assert_nonnull(password.data);
3253
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3254
sizeof(dummy_test_password)), ==, 0);
3256
g_assert_false(quit_now);
3257
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3259
g_assert_nonnull(find_matching_task(queue, (task_context){
3260
.func=read_mandos_client_output,
3263
.password=&password,
3264
.password_is_read=&password_is_read,
3265
.quit_now=&quit_now,
3268
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3269
EPOLLIN | EPOLLRDHUP));
3271
g_assert_cmpint(close(pipefds[1]), ==, 0);
3275
void test_read_mandos_client_output_malloc(__attribute__((unused))
3276
test_fixture *fixture,
3277
__attribute__((unused))
3278
gconstpointer user_data){
3279
__attribute__((cleanup(cleanup_close)))
3280
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3281
g_assert_cmpint(epoll_fd, >=, 0);
3284
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3286
const char dummy_test_password[] = "dummy test password";
3287
/* Start with an empty buffer */
3288
__attribute__((cleanup(cleanup_buffer)))
3289
buffer password = {};
3291
bool password_is_read = false;
3292
bool quit_now = false;
3293
__attribute__((cleanup(cleanup_queue)))
3294
task_queue *queue = create_queue();
3295
g_assert_nonnull(queue);
3297
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3298
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3299
sizeof(dummy_test_password)),
3300
==, (int)sizeof(dummy_test_password));
3302
task_context task = {
3303
.func=read_mandos_client_output,
3306
.password=&password,
3307
.password_is_read=&password_is_read,
3308
.quit_now=&quit_now,
3310
task.func(task, queue);
3312
g_assert_false(password_is_read);
3313
g_assert_cmpint((int)password.length, ==,
3314
(int)sizeof(dummy_test_password));
3315
g_assert_nonnull(password.data);
3316
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3317
sizeof(dummy_test_password)), ==, 0);
3319
g_assert_false(quit_now);
3320
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3322
g_assert_nonnull(find_matching_task(queue, (task_context){
3323
.func=read_mandos_client_output,
3326
.password=&password,
3327
.password_is_read=&password_is_read,
3328
.quit_now=&quit_now,
3331
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3332
EPOLLIN | EPOLLRDHUP));
3334
g_assert_cmpint(close(pipefds[1]), ==, 0);
3338
void test_read_mandos_client_output_append(__attribute__((unused))
3339
test_fixture *fixture,
3340
__attribute__((unused))
3341
gconstpointer user_data){
3342
__attribute__((cleanup(cleanup_close)))
3343
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3344
g_assert_cmpint(epoll_fd, >=, 0);
3347
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3349
const char dummy_test_password[] = "dummy test password";
3350
__attribute__((cleanup(cleanup_buffer)))
3352
.data=malloc(PIPE_BUF),
3354
.allocated=PIPE_BUF,
3356
g_assert_nonnull(password.data);
3357
if(mlock(password.data, password.allocated) != 0){
3358
g_assert_true(errno == EPERM or errno == ENOMEM);
3361
memset(password.data, 'x', PIPE_BUF);
3362
char password_expected[PIPE_BUF];
3363
memcpy(password_expected, password.data, PIPE_BUF);
3365
bool password_is_read = false;
3366
bool quit_now = false;
3367
__attribute__((cleanup(cleanup_queue)))
3368
task_queue *queue = create_queue();
3369
g_assert_nonnull(queue);
3371
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3372
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3373
sizeof(dummy_test_password)),
3374
==, (int)sizeof(dummy_test_password));
3376
task_context task = {
3377
.func=read_mandos_client_output,
3380
.password=&password,
3381
.password_is_read=&password_is_read,
3382
.quit_now=&quit_now,
3384
task.func(task, queue);
3386
g_assert_false(password_is_read);
3387
g_assert_cmpint((int)password.length, ==,
3388
PIPE_BUF + sizeof(dummy_test_password));
3389
g_assert_nonnull(password.data);
3390
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3392
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3393
dummy_test_password,
3394
sizeof(dummy_test_password)), ==, 0);
3395
g_assert_false(quit_now);
3396
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3398
g_assert_nonnull(find_matching_task(queue, (task_context){
3399
.func=read_mandos_client_output,
3402
.password=&password,
3403
.password_is_read=&password_is_read,
3404
.quit_now=&quit_now,
3407
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3408
EPOLLIN | EPOLLRDHUP));
3411
static char *make_temporary_directory(void);
3413
static void test_add_inotify_dir_watch(__attribute__((unused))
3414
test_fixture *fixture,
3415
__attribute__((unused))
3416
gconstpointer user_data){
3417
__attribute__((cleanup(cleanup_close)))
3418
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3419
g_assert_cmpint(epoll_fd, >=, 0);
3420
__attribute__((cleanup(cleanup_queue)))
3421
task_queue *queue = create_queue();
3422
g_assert_nonnull(queue);
3423
__attribute__((cleanup(string_set_clear)))
3424
string_set cancelled_filenames = {};
3425
const mono_microsecs current_time = 0;
3427
bool quit_now = false;
3428
buffer password = {};
3429
bool mandos_client_exited = false;
3430
bool password_is_read = false;
3432
__attribute__((cleanup(cleanup_string)))
3433
char *tempdir = make_temporary_directory();
3434
g_assert_nonnull(tempdir);
3436
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3438
&cancelled_filenames,
3440
&mandos_client_exited,
3441
&password_is_read));
3443
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3445
const task_context *const added_read_task
3446
= find_matching_task(queue, (task_context){
3447
.func=read_inotify_event,
3449
.quit_now=&quit_now,
3450
.password=&password,
3452
.cancelled_filenames=&cancelled_filenames,
3453
.current_time=¤t_time,
3454
.mandos_client_exited=&mandos_client_exited,
3455
.password_is_read=&password_is_read,
3457
g_assert_nonnull(added_read_task);
3459
g_assert_cmpint(added_read_task->fd, >, 2);
3460
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3461
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3462
added_read_task->fd,
3463
EPOLLIN | EPOLLRDHUP));
3465
g_assert_cmpint(rmdir(tempdir), ==, 0);
3468
static char *make_temporary_directory(void){
3469
char *name = strdup("/tmp/mandosXXXXXX");
3470
g_assert_nonnull(name);
3471
char *result = mkdtemp(name);
3478
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3479
test_fixture *fixture,
3480
__attribute__((unused))
3481
gconstpointer user_data){
3482
__attribute__((cleanup(cleanup_close)))
3483
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3484
g_assert_cmpint(epoll_fd, >=, 0);
3485
__attribute__((cleanup(cleanup_queue)))
3486
task_queue *queue = create_queue();
3487
g_assert_nonnull(queue);
3488
__attribute__((cleanup(string_set_clear)))
3489
string_set cancelled_filenames = {};
3490
const mono_microsecs current_time = 0;
3492
bool quit_now = false;
3493
buffer password = {};
3494
bool mandos_client_exited = false;
3495
bool password_is_read = false;
3497
const char nonexistent_dir[] = "/nonexistent";
3499
FILE *real_stderr = stderr;
3500
FILE *devnull = fopen("/dev/null", "we");
3501
g_assert_nonnull(devnull);
3503
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3504
&password, nonexistent_dir,
3505
&cancelled_filenames,
3507
&mandos_client_exited,
3508
&password_is_read));
3509
stderr = real_stderr;
3510
g_assert_cmpint(fclose(devnull), ==, 0);
3512
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3515
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3516
test_fixture *fixture,
3517
__attribute__((unused))
3520
__attribute__((cleanup(cleanup_close)))
3521
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3522
g_assert_cmpint(epoll_fd, >=, 0);
3523
__attribute__((cleanup(cleanup_queue)))
3524
task_queue *queue = create_queue();
3525
g_assert_nonnull(queue);
3526
__attribute__((cleanup(string_set_clear)))
3527
string_set cancelled_filenames = {};
3528
const mono_microsecs current_time = 0;
3530
bool quit_now = false;
3531
buffer password = {};
3532
bool mandos_client_exited = false;
3533
bool password_is_read = false;
3535
const char not_a_directory[] = "/dev/tty";
3537
FILE *real_stderr = stderr;
3538
FILE *devnull = fopen("/dev/null", "we");
3539
g_assert_nonnull(devnull);
3541
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3542
&password, not_a_directory,
3543
&cancelled_filenames,
3545
&mandos_client_exited,
3546
&password_is_read));
3547
stderr = real_stderr;
3548
g_assert_cmpint(fclose(devnull), ==, 0);
3550
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3553
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3554
test_fixture *fixture,
3555
__attribute__((unused))
3558
__attribute__((cleanup(cleanup_close)))
3559
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3560
g_assert_cmpint(epoll_fd, >=, 0);
3561
__attribute__((cleanup(cleanup_queue)))
3562
task_queue *queue = create_queue();
3563
g_assert_nonnull(queue);
3564
__attribute__((cleanup(string_set_clear)))
3565
string_set cancelled_filenames = {};
3566
const mono_microsecs current_time = 0;
3568
bool quit_now = false;
3569
buffer password = {};
3570
bool mandos_client_exited = false;
3571
bool password_is_read = false;
3573
__attribute__((cleanup(cleanup_string)))
3574
char *tempdir = make_temporary_directory();
3575
g_assert_nonnull(tempdir);
3577
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3579
&cancelled_filenames,
3581
&mandos_client_exited,
3582
&password_is_read));
3584
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3586
const task_context *const added_read_task
3587
= find_matching_task(queue,
3588
(task_context){ .func=read_inotify_event });
3589
g_assert_nonnull(added_read_task);
3591
g_assert_cmpint(added_read_task->fd, >, 2);
3592
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3594
/* "sufficient to read at least one event." - inotify(7) */
3595
const size_t ievent_size = (sizeof(struct inotify_event)
3597
struct inotify_event *ievent = malloc(ievent_size);
3598
g_assert_nonnull(ievent);
3600
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3602
g_assert_cmpint(errno, ==, EAGAIN);
3606
g_assert_cmpint(rmdir(tempdir), ==, 0);
3609
static char *make_temporary_file_in_directory(const char
3613
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3614
test_fixture *fixture,
3615
__attribute__((unused))
3618
__attribute__((cleanup(cleanup_close)))
3619
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3620
g_assert_cmpint(epoll_fd, >=, 0);
3621
__attribute__((cleanup(cleanup_queue)))
3622
task_queue *queue = create_queue();
3623
g_assert_nonnull(queue);
3624
__attribute__((cleanup(string_set_clear)))
3625
string_set cancelled_filenames = {};
3626
const mono_microsecs current_time = 0;
3628
bool quit_now = false;
3629
buffer password = {};
3630
bool mandos_client_exited = false;
3631
bool password_is_read = false;
3633
__attribute__((cleanup(cleanup_string)))
3634
char *tempdir = make_temporary_directory();
3635
g_assert_nonnull(tempdir);
3637
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3639
&cancelled_filenames,
3641
&mandos_client_exited,
3642
&password_is_read));
3644
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3646
const task_context *const added_read_task
3647
= find_matching_task(queue,
3648
(task_context){ .func=read_inotify_event });
3649
g_assert_nonnull(added_read_task);
3651
g_assert_cmpint(added_read_task->fd, >, 2);
3652
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3654
__attribute__((cleanup(cleanup_string)))
3655
char *filename = make_temporary_file_in_directory(tempdir);
3656
g_assert_nonnull(filename);
3658
/* "sufficient to read at least one event." - inotify(7) */
3659
const size_t ievent_size = (sizeof(struct inotify_event)
3661
struct inotify_event *ievent = malloc(ievent_size);
3662
g_assert_nonnull(ievent);
3664
ssize_t read_size = 0;
3665
read_size = read(added_read_task->fd, ievent, ievent_size);
3667
g_assert_cmpint((int)read_size, >, 0);
3668
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3669
g_assert_cmpstr(ievent->name, ==, basename(filename));
3673
g_assert_cmpint(unlink(filename), ==, 0);
3674
g_assert_cmpint(rmdir(tempdir), ==, 0);
3677
static char *make_temporary_prefixed_file_in_directory(const char
3681
char *filename = NULL;
3682
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3684
g_assert_nonnull(filename);
3685
const int fd = mkostemp(filename, O_CLOEXEC);
3686
g_assert_cmpint(fd, >=, 0);
3687
g_assert_cmpint(close(fd), ==, 0);
3691
static char *make_temporary_file_in_directory(const char
3693
return make_temporary_prefixed_file_in_directory("temp", dir);
3697
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3698
test_fixture *fixture,
3699
__attribute__((unused))
3700
gconstpointer user_data){
3701
__attribute__((cleanup(cleanup_close)))
3702
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3703
g_assert_cmpint(epoll_fd, >=, 0);
3704
__attribute__((cleanup(cleanup_queue)))
3705
task_queue *queue = create_queue();
3706
g_assert_nonnull(queue);
3707
__attribute__((cleanup(string_set_clear)))
3708
string_set cancelled_filenames = {};
3709
const mono_microsecs current_time = 0;
3711
bool quit_now = false;
3712
buffer password = {};
3713
bool mandos_client_exited = false;
3714
bool password_is_read = false;
3716
__attribute__((cleanup(cleanup_string)))
3717
char *watchdir = make_temporary_directory();
3718
g_assert_nonnull(watchdir);
3720
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3721
&password, watchdir,
3722
&cancelled_filenames,
3724
&mandos_client_exited,
3725
&password_is_read));
3727
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3729
const task_context *const added_read_task
3730
= find_matching_task(queue,
3731
(task_context){ .func=read_inotify_event });
3732
g_assert_nonnull(added_read_task);
3734
g_assert_cmpint(added_read_task->fd, >, 2);
3735
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3737
char *sourcedir = make_temporary_directory();
3738
g_assert_nonnull(sourcedir);
3740
__attribute__((cleanup(cleanup_string)))
3741
char *filename = make_temporary_file_in_directory(sourcedir);
3742
g_assert_nonnull(filename);
3744
__attribute__((cleanup(cleanup_string)))
3745
char *targetfilename = NULL;
3746
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3747
basename(filename)), >, 0);
3748
g_assert_nonnull(targetfilename);
3750
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3751
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3754
/* "sufficient to read at least one event." - inotify(7) */
3755
const size_t ievent_size = (sizeof(struct inotify_event)
3757
struct inotify_event *ievent = malloc(ievent_size);
3758
g_assert_nonnull(ievent);
3760
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3762
g_assert_cmpint((int)read_size, >, 0);
3763
g_assert_true(ievent->mask & IN_MOVED_TO);
3764
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3768
g_assert_cmpint(unlink(targetfilename), ==, 0);
3769
g_assert_cmpint(rmdir(watchdir), ==, 0);
3773
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3774
test_fixture *fixture,
3775
__attribute__((unused))
3778
__attribute__((cleanup(cleanup_close)))
3779
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3780
g_assert_cmpint(epoll_fd, >=, 0);
3781
__attribute__((cleanup(cleanup_queue)))
3782
task_queue *queue = create_queue();
3783
g_assert_nonnull(queue);
3784
__attribute__((cleanup(string_set_clear)))
3785
string_set cancelled_filenames = {};
3786
const mono_microsecs current_time = 0;
3788
bool quit_now = false;
3789
buffer password = {};
3790
bool mandos_client_exited = false;
3791
bool password_is_read = false;
3793
__attribute__((cleanup(cleanup_string)))
3794
char *tempdir = make_temporary_directory();
3795
g_assert_nonnull(tempdir);
3797
__attribute__((cleanup(cleanup_string)))
3798
char *tempfilename = make_temporary_file_in_directory(tempdir);
3799
g_assert_nonnull(tempfilename);
3801
__attribute__((cleanup(cleanup_string)))
3802
char *targetdir = make_temporary_directory();
3803
g_assert_nonnull(targetdir);
3805
__attribute__((cleanup(cleanup_string)))
3806
char *targetfilename = NULL;
3807
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3808
basename(tempfilename)), >, 0);
3809
g_assert_nonnull(targetfilename);
3811
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3813
&cancelled_filenames,
3815
&mandos_client_exited,
3816
&password_is_read));
3818
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3820
const task_context *const added_read_task
3821
= find_matching_task(queue,
3822
(task_context){ .func=read_inotify_event });
3823
g_assert_nonnull(added_read_task);
3825
/* "sufficient to read at least one event." - inotify(7) */
3826
const size_t ievent_size = (sizeof(struct inotify_event)
3828
struct inotify_event *ievent = malloc(ievent_size);
3829
g_assert_nonnull(ievent);
3831
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3833
g_assert_cmpint((int)read_size, >, 0);
3834
g_assert_true(ievent->mask & IN_MOVED_FROM);
3835
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3839
g_assert_cmpint(unlink(targetfilename), ==, 0);
3840
g_assert_cmpint(rmdir(targetdir), ==, 0);
3841
g_assert_cmpint(rmdir(tempdir), ==, 0);
3845
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3846
test_fixture *fixture,
3847
__attribute__((unused))
3848
gconstpointer user_data){
3849
__attribute__((cleanup(cleanup_close)))
3850
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3851
g_assert_cmpint(epoll_fd, >=, 0);
3852
__attribute__((cleanup(cleanup_queue)))
3853
task_queue *queue = create_queue();
3854
g_assert_nonnull(queue);
3855
__attribute__((cleanup(string_set_clear)))
3856
string_set cancelled_filenames = {};
3857
const mono_microsecs current_time = 0;
3859
bool quit_now = false;
3860
buffer password = {};
3861
bool mandos_client_exited = false;
3862
bool password_is_read = false;
3864
__attribute__((cleanup(cleanup_string)))
3865
char *tempdir = make_temporary_directory();
3866
g_assert_nonnull(tempdir);
3868
__attribute__((cleanup(cleanup_string)))
3869
char *tempfile = make_temporary_file_in_directory(tempdir);
3870
g_assert_nonnull(tempfile);
3872
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3874
&cancelled_filenames,
3876
&mandos_client_exited,
3877
&password_is_read));
3878
g_assert_cmpint(unlink(tempfile), ==, 0);
3880
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3882
const task_context *const added_read_task
3883
= find_matching_task(queue,
3884
(task_context){ .func=read_inotify_event });
3885
g_assert_nonnull(added_read_task);
3887
g_assert_cmpint(added_read_task->fd, >, 2);
3888
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3890
/* "sufficient to read at least one event." - inotify(7) */
3891
const size_t ievent_size = (sizeof(struct inotify_event)
3893
struct inotify_event *ievent = malloc(ievent_size);
3894
g_assert_nonnull(ievent);
3896
ssize_t read_size = 0;
3897
read_size = read(added_read_task->fd, ievent, ievent_size);
3899
g_assert_cmpint((int)read_size, >, 0);
3900
g_assert_true(ievent->mask & IN_DELETE);
3901
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3905
g_assert_cmpint(rmdir(tempdir), ==, 0);
3909
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3910
test_fixture *fixture,
3911
__attribute__((unused))
3914
__attribute__((cleanup(cleanup_close)))
3915
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3916
g_assert_cmpint(epoll_fd, >=, 0);
3917
__attribute__((cleanup(cleanup_queue)))
3918
task_queue *queue = create_queue();
3919
g_assert_nonnull(queue);
3920
__attribute__((cleanup(string_set_clear)))
3921
string_set cancelled_filenames = {};
3922
const mono_microsecs current_time = 0;
3924
bool quit_now = false;
3925
buffer password = {};
3926
bool mandos_client_exited = false;
3927
bool password_is_read = false;
3929
__attribute__((cleanup(cleanup_string)))
3930
char *tempdir = make_temporary_directory();
3931
g_assert_nonnull(tempdir);
3933
__attribute__((cleanup(cleanup_string)))
3934
char *tempfile = make_temporary_file_in_directory(tempdir);
3935
g_assert_nonnull(tempfile);
3936
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3938
g_assert_cmpint(tempfile_fd, >, 2);
3940
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3942
&cancelled_filenames,
3944
&mandos_client_exited,
3945
&password_is_read));
3946
g_assert_cmpint(unlink(tempfile), ==, 0);
3948
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3950
const task_context *const added_read_task
3951
= find_matching_task(queue,
3952
(task_context){ .func=read_inotify_event });
3953
g_assert_nonnull(added_read_task);
3955
g_assert_cmpint(added_read_task->fd, >, 2);
3956
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3958
/* "sufficient to read at least one event." - inotify(7) */
3959
const size_t ievent_size = (sizeof(struct inotify_event)
3961
struct inotify_event *ievent = malloc(ievent_size);
3962
g_assert_nonnull(ievent);
3964
ssize_t read_size = 0;
3965
read_size = read(added_read_task->fd, ievent, ievent_size);
3967
g_assert_cmpint((int)read_size, >, 0);
3968
g_assert_true(ievent->mask & IN_DELETE);
3969
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3971
g_assert_cmpint(close(tempfile_fd), ==, 0);
3973
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3974
file not appear as an ievent, so we should not see it now. */
3975
read_size = read(added_read_task->fd, ievent, ievent_size);
3976
g_assert_cmpint((int)read_size, ==, -1);
3977
g_assert_true(errno == EAGAIN);
3981
g_assert_cmpint(rmdir(tempdir), ==, 0);
3984
static void test_read_inotify_event_readerror(__attribute__((unused))
3985
test_fixture *fixture,
3986
__attribute__((unused))
3989
__attribute__((cleanup(cleanup_close)))
3990
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3991
g_assert_cmpint(epoll_fd, >=, 0);
3992
const mono_microsecs current_time = 0;
3994
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3995
const int fd = open("/proc/self/mem",
3996
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3998
bool quit_now = false;
3999
__attribute__((cleanup(cleanup_queue)))
4000
task_queue *queue = create_queue();
4001
g_assert_nonnull(queue);
4003
task_context task = {
4004
.func=read_inotify_event,
4007
.quit_now=&quit_now,
4008
.filename=strdup("/nonexistent"),
4009
.cancelled_filenames = &(string_set){},
4011
.current_time=¤t_time,
4013
g_assert_nonnull(task.filename);
4014
run_task_with_stderr_to_dev_null(task, queue);
4015
g_assert_true(quit_now);
4016
g_assert_true(queue->next_run == 0);
4017
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4019
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4021
g_assert_cmpint(close(fd), ==, -1);
4024
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4025
test_fixture *fixture,
4026
__attribute__((unused))
4029
const mono_microsecs current_time = 17;
4032
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4033
const int epoll_fd = pipefds[0]; /* This will obviously fail */
4035
bool quit_now = false;
4036
buffer password = {};
4037
bool mandos_client_exited = false;
4038
bool password_is_read = false;
4039
__attribute__((cleanup(cleanup_queue)))
4040
task_queue *queue = create_queue();
4041
g_assert_nonnull(queue);
4043
task_context task = {
4044
.func=read_inotify_event,
4047
.quit_now=&quit_now,
4048
.password=&password,
4049
.filename=strdup("/nonexistent"),
4050
.cancelled_filenames = &(string_set){},
4052
.current_time=¤t_time,
4053
.mandos_client_exited=&mandos_client_exited,
4054
.password_is_read=&password_is_read,
4056
g_assert_nonnull(task.filename);
4057
run_task_with_stderr_to_dev_null(task, queue);
4059
g_assert_nonnull(find_matching_task(queue, task));
4060
g_assert_true(queue->next_run == 1000000 + current_time);
4062
g_assert_cmpint(close(pipefds[0]), ==, 0);
4063
g_assert_cmpint(close(pipefds[1]), ==, 0);
4066
static void test_read_inotify_event_nodata(__attribute__((unused))
4067
test_fixture *fixture,
4068
__attribute__((unused))
4069
gconstpointer user_data){
4070
__attribute__((cleanup(cleanup_close)))
4071
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4072
g_assert_cmpint(epoll_fd, >=, 0);
4073
const mono_microsecs current_time = 0;
4076
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4078
bool quit_now = false;
4079
buffer password = {};
4080
bool mandos_client_exited = false;
4081
bool password_is_read = false;
4082
__attribute__((cleanup(cleanup_queue)))
4083
task_queue *queue = create_queue();
4084
g_assert_nonnull(queue);
4086
task_context task = {
4087
.func=read_inotify_event,
4090
.quit_now=&quit_now,
4091
.password=&password,
4092
.filename=strdup("/nonexistent"),
4093
.cancelled_filenames = &(string_set){},
4095
.current_time=¤t_time,
4096
.mandos_client_exited=&mandos_client_exited,
4097
.password_is_read=&password_is_read,
4099
g_assert_nonnull(task.filename);
4100
task.func(task, queue);
4101
g_assert_false(quit_now);
4102
g_assert_true(queue->next_run == 0);
4103
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4105
g_assert_nonnull(find_matching_task(queue, (task_context){
4106
.func=read_inotify_event,
4109
.quit_now=&quit_now,
4110
.password=&password,
4111
.filename=task.filename,
4112
.cancelled_filenames=task.cancelled_filenames,
4113
.current_time=¤t_time,
4114
.mandos_client_exited=&mandos_client_exited,
4115
.password_is_read=&password_is_read,
4118
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4119
EPOLLIN | EPOLLRDHUP));
4121
g_assert_cmpint(close(pipefds[1]), ==, 0);
4124
static void test_read_inotify_event_eof(__attribute__((unused))
4125
test_fixture *fixture,
4126
__attribute__((unused))
4127
gconstpointer user_data){
4128
__attribute__((cleanup(cleanup_close)))
4129
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4130
g_assert_cmpint(epoll_fd, >=, 0);
4131
const mono_microsecs current_time = 0;
4134
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4135
g_assert_cmpint(close(pipefds[1]), ==, 0);
4137
bool quit_now = false;
4138
buffer password = {};
4139
__attribute__((cleanup(cleanup_queue)))
4140
task_queue *queue = create_queue();
4141
g_assert_nonnull(queue);
4143
task_context task = {
4144
.func=read_inotify_event,
4147
.quit_now=&quit_now,
4148
.password=&password,
4149
.filename=strdup("/nonexistent"),
4150
.cancelled_filenames = &(string_set){},
4152
.current_time=¤t_time,
4154
run_task_with_stderr_to_dev_null(task, queue);
4155
g_assert_true(quit_now);
4156
g_assert_true(queue->next_run == 0);
4157
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4159
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4161
g_assert_cmpint(close(pipefds[0]), ==, -1);
4165
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4166
test_fixture *fixture,
4167
__attribute__((unused))
4168
gconstpointer user_data){
4169
__attribute__((cleanup(cleanup_close)))
4170
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4171
g_assert_cmpint(epoll_fd, >=, 0);
4172
const mono_microsecs current_time = 0;
4175
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4177
/* "sufficient to read at least one event." - inotify(7) */
4178
const size_t ievent_max_size = (sizeof(struct inotify_event)
4180
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4182
struct inotify_event event;
4183
char name_buffer[NAME_MAX + 1];
4185
struct inotify_event *const ievent = &ievent_buffer.event;
4187
const char dummy_file_name[] = "ask.dummy_file_name";
4188
ievent->mask = IN_CLOSE_WRITE;
4189
ievent->len = sizeof(dummy_file_name);
4190
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4191
const size_t ievent_size = (sizeof(struct inotify_event)
4192
+ sizeof(dummy_file_name));
4193
#if defined(__GNUC__) and __GNUC__ >= 11
4194
#pragma GCC diagnostic push
4195
/* ievent is pointing into a struct which is of sufficient size */
4196
#pragma GCC diagnostic ignored "-Wstringop-overread"
4198
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4200
#if defined(__GNUC__) and __GNUC__ >= 11
4201
#pragma GCC diagnostic pop
4203
g_assert_cmpint(close(pipefds[1]), ==, 0);
4205
bool quit_now = false;
4206
buffer password = {};
4207
bool mandos_client_exited = false;
4208
bool password_is_read = false;
4209
__attribute__((cleanup(cleanup_queue)))
4210
task_queue *queue = create_queue();
4211
g_assert_nonnull(queue);
4213
task_context task = {
4214
.func=read_inotify_event,
4217
.quit_now=&quit_now,
4218
.password=&password,
4219
.filename=strdup("/nonexistent"),
4220
.cancelled_filenames = &(string_set){},
4222
.current_time=¤t_time,
4223
.mandos_client_exited=&mandos_client_exited,
4224
.password_is_read=&password_is_read,
4226
task.func(task, queue);
4227
g_assert_false(quit_now);
4228
g_assert_true(queue->next_run != 0);
4229
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4231
g_assert_nonnull(find_matching_task(queue, (task_context){
4232
.func=read_inotify_event,
4235
.quit_now=&quit_now,
4236
.password=&password,
4237
.filename=task.filename,
4238
.cancelled_filenames=task.cancelled_filenames,
4239
.current_time=¤t_time,
4240
.mandos_client_exited=&mandos_client_exited,
4241
.password_is_read=&password_is_read,
4244
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4245
EPOLLIN | EPOLLRDHUP));
4247
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4249
__attribute__((cleanup(cleanup_string)))
4250
char *filename = NULL;
4251
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4252
dummy_file_name), >, 0);
4253
g_assert_nonnull(filename);
4254
g_assert_nonnull(find_matching_task(queue, (task_context){
4255
.func=open_and_parse_question,
4258
.question_filename=filename,
4259
.password=&password,
4260
.cancelled_filenames=task.cancelled_filenames,
4261
.current_time=¤t_time,
4262
.mandos_client_exited=&mandos_client_exited,
4263
.password_is_read=&password_is_read,
4268
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4269
test_fixture *fixture,
4270
__attribute__((unused))
4271
gconstpointer user_data){
4272
__attribute__((cleanup(cleanup_close)))
4273
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4274
g_assert_cmpint(epoll_fd, >=, 0);
4275
const mono_microsecs current_time = 0;
4278
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4280
/* "sufficient to read at least one event." - inotify(7) */
4281
const size_t ievent_max_size = (sizeof(struct inotify_event)
4283
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4285
struct inotify_event event;
4286
char name_buffer[NAME_MAX + 1];
4288
struct inotify_event *const ievent = &ievent_buffer.event;
4290
const char dummy_file_name[] = "ask.dummy_file_name";
4291
ievent->mask = IN_MOVED_TO;
4292
ievent->len = sizeof(dummy_file_name);
4293
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4294
const size_t ievent_size = (sizeof(struct inotify_event)
4295
+ sizeof(dummy_file_name));
4296
#if defined(__GNUC__) and __GNUC__ >= 11
4297
#pragma GCC diagnostic push
4298
/* ievent is pointing into a struct which is of sufficient size */
4299
#pragma GCC diagnostic ignored "-Wstringop-overread"
4301
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4303
#if defined(__GNUC__) and __GNUC__ >= 11
4304
#pragma GCC diagnostic pop
4306
g_assert_cmpint(close(pipefds[1]), ==, 0);
4308
bool quit_now = false;
4309
buffer password = {};
4310
bool mandos_client_exited = false;
4311
bool password_is_read = false;
4312
__attribute__((cleanup(cleanup_queue)))
4313
task_queue *queue = create_queue();
4314
g_assert_nonnull(queue);
4316
task_context task = {
4317
.func=read_inotify_event,
4320
.quit_now=&quit_now,
4321
.password=&password,
4322
.filename=strdup("/nonexistent"),
4323
.cancelled_filenames = &(string_set){},
4325
.current_time=¤t_time,
4326
.mandos_client_exited=&mandos_client_exited,
4327
.password_is_read=&password_is_read,
4329
task.func(task, queue);
4330
g_assert_false(quit_now);
4331
g_assert_true(queue->next_run != 0);
4332
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4334
g_assert_nonnull(find_matching_task(queue, (task_context){
4335
.func=read_inotify_event,
4338
.quit_now=&quit_now,
4339
.password=&password,
4340
.filename=task.filename,
4341
.cancelled_filenames=task.cancelled_filenames,
4342
.current_time=¤t_time,
4343
.mandos_client_exited=&mandos_client_exited,
4344
.password_is_read=&password_is_read,
4347
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4348
EPOLLIN | EPOLLRDHUP));
4350
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4352
__attribute__((cleanup(cleanup_string)))
4353
char *filename = NULL;
4354
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4355
dummy_file_name), >, 0);
4356
g_assert_nonnull(filename);
4357
g_assert_nonnull(find_matching_task(queue, (task_context){
4358
.func=open_and_parse_question,
4361
.question_filename=filename,
4362
.password=&password,
4363
.cancelled_filenames=task.cancelled_filenames,
4364
.current_time=¤t_time,
4365
.mandos_client_exited=&mandos_client_exited,
4366
.password_is_read=&password_is_read,
4371
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4372
test_fixture *fixture,
4373
__attribute__((unused))
4374
gconstpointer user_data){
4375
__attribute__((cleanup(cleanup_close)))
4376
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4377
g_assert_cmpint(epoll_fd, >=, 0);
4378
__attribute__((cleanup(string_set_clear)))
4379
string_set cancelled_filenames = {};
4380
const mono_microsecs current_time = 0;
4383
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4385
/* "sufficient to read at least one event." - inotify(7) */
4386
const size_t ievent_max_size = (sizeof(struct inotify_event)
4388
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4390
struct inotify_event event;
4391
char name_buffer[NAME_MAX + 1];
4393
struct inotify_event *const ievent = &ievent_buffer.event;
4395
const char dummy_file_name[] = "ask.dummy_file_name";
4396
ievent->mask = IN_MOVED_FROM;
4397
ievent->len = sizeof(dummy_file_name);
4398
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4399
const size_t ievent_size = (sizeof(struct inotify_event)
4400
+ sizeof(dummy_file_name));
4401
#if defined(__GNUC__) and __GNUC__ >= 11
4402
#pragma GCC diagnostic push
4403
/* ievent is pointing into a struct which is of sufficient size */
4404
#pragma GCC diagnostic ignored "-Wstringop-overread"
4406
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4408
#if defined(__GNUC__) and __GNUC__ >= 11
4409
#pragma GCC diagnostic pop
4411
g_assert_cmpint(close(pipefds[1]), ==, 0);
4413
bool quit_now = false;
4414
buffer password = {};
4415
bool mandos_client_exited = false;
4416
bool password_is_read = false;
4417
__attribute__((cleanup(cleanup_queue)))
4418
task_queue *queue = create_queue();
4419
g_assert_nonnull(queue);
4421
task_context task = {
4422
.func=read_inotify_event,
4425
.quit_now=&quit_now,
4426
.password=&password,
4427
.filename=strdup("/nonexistent"),
4428
.cancelled_filenames=&cancelled_filenames,
4429
.current_time=¤t_time,
4430
.mandos_client_exited=&mandos_client_exited,
4431
.password_is_read=&password_is_read,
4433
task.func(task, queue);
4434
g_assert_false(quit_now);
4435
g_assert_true(queue->next_run == 0);
4436
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4438
g_assert_nonnull(find_matching_task(queue, (task_context){
4439
.func=read_inotify_event,
4442
.quit_now=&quit_now,
4443
.password=&password,
4444
.filename=task.filename,
4445
.cancelled_filenames=&cancelled_filenames,
4446
.current_time=¤t_time,
4447
.mandos_client_exited=&mandos_client_exited,
4448
.password_is_read=&password_is_read,
4451
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4452
EPOLLIN | EPOLLRDHUP));
4454
__attribute__((cleanup(cleanup_string)))
4455
char *filename = NULL;
4456
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4457
dummy_file_name), >, 0);
4458
g_assert_nonnull(filename);
4459
g_assert_true(string_set_contains(*task.cancelled_filenames,
4463
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4464
test_fixture *fixture,
4465
__attribute__((unused))
4468
__attribute__((cleanup(cleanup_close)))
4469
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4470
g_assert_cmpint(epoll_fd, >=, 0);
4471
__attribute__((cleanup(string_set_clear)))
4472
string_set cancelled_filenames = {};
4473
const mono_microsecs current_time = 0;
4476
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4478
/* "sufficient to read at least one event." - inotify(7) */
4479
const size_t ievent_max_size = (sizeof(struct inotify_event)
4481
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4483
struct inotify_event event;
4484
char name_buffer[NAME_MAX + 1];
4486
struct inotify_event *const ievent = &ievent_buffer.event;
4488
const char dummy_file_name[] = "ask.dummy_file_name";
4489
ievent->mask = IN_DELETE;
4490
ievent->len = sizeof(dummy_file_name);
4491
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4492
const size_t ievent_size = (sizeof(struct inotify_event)
4493
+ sizeof(dummy_file_name));
4494
#if defined(__GNUC__) and __GNUC__ >= 11
4495
#pragma GCC diagnostic push
4496
/* ievent is pointing into a struct which is of sufficient size */
4497
#pragma GCC diagnostic ignored "-Wstringop-overread"
4499
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4501
#if defined(__GNUC__) and __GNUC__ >= 11
4502
#pragma GCC diagnostic pop
4504
g_assert_cmpint(close(pipefds[1]), ==, 0);
4506
bool quit_now = false;
4507
buffer password = {};
4508
bool mandos_client_exited = false;
4509
bool password_is_read = false;
4510
__attribute__((cleanup(cleanup_queue)))
4511
task_queue *queue = create_queue();
4512
g_assert_nonnull(queue);
4514
task_context task = {
4515
.func=read_inotify_event,
4518
.quit_now=&quit_now,
4519
.password=&password,
4520
.filename=strdup("/nonexistent"),
4521
.cancelled_filenames=&cancelled_filenames,
4522
.current_time=¤t_time,
4523
.mandos_client_exited=&mandos_client_exited,
4524
.password_is_read=&password_is_read,
4526
task.func(task, queue);
4527
g_assert_false(quit_now);
4528
g_assert_true(queue->next_run == 0);
4529
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4531
g_assert_nonnull(find_matching_task(queue, (task_context){
4532
.func=read_inotify_event,
4535
.quit_now=&quit_now,
4536
.password=&password,
4537
.filename=task.filename,
4538
.cancelled_filenames=&cancelled_filenames,
4539
.current_time=¤t_time,
4540
.mandos_client_exited=&mandos_client_exited,
4541
.password_is_read=&password_is_read,
4544
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4545
EPOLLIN | EPOLLRDHUP));
4547
__attribute__((cleanup(cleanup_string)))
4548
char *filename = NULL;
4549
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4550
dummy_file_name), >, 0);
4551
g_assert_nonnull(filename);
4552
g_assert_true(string_set_contains(*task.cancelled_filenames,
4557
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4558
test_fixture *fixture,
4559
__attribute__((unused))
4562
__attribute__((cleanup(cleanup_close)))
4563
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4564
g_assert_cmpint(epoll_fd, >=, 0);
4565
const mono_microsecs current_time = 0;
4568
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4570
/* "sufficient to read at least one event." - inotify(7) */
4571
const size_t ievent_max_size = (sizeof(struct inotify_event)
4573
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4575
struct inotify_event event;
4576
char name_buffer[NAME_MAX + 1];
4578
struct inotify_event *const ievent = &ievent_buffer.event;
4580
const char dummy_file_name[] = "ignored.dummy_file_name";
4581
ievent->mask = IN_CLOSE_WRITE;
4582
ievent->len = sizeof(dummy_file_name);
4583
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4584
const size_t ievent_size = (sizeof(struct inotify_event)
4585
+ sizeof(dummy_file_name));
4586
#if defined(__GNUC__) and __GNUC__ >= 11
4587
#pragma GCC diagnostic push
4588
/* ievent is pointing into a struct which is of sufficient size */
4589
#pragma GCC diagnostic ignored "-Wstringop-overread"
4591
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4593
#if defined(__GNUC__) and __GNUC__ >= 11
4594
#pragma GCC diagnostic pop
4596
g_assert_cmpint(close(pipefds[1]), ==, 0);
4598
bool quit_now = false;
4599
buffer password = {};
4600
bool mandos_client_exited = false;
4601
bool password_is_read = false;
4602
__attribute__((cleanup(cleanup_queue)))
4603
task_queue *queue = create_queue();
4604
g_assert_nonnull(queue);
4606
task_context task = {
4607
.func=read_inotify_event,
4610
.quit_now=&quit_now,
4611
.password=&password,
4612
.filename=strdup("/nonexistent"),
4613
.cancelled_filenames = &(string_set){},
4615
.current_time=¤t_time,
4616
.mandos_client_exited=&mandos_client_exited,
4617
.password_is_read=&password_is_read,
4619
task.func(task, queue);
4620
g_assert_false(quit_now);
4621
g_assert_true(queue->next_run == 0);
4622
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4624
g_assert_nonnull(find_matching_task(queue, (task_context){
4625
.func=read_inotify_event,
4628
.quit_now=&quit_now,
4629
.password=&password,
4630
.filename=task.filename,
4631
.cancelled_filenames=task.cancelled_filenames,
4632
.current_time=¤t_time,
4633
.mandos_client_exited=&mandos_client_exited,
4634
.password_is_read=&password_is_read,
4637
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4638
EPOLLIN | EPOLLRDHUP));
4642
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4643
test_fixture *fixture,
4644
__attribute__((unused))
4645
gconstpointer user_data){
4646
__attribute__((cleanup(cleanup_close)))
4647
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4648
g_assert_cmpint(epoll_fd, >=, 0);
4649
const mono_microsecs current_time = 0;
4652
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4654
/* "sufficient to read at least one event." - inotify(7) */
4655
const size_t ievent_max_size = (sizeof(struct inotify_event)
4657
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4659
struct inotify_event event;
4660
char name_buffer[NAME_MAX + 1];
4662
struct inotify_event *const ievent = &ievent_buffer.event;
4664
const char dummy_file_name[] = "ignored.dummy_file_name";
4665
ievent->mask = IN_MOVED_TO;
4666
ievent->len = sizeof(dummy_file_name);
4667
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4668
const size_t ievent_size = (sizeof(struct inotify_event)
4669
+ sizeof(dummy_file_name));
4670
#if defined(__GNUC__) and __GNUC__ >= 11
4671
#pragma GCC diagnostic push
4672
/* ievent is pointing into a struct which is of sufficient size */
4673
#pragma GCC diagnostic ignored "-Wstringop-overread"
4675
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4677
#if defined(__GNUC__) and __GNUC__ >= 11
4678
#pragma GCC diagnostic pop
4680
g_assert_cmpint(close(pipefds[1]), ==, 0);
4682
bool quit_now = false;
4683
buffer password = {};
4684
bool mandos_client_exited = false;
4685
bool password_is_read = false;
4686
__attribute__((cleanup(cleanup_queue)))
4687
task_queue *queue = create_queue();
4688
g_assert_nonnull(queue);
4690
task_context task = {
4691
.func=read_inotify_event,
4694
.quit_now=&quit_now,
4695
.password=&password,
4696
.filename=strdup("/nonexistent"),
4697
.cancelled_filenames = &(string_set){},
4699
.current_time=¤t_time,
4700
.mandos_client_exited=&mandos_client_exited,
4701
.password_is_read=&password_is_read,
4703
task.func(task, queue);
4704
g_assert_false(quit_now);
4705
g_assert_true(queue->next_run == 0);
4706
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4708
g_assert_nonnull(find_matching_task(queue, (task_context){
4709
.func=read_inotify_event,
4712
.quit_now=&quit_now,
4713
.password=&password,
4714
.filename=task.filename,
4715
.cancelled_filenames=task.cancelled_filenames,
4716
.current_time=¤t_time,
4717
.mandos_client_exited=&mandos_client_exited,
4718
.password_is_read=&password_is_read,
4721
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4722
EPOLLIN | EPOLLRDHUP));
4726
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4727
test_fixture *fixture,
4728
__attribute__((unused))
4731
__attribute__((cleanup(cleanup_close)))
4732
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4733
g_assert_cmpint(epoll_fd, >=, 0);
4734
__attribute__((cleanup(string_set_clear)))
4735
string_set cancelled_filenames = {};
4736
const mono_microsecs current_time = 0;
4739
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4741
/* "sufficient to read at least one event." - inotify(7) */
4742
const size_t ievent_max_size = (sizeof(struct inotify_event)
4744
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4746
struct inotify_event event;
4747
char name_buffer[NAME_MAX + 1];
4749
struct inotify_event *const ievent = &ievent_buffer.event;
4751
const char dummy_file_name[] = "ignored.dummy_file_name";
4752
ievent->mask = IN_MOVED_FROM;
4753
ievent->len = sizeof(dummy_file_name);
4754
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4755
const size_t ievent_size = (sizeof(struct inotify_event)
4756
+ sizeof(dummy_file_name));
4757
#if defined(__GNUC__) and __GNUC__ >= 11
4758
#pragma GCC diagnostic push
4759
/* ievent is pointing into a struct which is of sufficient size */
4760
#pragma GCC diagnostic ignored "-Wstringop-overread"
4762
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4764
#if defined(__GNUC__) and __GNUC__ >= 11
4765
#pragma GCC diagnostic pop
4767
g_assert_cmpint(close(pipefds[1]), ==, 0);
4769
bool quit_now = false;
4770
buffer password = {};
4771
bool mandos_client_exited = false;
4772
bool password_is_read = false;
4773
__attribute__((cleanup(cleanup_queue)))
4774
task_queue *queue = create_queue();
4775
g_assert_nonnull(queue);
4777
task_context task = {
4778
.func=read_inotify_event,
4781
.quit_now=&quit_now,
4782
.password=&password,
4783
.filename=strdup("/nonexistent"),
4784
.cancelled_filenames=&cancelled_filenames,
4785
.current_time=¤t_time,
4786
.mandos_client_exited=&mandos_client_exited,
4787
.password_is_read=&password_is_read,
4789
task.func(task, queue);
4790
g_assert_false(quit_now);
4791
g_assert_true(queue->next_run == 0);
4792
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4794
g_assert_nonnull(find_matching_task(queue, (task_context){
4795
.func=read_inotify_event,
4798
.quit_now=&quit_now,
4799
.password=&password,
4800
.filename=task.filename,
4801
.cancelled_filenames=&cancelled_filenames,
4802
.current_time=¤t_time,
4803
.mandos_client_exited=&mandos_client_exited,
4804
.password_is_read=&password_is_read,
4807
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4808
EPOLLIN | EPOLLRDHUP));
4810
__attribute__((cleanup(cleanup_string)))
4811
char *filename = NULL;
4812
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4813
dummy_file_name), >, 0);
4814
g_assert_nonnull(filename);
4815
g_assert_false(string_set_contains(cancelled_filenames, filename));
4819
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4820
test_fixture *fixture,
4821
__attribute__((unused))
4824
__attribute__((cleanup(cleanup_close)))
4825
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4826
g_assert_cmpint(epoll_fd, >=, 0);
4827
__attribute__((cleanup(string_set_clear)))
4828
string_set cancelled_filenames = {};
4829
const mono_microsecs current_time = 0;
4832
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4834
/* "sufficient to read at least one event." - inotify(7) */
4835
const size_t ievent_max_size = (sizeof(struct inotify_event)
4837
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4839
struct inotify_event event;
4840
char name_buffer[NAME_MAX + 1];
4842
struct inotify_event *const ievent = &ievent_buffer.event;
4844
const char dummy_file_name[] = "ignored.dummy_file_name";
4845
ievent->mask = IN_DELETE;
4846
ievent->len = sizeof(dummy_file_name);
4847
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4848
const size_t ievent_size = (sizeof(struct inotify_event)
4849
+ sizeof(dummy_file_name));
4850
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4852
g_assert_cmpint(close(pipefds[1]), ==, 0);
4854
bool quit_now = false;
4855
buffer password = {};
4856
bool mandos_client_exited = false;
4857
bool password_is_read = false;
4858
__attribute__((cleanup(cleanup_queue)))
4859
task_queue *queue = create_queue();
4860
g_assert_nonnull(queue);
4862
task_context task = {
4863
.func=read_inotify_event,
4866
.quit_now=&quit_now,
4867
.password=&password,
4868
.filename=strdup("/nonexistent"),
4869
.cancelled_filenames=&cancelled_filenames,
4870
.current_time=¤t_time,
4871
.mandos_client_exited=&mandos_client_exited,
4872
.password_is_read=&password_is_read,
4874
task.func(task, queue);
4875
g_assert_false(quit_now);
4876
g_assert_true(queue->next_run == 0);
4877
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4879
g_assert_nonnull(find_matching_task(queue, (task_context){
4880
.func=read_inotify_event,
4883
.quit_now=&quit_now,
4884
.password=&password,
4885
.filename=task.filename,
4886
.cancelled_filenames=&cancelled_filenames,
4887
.current_time=¤t_time,
4888
.mandos_client_exited=&mandos_client_exited,
4889
.password_is_read=&password_is_read,
4892
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4893
EPOLLIN | EPOLLRDHUP));
4895
__attribute__((cleanup(cleanup_string)))
4896
char *filename = NULL;
4897
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4898
dummy_file_name), >, 0);
4899
g_assert_nonnull(filename);
4900
g_assert_false(string_set_contains(cancelled_filenames, filename));
4904
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4905
test_fixture *fixture,
4906
__attribute__((unused))
4907
gconstpointer user_data){
4908
__attribute__((cleanup(cleanup_close)))
4909
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4910
g_assert_cmpint(epoll_fd, >=, 0);
4911
__attribute__((cleanup(string_set_clear)))
4912
string_set cancelled_filenames = {};
4913
bool mandos_client_exited = false;
4914
bool password_is_read = false;
4915
__attribute__((cleanup(cleanup_queue)))
4916
task_queue *queue = create_queue();
4917
g_assert_nonnull(queue);
4919
char *const filename = strdup("/nonexistent");
4920
g_assert_nonnull(filename);
4921
task_context task = {
4922
.func=open_and_parse_question,
4923
.question_filename=filename,
4925
.password=(buffer[]){{}},
4927
.cancelled_filenames=&cancelled_filenames,
4928
.current_time=(mono_microsecs[]){0},
4929
.mandos_client_exited=&mandos_client_exited,
4930
.password_is_read=&password_is_read,
4932
task.func(task, queue);
4933
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4936
static void test_open_and_parse_question_EIO(__attribute__((unused))
4937
test_fixture *fixture,
4938
__attribute__((unused))
4939
gconstpointer user_data){
4940
__attribute__((cleanup(cleanup_close)))
4941
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4942
g_assert_cmpint(epoll_fd, >=, 0);
4943
__attribute__((cleanup(string_set_clear)))
4944
string_set cancelled_filenames = {};
4945
buffer password = {};
4946
bool mandos_client_exited = false;
4947
bool password_is_read = false;
4948
__attribute__((cleanup(cleanup_queue)))
4949
task_queue *queue = create_queue();
4950
g_assert_nonnull(queue);
4951
const mono_microsecs current_time = 0;
4953
char *filename = strdup("/proc/self/mem");
4954
task_context task = {
4955
.func=open_and_parse_question,
4956
.question_filename=filename,
4958
.password=&password,
4960
.cancelled_filenames=&cancelled_filenames,
4961
.current_time=¤t_time,
4962
.mandos_client_exited=&mandos_client_exited,
4963
.password_is_read=&password_is_read,
4965
run_task_with_stderr_to_dev_null(task, queue);
4966
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4970
test_open_and_parse_question_parse_error(__attribute__((unused))
4971
test_fixture *fixture,
4972
__attribute__((unused))
4973
gconstpointer user_data){
4974
__attribute__((cleanup(cleanup_close)))
4975
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4976
g_assert_cmpint(epoll_fd, >=, 0);
4977
__attribute__((cleanup(string_set_clear)))
4978
string_set cancelled_filenames = {};
4979
__attribute__((cleanup(cleanup_queue)))
4980
task_queue *queue = create_queue();
4981
g_assert_nonnull(queue);
4983
__attribute__((cleanup(cleanup_string)))
4984
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4985
g_assert_nonnull(tempfilename);
4986
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4987
g_assert_cmpint(tempfile, >, 0);
4988
const char bad_data[] = "this is bad syntax\n";
4989
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4990
==, sizeof(bad_data));
4991
g_assert_cmpint(close(tempfile), ==, 0);
4993
char *const filename = strdup(tempfilename);
4994
g_assert_nonnull(filename);
4995
task_context task = {
4996
.func=open_and_parse_question,
4997
.question_filename=filename,
4999
.password=(buffer[]){{}},
5001
.cancelled_filenames=&cancelled_filenames,
5002
.current_time=(mono_microsecs[]){0},
5003
.mandos_client_exited=(bool[]){false},
5004
.password_is_read=(bool[]){false},
5006
run_task_with_stderr_to_dev_null(task, queue);
5008
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5010
g_assert_cmpint(unlink(tempfilename), ==, 0);
5014
void test_open_and_parse_question_nosocket(__attribute__((unused))
5015
test_fixture *fixture,
5016
__attribute__((unused))
5017
gconstpointer user_data){
5018
__attribute__((cleanup(cleanup_close)))
5019
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5020
g_assert_cmpint(epoll_fd, >=, 0);
5021
__attribute__((cleanup(string_set_clear)))
5022
string_set cancelled_filenames = {};
5023
__attribute__((cleanup(cleanup_queue)))
5024
task_queue *queue = create_queue();
5025
g_assert_nonnull(queue);
5027
__attribute__((cleanup(cleanup_string)))
5028
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5029
g_assert_nonnull(tempfilename);
5030
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5031
g_assert_cmpint(questionfile, >, 0);
5032
FILE *qf = fdopen(questionfile, "w");
5033
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
5034
g_assert_cmpint(fclose(qf), ==, 0);
5036
char *const filename = strdup(tempfilename);
5037
g_assert_nonnull(filename);
5038
task_context task = {
5039
.func=open_and_parse_question,
5040
.question_filename=filename,
5042
.password=(buffer[]){{}},
5044
.cancelled_filenames=&cancelled_filenames,
5045
.current_time=(mono_microsecs[]){0},
5046
.mandos_client_exited=(bool[]){false},
5047
.password_is_read=(bool[]){false},
5049
run_task_with_stderr_to_dev_null(task, queue);
5050
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5052
g_assert_cmpint(unlink(tempfilename), ==, 0);
5056
void test_open_and_parse_question_badsocket(__attribute__((unused))
5057
test_fixture *fixture,
5058
__attribute__((unused))
5059
gconstpointer user_data){
5060
__attribute__((cleanup(cleanup_close)))
5061
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5062
g_assert_cmpint(epoll_fd, >=, 0);
5063
__attribute__((cleanup(string_set_clear)))
5064
string_set cancelled_filenames = {};
5065
__attribute__((cleanup(cleanup_queue)))
5066
task_queue *queue = create_queue();
5067
g_assert_nonnull(queue);
5069
__attribute__((cleanup(cleanup_string)))
5070
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5071
g_assert_nonnull(tempfilename);
5072
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5073
g_assert_cmpint(questionfile, >, 0);
5074
FILE *qf = fdopen(questionfile, "w");
5075
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
5076
g_assert_cmpint(fclose(qf), ==, 0);
5078
char *const filename = strdup(tempfilename);
5079
g_assert_nonnull(filename);
5080
task_context task = {
5081
.func=open_and_parse_question,
5082
.question_filename=filename,
5084
.password=(buffer[]){{}},
5086
.cancelled_filenames=&cancelled_filenames,
5087
.current_time=(mono_microsecs[]){0},
5088
.mandos_client_exited=(bool[]){false},
5089
.password_is_read=(bool[]){false},
5091
run_task_with_stderr_to_dev_null(task, queue);
5092
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5094
g_assert_cmpint(unlink(tempfilename), ==, 0);
5098
void test_open_and_parse_question_nopid(__attribute__((unused))
5099
test_fixture *fixture,
5100
__attribute__((unused))
5101
gconstpointer user_data){
5102
__attribute__((cleanup(cleanup_close)))
5103
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5104
g_assert_cmpint(epoll_fd, >=, 0);
5105
__attribute__((cleanup(string_set_clear)))
5106
string_set cancelled_filenames = {};
5107
__attribute__((cleanup(cleanup_queue)))
5108
task_queue *queue = create_queue();
5109
g_assert_nonnull(queue);
5111
__attribute__((cleanup(cleanup_string)))
5112
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5113
g_assert_nonnull(tempfilename);
5114
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5115
g_assert_cmpint(questionfile, >, 0);
5116
FILE *qf = fdopen(questionfile, "w");
5117
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5118
g_assert_cmpint(fclose(qf), ==, 0);
5120
char *const filename = strdup(tempfilename);
5121
g_assert_nonnull(filename);
5122
task_context task = {
5123
.func=open_and_parse_question,
5124
.question_filename=filename,
5126
.password=(buffer[]){{}},
5128
.cancelled_filenames=&cancelled_filenames,
5129
.current_time=(mono_microsecs[]){0},
5130
.mandos_client_exited=(bool[]){false},
5131
.password_is_read=(bool[]){false},
5133
run_task_with_stderr_to_dev_null(task, queue);
5134
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5136
g_assert_cmpint(unlink(tempfilename), ==, 0);
5140
void test_open_and_parse_question_badpid(__attribute__((unused))
5141
test_fixture *fixture,
5142
__attribute__((unused))
5143
gconstpointer user_data){
5144
__attribute__((cleanup(cleanup_close)))
5145
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5146
g_assert_cmpint(epoll_fd, >=, 0);
5147
__attribute__((cleanup(string_set_clear)))
5148
string_set cancelled_filenames = {};
5149
__attribute__((cleanup(cleanup_queue)))
5150
task_queue *queue = create_queue();
5151
g_assert_nonnull(queue);
5153
__attribute__((cleanup(cleanup_string)))
5154
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5155
g_assert_nonnull(tempfilename);
5156
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5157
g_assert_cmpint(questionfile, >, 0);
5158
FILE *qf = fdopen(questionfile, "w");
5159
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5161
g_assert_cmpint(fclose(qf), ==, 0);
5163
char *const filename = strdup(tempfilename);
5164
g_assert_nonnull(filename);
5165
task_context task = {
5166
.func=open_and_parse_question,
5167
.question_filename=filename,
5169
.password=(buffer[]){{}},
5171
.cancelled_filenames=&cancelled_filenames,
5172
.current_time=(mono_microsecs[]){0},
5173
.mandos_client_exited=(bool[]){false},
5174
.password_is_read=(bool[]){false},
5176
run_task_with_stderr_to_dev_null(task, queue);
5177
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5179
g_assert_cmpint(unlink(tempfilename), ==, 0);
5183
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5184
test_fixture *fixture,
5185
__attribute__((unused))
5186
gconstpointer user_data){
5187
__attribute__((cleanup(cleanup_close)))
5188
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5189
g_assert_cmpint(epoll_fd, >=, 0);
5190
__attribute__((cleanup(string_set_clear)))
5191
string_set cancelled_filenames = {};
5192
buffer password = {};
5193
bool mandos_client_exited = false;
5194
bool password_is_read = false;
5195
__attribute__((cleanup(cleanup_queue)))
5196
task_queue *queue = create_queue();
5197
g_assert_nonnull(queue);
5198
const mono_microsecs current_time = 0;
5200
/* Find value of sysctl kernel.pid_max */
5201
uintmax_t pid_max = 0;
5202
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5203
g_assert_nonnull(sysctl_pid_max);
5204
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5206
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5208
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5209
g_assert_true(nonexisting_pid > 0); /* Overflow check */
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"\n", (uintmax_t)nonexisting_pid),
5220
g_assert_cmpint(fclose(qf), ==, 0);
5222
char *const question_filename = strdup(tempfilename);
5223
g_assert_nonnull(question_filename);
5224
task_context task = {
5225
.func=open_and_parse_question,
5226
.question_filename=question_filename,
5228
.password=&password,
5229
.filename=question_filename,
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, ==, 0);
5238
g_assert_cmpint(unlink(tempfilename), ==, 0);
5242
test_open_and_parse_question_no_notafter(__attribute__((unused))
5243
test_fixture *fixture,
5244
__attribute__((unused))
5245
gconstpointer user_data){
5246
__attribute__((cleanup(cleanup_close)))
5247
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5248
g_assert_cmpint(epoll_fd, >=, 0);
5249
__attribute__((cleanup(string_set_clear)))
5250
string_set cancelled_filenames = {};
5251
buffer password = {};
5252
bool mandos_client_exited = false;
5253
bool password_is_read = false;
5254
__attribute__((cleanup(cleanup_queue)))
5255
task_queue *queue = create_queue();
5256
g_assert_nonnull(queue);
5257
const mono_microsecs current_time = 0;
5259
__attribute__((cleanup(cleanup_string)))
5260
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5261
g_assert_nonnull(tempfilename);
5262
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5263
g_assert_cmpint(questionfile, >, 0);
5264
FILE *qf = fdopen(questionfile, "w");
5265
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5266
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5267
g_assert_cmpint(fclose(qf), ==, 0);
5269
char *const filename = strdup(tempfilename);
5270
g_assert_nonnull(filename);
5271
task_context task = {
5272
.func=open_and_parse_question,
5273
.question_filename=filename,
5275
.password=&password,
5277
.cancelled_filenames=&cancelled_filenames,
5278
.current_time=¤t_time,
5279
.mandos_client_exited=&mandos_client_exited,
5280
.password_is_read=&password_is_read,
5282
task.func(task, queue);
5283
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5285
__attribute__((cleanup(cleanup_string)))
5286
char *socket_filename = strdup("/nonexistent");
5287
g_assert_nonnull(socket_filename);
5288
g_assert_nonnull(find_matching_task(queue, (task_context){
5289
.func=connect_question_socket,
5290
.question_filename=tempfilename,
5291
.filename=socket_filename,
5293
.password=&password,
5294
.current_time=¤t_time,
5295
.mandos_client_exited=&mandos_client_exited,
5296
.password_is_read=&password_is_read,
5299
g_assert_true(queue->next_run != 0);
5301
g_assert_cmpint(unlink(tempfilename), ==, 0);
5305
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5306
test_fixture *fixture,
5307
__attribute__((unused))
5308
gconstpointer user_data){
5309
__attribute__((cleanup(cleanup_close)))
5310
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5311
g_assert_cmpint(epoll_fd, >=, 0);
5312
__attribute__((cleanup(string_set_clear)))
5313
string_set cancelled_filenames = {};
5314
buffer password = {};
5315
bool mandos_client_exited = false;
5316
bool password_is_read = false;
5317
__attribute__((cleanup(cleanup_queue)))
5318
task_queue *queue = create_queue();
5319
g_assert_nonnull(queue);
5320
const mono_microsecs current_time = 0;
5322
__attribute__((cleanup(cleanup_string)))
5323
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5324
g_assert_nonnull(tempfilename);
5325
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5326
g_assert_cmpint(questionfile, >, 0);
5327
FILE *qf = fdopen(questionfile, "w");
5328
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5329
PRIuMAX "\nNotAfter=\n",
5330
(uintmax_t)getpid()), >, 0);
5331
g_assert_cmpint(fclose(qf), ==, 0);
5333
char *const filename = strdup(tempfilename);
5334
g_assert_nonnull(filename);
5335
task_context task = {
5336
.func=open_and_parse_question,
5337
.question_filename=filename,
5339
.password=&password,
5341
.cancelled_filenames=&cancelled_filenames,
5342
.current_time=¤t_time,
5343
.mandos_client_exited=&mandos_client_exited,
5344
.password_is_read=&password_is_read,
5346
run_task_with_stderr_to_dev_null(task, queue);
5347
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5349
__attribute__((cleanup(cleanup_string)))
5350
char *socket_filename = strdup("/nonexistent");
5351
g_assert_nonnull(find_matching_task(queue, (task_context){
5352
.func=connect_question_socket,
5353
.question_filename=tempfilename,
5354
.filename=socket_filename,
5356
.password=&password,
5357
.current_time=¤t_time,
5358
.mandos_client_exited=&mandos_client_exited,
5359
.password_is_read=&password_is_read,
5361
g_assert_true(queue->next_run != 0);
5363
g_assert_cmpint(unlink(tempfilename), ==, 0);
5367
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5369
const mono_microsecs
5371
const mono_microsecs
5373
__attribute__((cleanup(cleanup_close)))
5374
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5375
g_assert_cmpint(epoll_fd, >=, 0);
5376
__attribute__((cleanup(string_set_clear)))
5377
string_set cancelled_filenames = {};
5378
buffer password = {};
5379
bool mandos_client_exited = false;
5380
bool password_is_read = false;
5381
__attribute__((cleanup(cleanup_queue)))
5382
task_queue *queue = create_queue();
5383
g_assert_nonnull(queue);
5384
queue->next_run = next_queue_run;
5386
__attribute__((cleanup(cleanup_string)))
5387
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5388
g_assert_nonnull(tempfilename);
5389
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5390
g_assert_cmpint(questionfile, >, 0);
5391
FILE *qf = fdopen(questionfile, "w");
5392
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5393
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5394
(uintmax_t)getpid(), notafter), >, 0);
5395
g_assert_cmpint(fclose(qf), ==, 0);
5397
char *const filename = strdup(tempfilename);
5398
g_assert_nonnull(filename);
5399
task_context task = {
5400
.func=open_and_parse_question,
5401
.question_filename=filename,
5403
.password=&password,
5405
.cancelled_filenames=&cancelled_filenames,
5406
.current_time=¤t_time,
5407
.mandos_client_exited=&mandos_client_exited,
5408
.password_is_read=&password_is_read,
5410
task.func(task, queue);
5412
if(queue->length >= 1){
5413
__attribute__((cleanup(cleanup_string)))
5414
char *socket_filename = strdup("/nonexistent");
5415
g_assert_nonnull(find_matching_task(queue, (task_context){
5416
.func=connect_question_socket,
5417
.filename=socket_filename,
5419
.password=&password,
5420
.current_time=¤t_time,
5421
.cancelled_filenames=&cancelled_filenames,
5422
.mandos_client_exited=&mandos_client_exited,
5423
.password_is_read=&password_is_read,
5425
g_assert_true(queue->next_run != 0);
5429
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5430
} else if(current_time >= notafter) {
5431
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5433
g_assert_nonnull(find_matching_task(queue, (task_context){
5434
.func=cancel_old_question,
5435
.question_filename=tempfilename,
5436
.filename=tempfilename,
5438
.cancelled_filenames=&cancelled_filenames,
5439
.current_time=¤t_time,
5442
g_assert_true(queue->next_run == 1);
5444
g_assert_cmpint(unlink(tempfilename), ==, 0);
5448
test_open_and_parse_question_notafter_0(__attribute__((unused))
5449
test_fixture *fixture,
5450
__attribute__((unused))
5451
gconstpointer user_data){
5452
/* current_time, notafter, next_queue_run */
5453
assert_open_and_parse_question_with_notafter(0, 0, 0);
5457
test_open_and_parse_question_notafter_1(__attribute__((unused))
5458
test_fixture *fixture,
5459
__attribute__((unused))
5460
gconstpointer user_data){
5461
/* current_time, notafter, next_queue_run */
5462
assert_open_and_parse_question_with_notafter(0, 1, 0);
5466
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5467
test_fixture *fixture,
5468
__attribute__((unused))
5469
gconstpointer user_data){
5470
/* current_time, notafter, next_queue_run */
5471
assert_open_and_parse_question_with_notafter(0, 1, 1);
5475
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5476
test_fixture *fixture,
5477
__attribute__((unused))
5478
gconstpointer user_data){
5479
/* current_time, notafter, next_queue_run */
5480
assert_open_and_parse_question_with_notafter(0, 1, 2);
5484
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5485
test_fixture *fixture,
5486
__attribute__((unused))
5487
gconstpointer user_data){
5488
/* current_time, notafter, next_queue_run */
5489
assert_open_and_parse_question_with_notafter(1, 1, 0);
5493
test_open_and_parse_question_late_notafter(__attribute__((unused))
5494
test_fixture *fixture,
5495
__attribute__((unused))
5496
gconstpointer user_data){
5497
/* current_time, notafter, next_queue_run */
5498
assert_open_and_parse_question_with_notafter(2, 1, 0);
5501
static void assert_cancel_old_question_param(const mono_microsecs
5503
const mono_microsecs
5505
const mono_microsecs
5507
const mono_microsecs
5509
__attribute__((cleanup(string_set_clear)))
5510
string_set cancelled_filenames = {};
5511
__attribute__((cleanup(cleanup_queue)))
5512
task_queue *queue = create_queue();
5513
g_assert_nonnull(queue);
5514
queue->next_run = next_queue_run;
5516
char *const question_filename = strdup("/nonexistent");
5517
g_assert_nonnull(question_filename);
5518
task_context task = {
5519
.func=cancel_old_question,
5520
.question_filename=question_filename,
5521
.filename=question_filename,
5523
.cancelled_filenames=&cancelled_filenames,
5524
.current_time=¤t_time,
5526
task.func(task, queue);
5528
if(current_time >= notafter){
5529
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5530
g_assert_true(string_set_contains(cancelled_filenames,
5533
g_assert_nonnull(find_matching_task(queue, (task_context){
5534
.func=cancel_old_question,
5535
.question_filename=question_filename,
5536
.filename=question_filename,
5538
.cancelled_filenames=&cancelled_filenames,
5539
.current_time=¤t_time,
5542
g_assert_false(string_set_contains(cancelled_filenames,
5543
question_filename));
5545
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5546
(unsigned int)next_set_to);
5549
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5550
test_fixture *fixture,
5551
__attribute__((unused))
5552
gconstpointer user_data){
5553
/* next_queue_run unset,
5554
cancellation should happen because time has come,
5555
next_queue_run should be unchanged */
5556
/* next_queue_run, notafter, current_time, next_set_to */
5557
assert_cancel_old_question_param(0, 1, 2, 0);
5560
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5561
test_fixture *fixture,
5562
__attribute__((unused))
5563
gconstpointer user_data){
5564
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5565
and current_time is not yet notafter or greater,
5566
update value of next_queue_run to value of notafter */
5567
/* next_queue_run, notafter, current_time, next_set_to */
5568
assert_cancel_old_question_param(0, 2, 1, 2);
5571
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5572
test_fixture *fixture,
5573
__attribute__((unused))
5574
gconstpointer user_data){
5575
/* next_queue_run 1,
5576
cancellation should happen because time has come,
5577
next_queue_run should be unchanged */
5578
/* next_queue_run, notafter, current_time, next_set_to */
5579
assert_cancel_old_question_param(1, 2, 3, 1);
5582
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5583
test_fixture *fixture,
5584
__attribute__((unused))
5585
gconstpointer user_data){
5586
/* If next_queue_run is set,
5587
and current_time is not yet notafter or greater,
5588
and notafter is larger than next_queue_run
5589
next_queue_run should be unchanged */
5590
/* next_queue_run, notafter, current_time, next_set_to */
5591
assert_cancel_old_question_param(1, 3, 2, 1);
5594
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5595
test_fixture *fixture,
5596
__attribute__((unused))
5597
gconstpointer user_data){
5598
/* next_queue_run 2,
5599
cancellation should happen because time has come,
5600
next_queue_run should be unchanged */
5601
/* next_queue_run, notafter, current_time, next_set_to */
5602
assert_cancel_old_question_param(2, 1, 3, 2);
5605
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5606
test_fixture *fixture,
5607
__attribute__((unused))
5608
gconstpointer user_data){
5609
/* If next_queue_run is set,
5610
and current_time is not yet notafter or greater,
5611
and notafter is larger than next_queue_run
5612
next_queue_run should be unchanged */
5613
/* next_queue_run, notafter, current_time, next_set_to */
5614
assert_cancel_old_question_param(2, 3, 1, 2);
5617
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5618
test_fixture *fixture,
5619
__attribute__((unused))
5620
gconstpointer user_data){
5621
/* next_queue_run 3,
5622
cancellation should happen because time has come,
5623
next_queue_run should be unchanged */
5624
/* next_queue_run, notafter, current_time, next_set_to */
5625
assert_cancel_old_question_param(3, 1, 2, 3);
5628
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5629
test_fixture *fixture,
5630
__attribute__((unused))
5631
gconstpointer user_data){
5632
/* If next_queue_run is set,
5633
and current_time is not yet notafter or greater,
5634
and notafter is smaller than next_queue_run
5635
update value of next_queue_run to value of notafter */
5636
/* next_queue_run, notafter, current_time, next_set_to */
5637
assert_cancel_old_question_param(3, 2, 1, 2);
5641
test_connect_question_socket_name_too_long(__attribute__((unused))
5642
test_fixture *fixture,
5643
__attribute__((unused))
5644
gconstpointer user_data){
5645
__attribute__((cleanup(cleanup_close)))
5646
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5647
g_assert_cmpint(epoll_fd, >=, 0);
5648
const char question_filename[] = "/nonexistent/question";
5649
__attribute__((cleanup(string_set_clear)))
5650
string_set cancelled_filenames = {};
5651
__attribute__((cleanup(cleanup_queue)))
5652
task_queue *queue = create_queue();
5653
g_assert_nonnull(queue);
5654
__attribute__((cleanup(cleanup_string)))
5655
char *tempdir = make_temporary_directory();
5656
g_assert_nonnull(tempdir);
5657
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5658
char socket_name[sizeof(unix_socket.sun_path)];
5659
memset(socket_name, 'x', sizeof(socket_name));
5660
socket_name[sizeof(socket_name)-1] = '\0';
5661
char *filename = NULL;
5662
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5664
g_assert_nonnull(filename);
5666
task_context task = {
5667
.func=connect_question_socket,
5668
.question_filename=strdup(question_filename),
5670
.password=(buffer[]){{}},
5672
.cancelled_filenames=&cancelled_filenames,
5673
.mandos_client_exited=(bool[]){false},
5674
.password_is_read=(bool[]){false},
5675
.current_time=(mono_microsecs[]){0},
5677
g_assert_nonnull(task.question_filename);
5678
run_task_with_stderr_to_dev_null(task, queue);
5680
g_assert_true(string_set_contains(cancelled_filenames,
5681
question_filename));
5682
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5683
g_assert_true(queue->next_run == 0);
5685
g_assert_cmpint(rmdir(tempdir), ==, 0);
5689
void test_connect_question_socket_connect_fail(__attribute__((unused))
5690
test_fixture *fixture,
5691
__attribute__((unused))
5694
__attribute__((cleanup(cleanup_close)))
5695
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5696
g_assert_cmpint(epoll_fd, >=, 0);
5697
const char question_filename[] = "/nonexistent/question";
5698
__attribute__((cleanup(string_set_clear)))
5699
string_set cancelled_filenames = {};
5700
const mono_microsecs current_time = 3;
5701
__attribute__((cleanup(cleanup_queue)))
5702
task_queue *queue = create_queue();
5703
g_assert_nonnull(queue);
5704
__attribute__((cleanup(cleanup_string)))
5705
char *tempdir = make_temporary_directory();
5706
g_assert_nonnull(tempdir);
5707
char socket_name[] = "nonexistent";
5708
char *filename = NULL;
5709
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5711
g_assert_nonnull(filename);
5713
task_context task = {
5714
.func=connect_question_socket,
5715
.question_filename=strdup(question_filename),
5717
.password=(buffer[]){{}},
5719
.cancelled_filenames=&cancelled_filenames,
5720
.mandos_client_exited=(bool[]){false},
5721
.password_is_read=(bool[]){false},
5722
.current_time=¤t_time,
5724
g_assert_nonnull(task.question_filename);
5725
run_task_with_stderr_to_dev_null(task, queue);
5727
g_assert_nonnull(find_matching_task(queue, task));
5729
g_assert_false(string_set_contains(cancelled_filenames,
5730
question_filename));
5731
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5732
g_assert_true(queue->next_run == 1000000 + current_time);
5734
g_assert_cmpint(rmdir(tempdir), ==, 0);
5738
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5739
test_fixture *fixture,
5740
__attribute__((unused))
5741
gconstpointer user_data){
5742
__attribute__((cleanup(cleanup_close)))
5743
const int epoll_fd = open("/dev/null",
5744
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5745
__attribute__((cleanup(cleanup_string)))
5746
char *const question_filename = strdup("/nonexistent/question");
5747
g_assert_nonnull(question_filename);
5748
__attribute__((cleanup(string_set_clear)))
5749
string_set cancelled_filenames = {};
5750
const mono_microsecs current_time = 5;
5751
__attribute__((cleanup(cleanup_queue)))
5752
task_queue *queue = create_queue();
5753
g_assert_nonnull(queue);
5754
__attribute__((cleanup(cleanup_string)))
5755
char *tempdir = make_temporary_directory();
5756
g_assert_nonnull(tempdir);
5757
__attribute__((cleanup(cleanup_close)))
5758
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5759
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5760
g_assert_cmpint(sock_fd, >=, 0);
5761
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5762
const char socket_name[] = "socket_name";
5763
__attribute__((cleanup(cleanup_string)))
5764
char *filename = NULL;
5765
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5767
g_assert_nonnull(filename);
5768
g_assert_cmpint((int)strlen(filename), <,
5769
(int)sizeof(sock_name.sun_path));
5770
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5771
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5772
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5773
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5774
task_context task = {
5775
.func=connect_question_socket,
5776
.question_filename=strdup(question_filename),
5778
.password=(buffer[]){{}},
5779
.filename=strdup(filename),
5780
.cancelled_filenames=&cancelled_filenames,
5781
.mandos_client_exited=(bool[]){false},
5782
.password_is_read=(bool[]){false},
5783
.current_time=¤t_time,
5785
g_assert_nonnull(task.question_filename);
5786
run_task_with_stderr_to_dev_null(task, queue);
5788
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5789
const task_context *const added_task
5790
= find_matching_task(queue, task);
5791
g_assert_nonnull(added_task);
5792
g_assert_true(queue->next_run == 1000000 + current_time);
5794
g_assert_cmpint(unlink(filename), ==, 0);
5795
g_assert_cmpint(rmdir(tempdir), ==, 0);
5799
void test_connect_question_socket_usable(__attribute__((unused))
5800
test_fixture *fixture,
5801
__attribute__((unused))
5802
gconstpointer user_data){
5803
__attribute__((cleanup(cleanup_close)))
5804
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5805
g_assert_cmpint(epoll_fd, >=, 0);
5806
__attribute__((cleanup(cleanup_string)))
5807
char *const question_filename = strdup("/nonexistent/question");
5808
g_assert_nonnull(question_filename);
5809
__attribute__((cleanup(string_set_clear)))
5810
string_set cancelled_filenames = {};
5811
buffer password = {};
5812
bool mandos_client_exited = false;
5813
bool password_is_read = false;
5814
const mono_microsecs current_time = 0;
5815
__attribute__((cleanup(cleanup_queue)))
5816
task_queue *queue = create_queue();
5817
g_assert_nonnull(queue);
5818
__attribute__((cleanup(cleanup_string)))
5819
char *tempdir = make_temporary_directory();
5820
g_assert_nonnull(tempdir);
5821
__attribute__((cleanup(cleanup_close)))
5822
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5823
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5824
g_assert_cmpint(sock_fd, >=, 0);
5825
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5826
const char socket_name[] = "socket_name";
5827
__attribute__((cleanup(cleanup_string)))
5828
char *filename = NULL;
5829
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5831
g_assert_nonnull(filename);
5832
g_assert_cmpint((int)strlen(filename), <,
5833
(int)sizeof(sock_name.sun_path));
5834
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5835
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5836
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5837
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5838
task_context task = {
5839
.func=connect_question_socket,
5840
.question_filename=strdup(question_filename),
5842
.password=&password,
5843
.filename=strdup(filename),
5844
.cancelled_filenames=&cancelled_filenames,
5845
.mandos_client_exited=&mandos_client_exited,
5846
.password_is_read=&password_is_read,
5847
.current_time=¤t_time,
5849
g_assert_nonnull(task.question_filename);
5850
task.func(task, queue);
5852
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5853
const task_context *const added_task
5854
= find_matching_task(queue, (task_context){
5855
.func=send_password_to_socket,
5856
.question_filename=question_filename,
5859
.password=&password,
5860
.cancelled_filenames=&cancelled_filenames,
5861
.mandos_client_exited=&mandos_client_exited,
5862
.password_is_read=&password_is_read,
5863
.current_time=¤t_time,
5865
g_assert_nonnull(added_task);
5866
g_assert_cmpint(added_task->fd, >, 0);
5868
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5871
const int fd = added_task->fd;
5872
g_assert_cmpint(fd, >, 0);
5873
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5876
char write_data[PIPE_BUF];
5878
/* Construct test password buffer */
5879
/* Start with + since that is what the real protocol uses */
5880
write_data[0] = '+';
5881
/* Set a special character at string end just to mark the end */
5882
write_data[sizeof(write_data)-2] = 'y';
5883
/* Set NUL at buffer end, as suggested by the protocol */
5884
write_data[sizeof(write_data)-1] = '\0';
5885
/* Fill rest of password with 'x' */
5886
memset(write_data+1, 'x', sizeof(write_data)-3);
5887
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5888
MSG_NOSIGNAL), ==, sizeof(write_data));
5891
/* read from sock_fd */
5892
char read_data[sizeof(write_data)];
5893
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5894
==, sizeof(read_data));
5896
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5899
/* writing to sock_fd should fail */
5900
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5901
MSG_NOSIGNAL), <, 0);
5903
/* reading from fd should fail */
5904
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5905
MSG_NOSIGNAL), <, 0);
5907
g_assert_cmpint(unlink(filename), ==, 0);
5908
g_assert_cmpint(rmdir(tempdir), ==, 0);
5912
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5913
test_fixture *fixture,
5914
__attribute__((unused))
5917
__attribute__((cleanup(cleanup_close)))
5918
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5919
g_assert_cmpint(epoll_fd, >=, 0);
5920
__attribute__((cleanup(cleanup_string)))
5921
char *const question_filename = strdup("/nonexistent/question");
5922
g_assert_nonnull(question_filename);
5923
__attribute__((cleanup(cleanup_string)))
5924
char *const filename = strdup("/nonexistent/socket");
5925
g_assert_nonnull(filename);
5926
__attribute__((cleanup(string_set_clear)))
5927
string_set cancelled_filenames = {};
5928
buffer password = {};
5929
bool password_is_read = true;
5930
__attribute__((cleanup(cleanup_queue)))
5931
task_queue *queue = create_queue();
5932
g_assert_nonnull(queue);
5934
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5935
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5937
__attribute__((cleanup(cleanup_close)))
5938
const int read_socket = socketfds[0];
5939
const int write_socket = socketfds[1];
5940
task_context task = {
5941
.func=send_password_to_socket,
5942
.question_filename=strdup(question_filename),
5943
.filename=strdup(filename),
5946
.password=&password,
5947
.cancelled_filenames=&cancelled_filenames,
5948
.mandos_client_exited=(bool[]){false},
5949
.password_is_read=&password_is_read,
5950
.current_time=(mono_microsecs[]){0},
5952
g_assert_nonnull(task.question_filename);
5954
task.func(task, queue);
5956
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5958
const task_context *const added_task
5959
= find_matching_task(queue, task);
5960
g_assert_nonnull(added_task);
5961
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5962
g_assert_true(password_is_read);
5964
g_assert_cmpint(added_task->fd, >, 0);
5965
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5970
test_send_password_to_socket_password_not_read(__attribute__((unused))
5971
test_fixture *fixture,
5972
__attribute__((unused))
5975
__attribute__((cleanup(cleanup_close)))
5976
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5977
g_assert_cmpint(epoll_fd, >=, 0);
5978
__attribute__((cleanup(cleanup_string)))
5979
char *const question_filename = strdup("/nonexistent/question");
5980
g_assert_nonnull(question_filename);
5981
__attribute__((cleanup(cleanup_string)))
5982
char *const filename = strdup("/nonexistent/socket");
5983
__attribute__((cleanup(string_set_clear)))
5984
string_set cancelled_filenames = {};
5985
buffer password = {};
5986
__attribute__((cleanup(cleanup_queue)))
5987
task_queue *queue = create_queue();
5988
g_assert_nonnull(queue);
5990
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5991
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5993
__attribute__((cleanup(cleanup_close)))
5994
const int read_socket = socketfds[0];
5995
const int write_socket = socketfds[1];
5996
task_context task = {
5997
.func=send_password_to_socket,
5998
.question_filename=strdup(question_filename),
5999
.filename=strdup(filename),
6002
.password=&password,
6003
.cancelled_filenames=&cancelled_filenames,
6004
.mandos_client_exited=(bool[]){false},
6005
.password_is_read=(bool[]){false},
6006
.current_time=(mono_microsecs[]){0},
6008
g_assert_nonnull(task.question_filename);
6010
task.func(task, queue);
6012
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6014
const task_context *const added_task = find_matching_task(queue,
6016
g_assert_nonnull(added_task);
6017
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6018
g_assert_true(queue->next_run == 0);
6020
g_assert_cmpint(added_task->fd, >, 0);
6021
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6026
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
6027
test_fixture *fixture,
6028
__attribute__((unused))
6029
gconstpointer user_data){
6030
__attribute__((cleanup(cleanup_close)))
6031
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6032
g_assert_cmpint(epoll_fd, >=, 0);
6033
const char question_filename[] = "/nonexistent/question";
6034
char *const filename = strdup("/nonexistent/socket");
6035
__attribute__((cleanup(string_set_clear)))
6036
string_set cancelled_filenames = {};
6039
/* Find a message size which triggers EMSGSIZE */
6040
__attribute__((cleanup(cleanup_string)))
6041
char *message_buffer = NULL;
6042
size_t message_size = PIPE_BUF + 1;
6043
for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
6044
if(message_size >= 1024*1024*1024){ /* 1 GiB */
6045
g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
6048
message_buffer = realloc(message_buffer, message_size);
6049
if(message_buffer == NULL){
6050
g_test_skip("Skipping EMSGSIZE test");
6051
g_test_message("Failed to malloc() %" PRIuMAX " bytes",
6052
(uintmax_t)message_size);
6055
/* Fill buffer with 'x' */
6056
memset(message_buffer, 'x', message_size);
6057
/* Create a new socketpair for each message size to avoid having
6058
to empty the pipe by reading the message to a separate buffer
6060
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6061
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6063
ssret = send(socketfds[1], message_buffer, message_size,
6065
error_t saved_errno = errno;
6066
g_assert_cmpint(close(socketfds[0]), ==, 0);
6067
g_assert_cmpint(close(socketfds[1]), ==, 0);
6070
if(saved_errno != EMSGSIZE) {
6071
g_test_skip("Skipping EMSGSIZE test");
6072
g_test_message("Error on send(%" PRIuMAX " bytes): %s",
6073
(uintmax_t)message_size,
6074
strerror(saved_errno));
6078
} else if(ssret != (ssize_t)message_size){
6079
g_test_skip("Skipping EMSGSIZE test");
6080
g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6081
" bytes", (uintmax_t)ssret,
6082
(intmax_t)message_size);
6086
g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6087
(intmax_t)message_size);
6090
.data=message_buffer,
6091
.length=message_size - 2, /* Compensate for added '+' and NUL */
6092
.allocated=message_size,
6094
if(mlock(password.data, password.allocated) != 0){
6095
g_assert_true(errno == EPERM or errno == ENOMEM);
6098
__attribute__((cleanup(cleanup_queue)))
6099
task_queue *queue = create_queue();
6100
g_assert_nonnull(queue);
6101
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6102
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6104
__attribute__((cleanup(cleanup_close)))
6105
const int read_socket = socketfds[0];
6106
__attribute__((cleanup(cleanup_close)))
6107
const int write_socket = socketfds[1];
6108
task_context task = {
6109
.func=send_password_to_socket,
6110
.question_filename=strdup(question_filename),
6114
.password=&password,
6115
.cancelled_filenames=&cancelled_filenames,
6116
.mandos_client_exited=(bool[]){true},
6117
.password_is_read=(bool[]){true},
6118
.current_time=(mono_microsecs[]){0},
6120
g_assert_nonnull(task.question_filename);
6122
run_task_with_stderr_to_dev_null(task, queue);
6124
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6125
g_assert_true(string_set_contains(cancelled_filenames,
6126
question_filename));
6129
static void test_send_password_to_socket_retry(__attribute__((unused))
6130
test_fixture *fixture,
6131
__attribute__((unused))
6134
__attribute__((cleanup(cleanup_close)))
6135
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6136
g_assert_cmpint(epoll_fd, >=, 0);
6137
__attribute__((cleanup(cleanup_string)))
6138
char *const question_filename = strdup("/nonexistent/question");
6139
g_assert_nonnull(question_filename);
6140
__attribute__((cleanup(cleanup_string)))
6141
char *const filename = strdup("/nonexistent/socket");
6142
g_assert_nonnull(filename);
6143
__attribute__((cleanup(string_set_clear)))
6144
string_set cancelled_filenames = {};
6145
__attribute__((cleanup(cleanup_buffer)))
6146
buffer password = {};
6148
__attribute__((cleanup(cleanup_queue)))
6149
task_queue *queue = create_queue();
6150
g_assert_nonnull(queue);
6152
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6153
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6155
__attribute__((cleanup(cleanup_close)))
6156
const int read_socket = socketfds[0];
6157
const int write_socket = socketfds[1];
6158
/* Close the server side socket to force ECONNRESET on client */
6159
g_assert_cmpint(close(read_socket), ==, 0);
6160
task_context task = {
6161
.func=send_password_to_socket,
6162
.question_filename=strdup(question_filename),
6163
.filename=strdup(filename),
6166
.password=&password,
6167
.cancelled_filenames=&cancelled_filenames,
6168
.mandos_client_exited=(bool[]){true},
6169
.password_is_read=(bool[]){true},
6170
.current_time=(mono_microsecs[]){0},
6172
g_assert_nonnull(task.question_filename);
6174
task.func(task, queue);
6176
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6178
const task_context *const added_task = find_matching_task(queue,
6180
g_assert_nonnull(added_task);
6181
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6183
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6188
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6189
test_fixture *fixture,
6190
__attribute__((unused))
6191
gconstpointer user_data){
6192
__attribute__((cleanup(cleanup_close)))
6193
const int epoll_fd = open("/dev/null",
6194
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6195
__attribute__((cleanup(cleanup_string)))
6196
char *const question_filename = strdup("/nonexistent/question");
6197
g_assert_nonnull(question_filename);
6198
__attribute__((cleanup(cleanup_string)))
6199
char *const filename = strdup("/nonexistent/socket");
6200
g_assert_nonnull(filename);
6201
__attribute__((cleanup(string_set_clear)))
6202
string_set cancelled_filenames = {};
6203
__attribute__((cleanup(cleanup_buffer)))
6204
buffer password = {};
6206
const mono_microsecs current_time = 11;
6207
__attribute__((cleanup(cleanup_queue)))
6208
task_queue *queue = create_queue();
6209
g_assert_nonnull(queue);
6211
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6212
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6214
__attribute__((cleanup(cleanup_close)))
6215
const int read_socket = socketfds[0];
6216
const int write_socket = socketfds[1];
6217
/* Close the server side socket to force ECONNRESET on client */
6218
g_assert_cmpint(close(read_socket), ==, 0);
6219
task_context task = {
6220
.func=send_password_to_socket,
6221
.question_filename=strdup(question_filename),
6222
.filename=strdup(filename),
6225
.password=&password,
6226
.cancelled_filenames=&cancelled_filenames,
6227
.mandos_client_exited=(bool[]){true},
6228
.password_is_read=(bool[]){true},
6229
.current_time=¤t_time,
6231
g_assert_nonnull(task.question_filename);
6233
run_task_with_stderr_to_dev_null(task, queue);
6235
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6237
const task_context *const added_task = find_matching_task(queue,
6239
g_assert_nonnull(added_task);
6240
g_assert_true(queue->next_run == current_time + 1000000);
6241
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6244
static void assert_send_password_to_socket_password(buffer password){
6245
__attribute__((cleanup(cleanup_close)))
6246
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6247
g_assert_cmpint(epoll_fd, >=, 0);
6248
char *const question_filename = strdup("/nonexistent/question");
6249
g_assert_nonnull(question_filename);
6250
char *const filename = strdup("/nonexistent/socket");
6251
g_assert_nonnull(filename);
6252
__attribute__((cleanup(string_set_clear)))
6253
string_set cancelled_filenames = {};
6255
__attribute__((cleanup(cleanup_queue)))
6256
task_queue *queue = create_queue();
6257
g_assert_nonnull(queue);
6259
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6260
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6262
__attribute__((cleanup(cleanup_close)))
6263
const int read_socket = socketfds[0];
6264
const int write_socket = socketfds[1];
6265
task_context task = {
6266
.func=send_password_to_socket,
6267
.question_filename=question_filename,
6271
.password=&password,
6272
.cancelled_filenames=&cancelled_filenames,
6273
.mandos_client_exited=(bool[]){true},
6274
.password_is_read=(bool[]){true},
6275
.current_time=(mono_microsecs[]){0},
6278
char *expected_written_data = malloc(password.length + 2);
6279
g_assert_nonnull(expected_written_data);
6280
expected_written_data[0] = '+';
6281
expected_written_data[password.length + 1] = '\0';
6282
if(password.length > 0){
6283
g_assert_nonnull(password.data);
6284
memcpy(expected_written_data + 1, password.data, password.length);
6287
task.func(task, queue);
6290
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6291
(int)(password.length + 2));
6292
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6294
g_assert_true(memcmp(expected_written_data, buf,
6295
password.length + 2) == 0);
6297
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6299
free(expected_written_data);
6303
test_send_password_to_socket_null_password(__attribute__((unused))
6304
test_fixture *fixture,
6305
__attribute__((unused))
6306
gconstpointer user_data){
6307
__attribute__((cleanup(cleanup_buffer)))
6308
buffer password = {};
6309
assert_send_password_to_socket_password(password);
6313
test_send_password_to_socket_empty_password(__attribute__((unused))
6314
test_fixture *fixture,
6315
__attribute__((unused))
6316
gconstpointer user_data){
6317
__attribute__((cleanup(cleanup_buffer)))
6319
.data=malloc(1), /* because malloc(0) may return NULL */
6321
.allocated=0, /* deliberate lie */
6323
g_assert_nonnull(password.data);
6324
assert_send_password_to_socket_password(password);
6328
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6329
test_fixture *fixture,
6330
__attribute__((unused))
6331
gconstpointer user_data){
6332
__attribute__((cleanup(cleanup_buffer)))
6338
if(mlock(password.data, password.allocated) != 0){
6339
g_assert_true(errno == EPERM or errno == ENOMEM);
6341
assert_send_password_to_socket_password(password);
6345
test_send_password_to_socket_text_password(__attribute__((unused))
6346
test_fixture *fixture,
6347
__attribute__((unused))
6348
gconstpointer user_data){
6349
const char dummy_test_password[] = "dummy test password";
6350
__attribute__((cleanup(cleanup_buffer)))
6352
.data = strdup(dummy_test_password),
6353
.length = strlen(dummy_test_password),
6354
.allocated = sizeof(dummy_test_password),
6356
if(mlock(password.data, password.allocated) != 0){
6357
g_assert_true(errno == EPERM or errno == ENOMEM);
6359
assert_send_password_to_socket_password(password);
6363
test_send_password_to_socket_binary_password(__attribute__((unused))
6364
test_fixture *fixture,
6365
__attribute__((unused))
6366
gconstpointer user_data){
6367
__attribute__((cleanup(cleanup_buffer)))
6373
g_assert_nonnull(password.data);
6374
if(mlock(password.data, password.allocated) != 0){
6375
g_assert_true(errno == EPERM or errno == ENOMEM);
6377
char c = 1; /* Start at 1, avoiding NUL */
6378
for(int i=0; i < 255; i++){
6379
password.data[i] = c++;
6381
assert_send_password_to_socket_password(password);
6385
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6386
test_fixture *fixture,
6387
__attribute__((unused))
6390
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6391
__attribute__((cleanup(cleanup_buffer)))
6393
.data=malloc(sizeof(test_password)),
6394
.length=sizeof(test_password),
6395
.allocated=sizeof(test_password),
6397
g_assert_nonnull(password.data);
6398
if(mlock(password.data, password.allocated) !=0){
6399
g_assert_true(errno == EPERM or errno == ENOMEM);
6401
memcpy(password.data, test_password, password.allocated);
6402
assert_send_password_to_socket_password(password);
6405
static bool assert_add_existing_questions_to_devnull(task_queue
6418
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6419
test_fixture *fixture,
6420
__attribute__((unused))
6423
__attribute__((cleanup(cleanup_queue)))
6424
task_queue *queue = create_queue();
6425
g_assert_nonnull(queue);
6426
__attribute__((cleanup(cleanup_close)))
6427
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6428
g_assert_cmpint(epoll_fd, >=, 0);
6429
__attribute__((cleanup(string_set_clear)))
6430
string_set cancelled_filenames = {};
6432
g_assert_false(assert_add_existing_questions_to_devnull
6435
(buffer[]){{}}, /* password */
6436
&cancelled_filenames,
6437
(mono_microsecs[]){0}, /* current_time */
6438
(bool[]){false}, /* mandos_client_exited */
6439
(bool[]){false}, /* password_is_read */
6440
"/nonexistent")); /* dirname */
6442
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6446
bool assert_add_existing_questions_to_devnull(task_queue
6453
*cancelled_filenames,
6454
const mono_microsecs
6455
*const current_time,
6457
mandos_client_exited,
6462
__attribute__((cleanup(cleanup_close)))
6463
const int devnull_fd = open("/dev/null",
6464
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6465
g_assert_cmpint(devnull_fd, >=, 0);
6466
__attribute__((cleanup(cleanup_close)))
6467
const int real_stderr_fd = dup(STDERR_FILENO);
6468
g_assert_cmpint(real_stderr_fd, >=, 0);
6469
dup2(devnull_fd, STDERR_FILENO);
6470
const bool ret = add_existing_questions(queue, epoll_fd, password,
6471
cancelled_filenames,
6473
mandos_client_exited,
6474
password_is_read, dirname);
6475
dup2(real_stderr_fd, STDERR_FILENO);
6480
void test_add_existing_questions_no_questions(__attribute__((unused))
6481
test_fixture *fixture,
6482
__attribute__((unused))
6485
__attribute__((cleanup(cleanup_queue)))
6486
task_queue *queue = create_queue();
6487
g_assert_nonnull(queue);
6488
__attribute__((cleanup(cleanup_close)))
6489
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6490
g_assert_cmpint(epoll_fd, >=, 0);
6491
__attribute__((cleanup(string_set_clear)))
6492
string_set cancelled_filenames = {};
6493
__attribute__((cleanup(cleanup_string)))
6494
char *tempdir = make_temporary_directory();
6495
g_assert_nonnull(tempdir);
6497
g_assert_false(assert_add_existing_questions_to_devnull
6500
(buffer[]){{}}, /* password */
6501
&cancelled_filenames,
6502
(mono_microsecs[]){0}, /* current_time */
6503
(bool[]){false}, /* mandos_client_exited */
6504
(bool[]){false}, /* password_is_read */
6507
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6509
g_assert_cmpint(rmdir(tempdir), ==, 0);
6512
static char *make_question_file_in_directory(const char *const);
6515
void test_add_existing_questions_one_question(__attribute__((unused))
6516
test_fixture *fixture,
6517
__attribute__((unused))
6520
__attribute__((cleanup(cleanup_queue)))
6521
task_queue *queue = create_queue();
6522
g_assert_nonnull(queue);
6523
__attribute__((cleanup(cleanup_close)))
6524
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6525
g_assert_cmpint(epoll_fd, >=, 0);
6526
__attribute__((cleanup(cleanup_buffer)))
6527
buffer password = {};
6528
__attribute__((cleanup(string_set_clear)))
6529
string_set cancelled_filenames = {};
6530
const mono_microsecs current_time = 0;
6531
bool mandos_client_exited = false;
6532
bool password_is_read = false;
6533
__attribute__((cleanup(cleanup_string)))
6534
char *tempdir = make_temporary_directory();
6535
g_assert_nonnull(tempdir);
6536
__attribute__((cleanup(cleanup_string)))
6537
char *question_filename
6538
= make_question_file_in_directory(tempdir);
6539
g_assert_nonnull(question_filename);
6541
g_assert_true(assert_add_existing_questions_to_devnull
6545
&cancelled_filenames,
6547
&mandos_client_exited,
6551
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6553
g_assert_nonnull(find_matching_task(queue, (task_context){
6554
.func=open_and_parse_question,
6556
.filename=question_filename,
6557
.question_filename=question_filename,
6558
.password=&password,
6559
.cancelled_filenames=&cancelled_filenames,
6560
.current_time=¤t_time,
6561
.mandos_client_exited=&mandos_client_exited,
6562
.password_is_read=&password_is_read,
6565
g_assert_true(queue->next_run == 1);
6567
g_assert_cmpint(unlink(question_filename), ==, 0);
6568
g_assert_cmpint(rmdir(tempdir), ==, 0);
6571
static char *make_question_file_in_directory(const char
6573
return make_temporary_prefixed_file_in_directory("ask.", dir);
6577
void test_add_existing_questions_two_questions(__attribute__((unused))
6578
test_fixture *fixture,
6579
__attribute__((unused))
6582
__attribute__((cleanup(cleanup_queue)))
6583
task_queue *queue = create_queue();
6584
g_assert_nonnull(queue);
6585
__attribute__((cleanup(cleanup_close)))
6586
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6587
g_assert_cmpint(epoll_fd, >=, 0);
6588
__attribute__((cleanup(cleanup_buffer)))
6589
buffer password = {};
6590
__attribute__((cleanup(string_set_clear)))
6591
string_set cancelled_filenames = {};
6592
const mono_microsecs current_time = 0;
6593
bool mandos_client_exited = false;
6594
bool password_is_read = false;
6595
__attribute__((cleanup(cleanup_string)))
6596
char *tempdir = make_temporary_directory();
6597
g_assert_nonnull(tempdir);
6598
__attribute__((cleanup(cleanup_string)))
6599
char *question_filename1
6600
= make_question_file_in_directory(tempdir);
6601
g_assert_nonnull(question_filename1);
6602
__attribute__((cleanup(cleanup_string)))
6603
char *question_filename2
6604
= make_question_file_in_directory(tempdir);
6605
g_assert_nonnull(question_filename2);
6607
g_assert_true(assert_add_existing_questions_to_devnull
6611
&cancelled_filenames,
6613
&mandos_client_exited,
6617
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6619
g_assert_true(queue->next_run == 1);
6621
__attribute__((cleanup(string_set_clear)))
6622
string_set seen_questions = {};
6624
bool queue_contains_question_opener(char *const question_filename){
6625
return(find_matching_task(queue, (task_context){
6626
.func=open_and_parse_question,
6628
.question_filename=question_filename,
6629
.password=&password,
6630
.cancelled_filenames=&cancelled_filenames,
6631
.current_time=¤t_time,
6632
.mandos_client_exited=&mandos_client_exited,
6633
.password_is_read=&password_is_read,
6637
g_assert_true(queue_contains_question_opener(question_filename1));
6638
g_assert_true(queue_contains_question_opener(question_filename2));
6640
g_assert_true(queue->next_run == 1);
6642
g_assert_cmpint(unlink(question_filename1), ==, 0);
6643
g_assert_cmpint(unlink(question_filename2), ==, 0);
6644
g_assert_cmpint(rmdir(tempdir), ==, 0);
6648
test_add_existing_questions_non_questions(__attribute__((unused))
6649
test_fixture *fixture,
6650
__attribute__((unused))
6651
gconstpointer user_data){
6652
__attribute__((cleanup(cleanup_queue)))
6653
task_queue *queue = create_queue();
6654
g_assert_nonnull(queue);
6655
__attribute__((cleanup(cleanup_close)))
6656
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6657
g_assert_cmpint(epoll_fd, >=, 0);
6658
__attribute__((cleanup(string_set_clear)))
6659
string_set cancelled_filenames = {};
6660
__attribute__((cleanup(cleanup_string)))
6661
char *tempdir = make_temporary_directory();
6662
g_assert_nonnull(tempdir);
6663
__attribute__((cleanup(cleanup_string)))
6664
char *question_filename1
6665
= make_temporary_file_in_directory(tempdir);
6666
g_assert_nonnull(question_filename1);
6667
__attribute__((cleanup(cleanup_string)))
6668
char *question_filename2
6669
= make_temporary_file_in_directory(tempdir);
6670
g_assert_nonnull(question_filename2);
6672
g_assert_false(assert_add_existing_questions_to_devnull
6675
(buffer[]){{}}, /* password */
6676
&cancelled_filenames,
6677
(mono_microsecs[]){0}, /* current_time */
6678
(bool[]){false}, /* mandos_client_exited */
6679
(bool[]){false}, /* password_is_read */
6682
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6684
g_assert_cmpint(unlink(question_filename1), ==, 0);
6685
g_assert_cmpint(unlink(question_filename2), ==, 0);
6686
g_assert_cmpint(rmdir(tempdir), ==, 0);
6690
test_add_existing_questions_both_types(__attribute__((unused))
6691
test_fixture *fixture,
6692
__attribute__((unused))
6693
gconstpointer user_data){
6694
__attribute__((cleanup(cleanup_queue)))
6695
task_queue *queue = create_queue();
6696
g_assert_nonnull(queue);
6697
__attribute__((cleanup(cleanup_close)))
6698
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6699
g_assert_cmpint(epoll_fd, >=, 0);
6700
__attribute__((cleanup(cleanup_buffer)))
6701
buffer password = {};
6702
__attribute__((cleanup(string_set_clear)))
6703
string_set cancelled_filenames = {};
6704
const mono_microsecs current_time = 0;
6705
bool mandos_client_exited = false;
6706
bool password_is_read = false;
6707
__attribute__((cleanup(cleanup_string)))
6708
char *tempdir = make_temporary_directory();
6709
g_assert_nonnull(tempdir);
6710
__attribute__((cleanup(cleanup_string)))
6711
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6712
g_assert_nonnull(tempfilename1);
6713
__attribute__((cleanup(cleanup_string)))
6714
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6715
g_assert_nonnull(tempfilename2);
6716
__attribute__((cleanup(cleanup_string)))
6717
char *question_filename
6718
= make_question_file_in_directory(tempdir);
6719
g_assert_nonnull(question_filename);
6721
g_assert_true(assert_add_existing_questions_to_devnull
6725
&cancelled_filenames,
6727
&mandos_client_exited,
6731
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6733
g_assert_nonnull(find_matching_task(queue, (task_context){
6734
.func=open_and_parse_question,
6736
.filename=question_filename,
6737
.question_filename=question_filename,
6738
.password=&password,
6739
.cancelled_filenames=&cancelled_filenames,
6740
.current_time=¤t_time,
6741
.mandos_client_exited=&mandos_client_exited,
6742
.password_is_read=&password_is_read,
6745
g_assert_true(queue->next_run == 1);
6747
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6748
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6749
g_assert_cmpint(unlink(question_filename), ==, 0);
6750
g_assert_cmpint(rmdir(tempdir), ==, 0);
6753
static void test_wait_for_event_timeout(__attribute__((unused))
6754
test_fixture *fixture,
6755
__attribute__((unused))
6756
gconstpointer user_data){
6757
__attribute__((cleanup(cleanup_close)))
6758
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6759
g_assert_cmpint(epoll_fd, >=, 0);
6761
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6764
static void test_wait_for_event_event(__attribute__((unused))
6765
test_fixture *fixture,
6766
__attribute__((unused))
6767
gconstpointer user_data){
6768
__attribute__((cleanup(cleanup_close)))
6769
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6770
g_assert_cmpint(epoll_fd, >=, 0);
6772
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6773
__attribute__((cleanup(cleanup_close)))
6774
const int read_pipe = pipefds[0];
6775
__attribute__((cleanup(cleanup_close)))
6776
const int write_pipe = pipefds[1];
6777
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6778
&(struct epoll_event)
6779
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6780
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6782
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6785
static void test_wait_for_event_sigchld(test_fixture *fixture,
6786
__attribute__((unused))
6787
gconstpointer user_data){
6788
const pid_t pid = fork();
6789
if(pid == 0){ /* Child */
6790
if(not restore_signal_handler(&fixture->orig_sigaction)){
6791
_exit(EXIT_FAILURE);
6793
if(not restore_sigmask(&fixture->orig_sigmask)){
6794
_exit(EXIT_FAILURE);
6798
g_assert_true(pid != -1);
6799
__attribute__((cleanup(cleanup_close)))
6800
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6801
g_assert_cmpint(epoll_fd, >=, 0);
6803
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6806
g_assert_true(waitpid(pid, &status, 0) == pid);
6807
g_assert_true(WIFEXITED(status));
6808
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6811
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6812
test_fixture *fixture,
6813
__attribute__((unused))
6814
gconstpointer user_data){
6815
__attribute__((cleanup(cleanup_queue)))
6816
task_queue *queue = create_queue();
6817
g_assert_nonnull(queue);
6818
queue->next_run = 1;
6819
__attribute__((cleanup(cleanup_close)))
6820
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6821
__attribute__((cleanup(string_set_clear)))
6822
string_set cancelled_filenames = {};
6823
bool quit_now = false;
6825
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6826
g_assert_false(quit_now);
6827
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6831
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6832
test_fixture *fixture,
6833
__attribute__((unused))
6836
__attribute__((cleanup(cleanup_queue)))
6837
task_queue *queue = create_queue();
6838
g_assert_nonnull(queue);
6839
__attribute__((cleanup(string_set_clear)))
6840
string_set cancelled_filenames = {};
6841
bool quit_now = false;
6842
const char question_filename[] = "/nonexistent/question_filename";
6843
g_assert_true(string_set_add(&cancelled_filenames,
6844
question_filename));
6846
g_assert_true(add_to_queue(queue,
6847
(task_context){ .func=dummy_func }));
6849
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6850
g_assert_false(quit_now);
6851
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6852
g_assert_false(string_set_contains(cancelled_filenames,
6853
question_filename));
6857
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6858
test_fixture *fixture,
6859
__attribute__((unused))
6862
__attribute__((cleanup(cleanup_queue)))
6863
task_queue *queue = create_queue();
6864
g_assert_nonnull(queue);
6865
__attribute__((cleanup(string_set_clear)))
6866
string_set cancelled_filenames = {};
6867
bool quit_now = false;
6869
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6870
__attribute__((cleanup(cleanup_close)))
6871
const int read_pipe = pipefds[0];
6872
g_assert_cmpint(close(pipefds[1]), ==, 0);
6873
const char question_filename[] = "/nonexistent/question_filename";
6874
g_assert_true(string_set_add(&cancelled_filenames,
6875
question_filename));
6876
__attribute__((nonnull))
6877
void quit_func(const task_context task,
6878
__attribute__((unused)) task_queue *const q){
6879
g_assert_nonnull(task.quit_now);
6880
*task.quit_now = true;
6882
task_context task = {
6884
.question_filename=strdup(question_filename),
6885
.quit_now=&quit_now,
6888
g_assert_nonnull(task.question_filename);
6890
g_assert_true(add_to_queue(queue, task));
6892
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6893
g_assert_false(quit_now);
6895
/* read_pipe should be closed already */
6897
bool read_pipe_closed = (close(read_pipe) == -1);
6898
read_pipe_closed &= (errno == EBADF);
6899
g_assert_true(read_pipe_closed);
6902
static void test_run_queue_one_task(__attribute__((unused))
6903
test_fixture *fixture,
6904
__attribute__((unused))
6905
gconstpointer user_data){
6906
__attribute__((cleanup(cleanup_queue)))
6907
task_queue *queue = create_queue();
6908
g_assert_nonnull(queue);
6909
__attribute__((cleanup(string_set_clear)))
6910
string_set cancelled_filenames = {};
6911
bool quit_now = false;
6913
__attribute__((nonnull))
6914
void next_run_func(__attribute__((unused))
6915
const task_context task,
6916
task_queue *const q){
6920
task_context task = {
6921
.func=next_run_func,
6923
g_assert_true(add_to_queue(queue, task));
6925
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6926
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6927
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6930
static void test_run_queue_two_tasks(__attribute__((unused))
6931
test_fixture *fixture,
6932
__attribute__((unused))
6933
gconstpointer user_data){
6934
__attribute__((cleanup(cleanup_queue)))
6935
task_queue *queue = create_queue();
6936
g_assert_nonnull(queue);
6937
queue->next_run = 1;
6938
__attribute__((cleanup(string_set_clear)))
6939
string_set cancelled_filenames = {};
6940
bool quit_now = false;
6941
bool mandos_client_exited = false;
6943
__attribute__((nonnull))
6944
void next_run_func(__attribute__((unused))
6945
const task_context task,
6946
task_queue *const q){
6950
__attribute__((nonnull))
6951
void exited_func(const task_context task,
6952
__attribute__((unused)) task_queue *const q){
6953
*task.mandos_client_exited = true;
6956
task_context task1 = {
6957
.func=next_run_func,
6959
g_assert_true(add_to_queue(queue, task1));
6961
task_context task2 = {
6963
.mandos_client_exited=&mandos_client_exited,
6965
g_assert_true(add_to_queue(queue, task2));
6967
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6968
g_assert_false(quit_now);
6969
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6970
g_assert_true(mandos_client_exited);
6971
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6974
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6975
test_fixture *fixture,
6976
__attribute__((unused))
6977
gconstpointer user_data){
6978
__attribute__((cleanup(cleanup_queue)))
6979
task_queue *queue = create_queue();
6980
g_assert_nonnull(queue);
6981
__attribute__((cleanup(string_set_clear)))
6982
string_set cancelled_filenames = {};
6983
bool quit_now = false;
6984
bool mandos_client_exited = false;
6985
bool password_is_read = false;
6987
__attribute__((nonnull))
6988
void set_exited_func(const task_context task,
6989
__attribute__((unused)) task_queue *const q){
6990
*task.mandos_client_exited = true;
6991
*task.quit_now = true;
6993
task_context task1 = {
6994
.func=set_exited_func,
6995
.quit_now=&quit_now,
6996
.mandos_client_exited=&mandos_client_exited,
6998
g_assert_true(add_to_queue(queue, task1));
7000
__attribute__((nonnull))
7001
void set_read_func(const task_context task,
7002
__attribute__((unused)) task_queue *const q){
7003
*task.quit_now = true;
7004
*task.password_is_read = true;
7006
task_context task2 = {
7007
.func=set_read_func,
7008
.quit_now=&quit_now,
7009
.password_is_read=&password_is_read,
7011
g_assert_true(add_to_queue(queue, task2));
7013
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
7014
g_assert_true(quit_now);
7015
g_assert_true(mandos_client_exited xor password_is_read);
7016
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7019
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
7020
test_fixture *fixture,
7021
__attribute__((unused))
7022
gconstpointer user_data){
7023
__attribute__((cleanup(cleanup_queue)))
7024
task_queue *queue = create_queue();
7025
g_assert_nonnull(queue);
7026
__attribute__((cleanup(string_set_clear)))
7027
string_set cancelled_filenames = {};
7029
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
7030
__attribute__((cleanup(cleanup_close)))
7031
const int read_pipe = pipefds[0];
7032
__attribute__((cleanup(cleanup_close)))
7033
const int write_pipe = pipefds[1];
7034
bool quit_now = false;
7036
__attribute__((nonnull))
7037
void read_func(const task_context task,
7038
__attribute__((unused)) task_queue *const q){
7039
*task.quit_now = true;
7041
task_context task1 = {
7043
.quit_now=&quit_now,
7046
g_assert_true(add_to_queue(queue, task1));
7048
__attribute__((nonnull))
7049
void write_func(const task_context task,
7050
__attribute__((unused)) task_queue *const q){
7051
*task.quit_now = true;
7053
task_context task2 = {
7055
.quit_now=&quit_now,
7058
g_assert_true(add_to_queue(queue, task2));
7060
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
7061
g_assert_true(quit_now);
7063
/* Either read_pipe or write_pipe should be closed already */
7065
bool close_read_pipe = (close(read_pipe) == -1);
7066
close_read_pipe &= (errno == EBADF);
7068
bool close_write_pipe = (close(write_pipe) == -1);
7069
close_write_pipe &= (errno == EBADF);
7070
g_assert_true(close_read_pipe xor close_write_pipe);
7071
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7074
static void test_setup_signal_handler(__attribute__((unused))
7075
test_fixture *fixture,
7076
__attribute__((unused))
7077
gconstpointer user_data){
7078
/* Save current SIGCHLD action, whatever it is */
7079
struct sigaction expected_sigchld_action;
7080
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7083
/* Act; i.e. run the setup_signal_handler() function */
7084
struct sigaction actual_old_sigchld_action;
7085
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
7087
/* Check that the function correctly set "actual_old_sigchld_action"
7088
to the same values as the previously saved
7089
"expected_sigchld_action" */
7090
/* Check member sa_handler */
7091
g_assert_true(actual_old_sigchld_action.sa_handler
7092
== expected_sigchld_action.sa_handler);
7093
/* Check member sa_mask */
7094
for(int signum = 1; signum < NSIG; signum++){
7095
const int expected_old_block_state
7096
= sigismember(&expected_sigchld_action.sa_mask, signum);
7097
g_assert_cmpint(expected_old_block_state, >=, 0);
7098
const int actual_old_block_state
7099
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
7100
g_assert_cmpint(actual_old_block_state, >=, 0);
7101
g_assert_cmpint(actual_old_block_state,
7102
==, expected_old_block_state);
7104
/* Check member sa_flags */
7105
g_assert_true((actual_old_sigchld_action.sa_flags
7106
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7107
== (expected_sigchld_action.sa_flags
7108
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7110
/* Retrieve the current signal handler for SIGCHLD as set by
7111
setup_signal_handler() */
7112
struct sigaction actual_new_sigchld_action;
7113
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7114
&actual_new_sigchld_action), ==, 0);
7115
/* Check that the signal handler (member sa_handler) is correctly
7116
set to the "handle_sigchld" function */
7117
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7118
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7119
g_assert_true(actual_new_sigchld_action.sa_handler
7121
/* Check (in member sa_mask) that at least a handful of signals are
7122
actually blocked during the signal handler */
7123
for(int signum = 1; signum < NSIG; signum++){
7124
int actual_new_block_state;
7130
actual_new_block_state
7131
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7132
g_assert_cmpint(actual_new_block_state, ==, 1);
7134
case SIGKILL: /* non-blockable */
7135
case SIGSTOP: /* non-blockable */
7136
case SIGCHLD: /* always blocked */
7141
/* Check member sa_flags */
7142
g_assert_true((actual_new_sigchld_action.sa_flags
7143
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7144
== (SA_NOCLDSTOP | SA_RESTART));
7146
/* Restore signal handler */
7147
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7151
static void test_restore_signal_handler(__attribute__((unused))
7152
test_fixture *fixture,
7153
__attribute__((unused))
7154
gconstpointer user_data){
7155
/* Save current SIGCHLD action, whatever it is */
7156
struct sigaction expected_sigchld_action;
7157
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7159
/* Since we haven't established a signal handler yet, there should
7160
not be one established. But another test may have relied on
7161
restore_signal_handler() to restore the signal handler, and if
7162
restore_signal_handler() is buggy (which we should be prepared
7163
for in this test) the signal handler may not have been restored
7164
properly; check for this: */
7165
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7167
/* Establish a signal handler */
7168
struct sigaction sigchld_action = {
7169
.sa_handler=handle_sigchld,
7170
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7172
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7173
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7175
/* Act; i.e. run the restore_signal_handler() function */
7176
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7178
/* Retrieve the restored signal handler data */
7179
struct sigaction actual_restored_sigchld_action;
7180
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7181
&actual_restored_sigchld_action), ==, 0);
7183
/* Check that the function correctly restored the signal action, as
7184
saved in "actual_restored_sigchld_action", to the same values as
7185
the previously saved "expected_sigchld_action" */
7186
/* Check member sa_handler */
7187
g_assert_true(actual_restored_sigchld_action.sa_handler
7188
== expected_sigchld_action.sa_handler);
7189
/* Check member sa_mask */
7190
for(int signum = 1; signum < NSIG; signum++){
7191
const int expected_old_block_state
7192
= sigismember(&expected_sigchld_action.sa_mask, signum);
7193
g_assert_cmpint(expected_old_block_state, >=, 0);
7194
const int actual_restored_block_state
7195
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7196
g_assert_cmpint(actual_restored_block_state, >=, 0);
7197
g_assert_cmpint(actual_restored_block_state,
7198
==, expected_old_block_state);
7200
/* Check member sa_flags */
7201
g_assert_true((actual_restored_sigchld_action.sa_flags
7202
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7203
== (expected_sigchld_action.sa_flags
7204
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7207
static void test_block_sigchld(__attribute__((unused))
7208
test_fixture *fixture,
7209
__attribute__((unused))
7210
gconstpointer user_data){
7211
/* Save original signal mask */
7212
sigset_t expected_sigmask;
7213
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7216
/* Make sure SIGCHLD is unblocked for this test */
7217
sigset_t sigchld_sigmask;
7218
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7219
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7220
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7223
/* Act; i.e. run the block_sigchld() function */
7224
sigset_t actual_old_sigmask;
7225
g_assert_true(block_sigchld(&actual_old_sigmask));
7227
/* Check the actual_old_sigmask; it should be the same as the
7228
previously saved signal mask "expected_sigmask". */
7229
for(int signum = 1; signum < NSIG; signum++){
7230
const int expected_old_block_state
7231
= sigismember(&expected_sigmask, signum);
7232
g_assert_cmpint(expected_old_block_state, >=, 0);
7233
const int actual_old_block_state
7234
= sigismember(&actual_old_sigmask, signum);
7235
g_assert_cmpint(actual_old_block_state, >=, 0);
7236
g_assert_cmpint(actual_old_block_state,
7237
==, expected_old_block_state);
7240
/* Retrieve the newly set signal mask */
7241
sigset_t actual_sigmask;
7242
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7244
/* SIGCHLD should be blocked */
7245
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7247
/* Restore signal mask */
7248
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7252
static void test_restore_sigmask(__attribute__((unused))
7253
test_fixture *fixture,
7254
__attribute__((unused))
7255
gconstpointer user_data){
7256
/* Save original signal mask */
7257
sigset_t orig_sigmask;
7258
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7260
/* Make sure SIGCHLD is blocked for this test */
7261
sigset_t sigchld_sigmask;
7262
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7263
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7264
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7267
/* Act; i.e. run the restore_sigmask() function */
7268
g_assert_true(restore_sigmask(&orig_sigmask));
7270
/* Retrieve the newly restored signal mask */
7271
sigset_t restored_sigmask;
7272
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7275
/* Check the restored_sigmask; it should be the same as the
7276
previously saved signal mask "orig_sigmask". */
7277
for(int signum = 1; signum < NSIG; signum++){
7278
const int orig_block_state = sigismember(&orig_sigmask, signum);
7279
g_assert_cmpint(orig_block_state, >=, 0);
7280
const int restored_block_state = sigismember(&restored_sigmask,
7282
g_assert_cmpint(restored_block_state, >=, 0);
7283
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7286
/* Restore signal mask */
7287
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7291
static void test_parse_arguments_noargs(__attribute__((unused))
7292
test_fixture *fixture,
7293
__attribute__((unused))
7294
gconstpointer user_data){
7298
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7300
char *agent_directory = NULL;
7301
char *helper_directory = NULL;
7304
char *mandos_argz = NULL;
7305
size_t mandos_argz_length = 0;
7307
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7308
&helper_directory, &user, &group,
7309
&mandos_argz, &mandos_argz_length));
7310
g_assert_null(agent_directory);
7311
g_assert_null(helper_directory);
7312
g_assert_true(user == 0);
7313
g_assert_true(group == 0);
7314
g_assert_null(mandos_argz);
7315
g_assert_true(mandos_argz_length == 0);
7317
for(char **arg = argv; *arg != NULL; arg++){
7322
__attribute__((nonnull))
7323
static bool parse_arguments_devnull(int argc, char *argv[],
7324
const bool exit_failure,
7325
char **agent_directory,
7326
char **helper_directory,
7330
size_t *mandos_argz_length){
7332
FILE *real_stderr = stderr;
7333
FILE *devnull = fopen("/dev/null", "we");
7334
g_assert_nonnull(devnull);
7337
const bool ret = parse_arguments(argc, argv, exit_failure,
7339
helper_directory, user, group,
7340
mandos_argz, mandos_argz_length);
7341
const error_t saved_errno = errno;
7343
stderr = real_stderr;
7344
g_assert_cmpint(fclose(devnull), ==, 0);
7346
errno = saved_errno;
7351
static void test_parse_arguments_invalid(__attribute__((unused))
7352
test_fixture *fixture,
7353
__attribute__((unused))
7354
gconstpointer user_data){
7357
strdup("--invalid"),
7359
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7361
char *agent_directory = NULL;
7362
char *helper_directory = NULL;
7365
char *mandos_argz = NULL;
7366
size_t mandos_argz_length = 0;
7368
g_assert_false(parse_arguments_devnull(argc, argv, false,
7370
&helper_directory, &user,
7371
&group, &mandos_argz,
7372
&mandos_argz_length));
7374
g_assert_true(errno == EINVAL);
7375
g_assert_null(agent_directory);
7376
g_assert_null(helper_directory);
7377
g_assert_null(mandos_argz);
7378
g_assert_true(mandos_argz_length == 0);
7380
for(char **arg = argv; *arg != NULL; arg++){
7385
static void test_parse_arguments_long_dir(__attribute__((unused))
7386
test_fixture *fixture,
7387
__attribute__((unused))
7388
gconstpointer user_data){
7391
strdup("--agent-directory"),
7394
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7396
__attribute__((cleanup(cleanup_string)))
7397
char *agent_directory = NULL;
7398
char *helper_directory = NULL;
7401
__attribute__((cleanup(cleanup_string)))
7402
char *mandos_argz = NULL;
7403
size_t mandos_argz_length = 0;
7405
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7406
&helper_directory, &user, &group,
7407
&mandos_argz, &mandos_argz_length));
7409
g_assert_cmpstr(agent_directory, ==, "/tmp");
7410
g_assert_null(helper_directory);
7411
g_assert_true(user == 0);
7412
g_assert_true(group == 0);
7413
g_assert_null(mandos_argz);
7414
g_assert_true(mandos_argz_length == 0);
7416
for(char **arg = argv; *arg != NULL; arg++){
7421
static void test_parse_arguments_short_dir(__attribute__((unused))
7422
test_fixture *fixture,
7423
__attribute__((unused))
7424
gconstpointer user_data){
7430
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7432
__attribute__((cleanup(cleanup_string)))
7433
char *agent_directory = NULL;
7434
char *helper_directory = NULL;
7437
__attribute__((cleanup(cleanup_string)))
7438
char *mandos_argz = NULL;
7439
size_t mandos_argz_length = 0;
7441
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7442
&helper_directory, &user, &group,
7443
&mandos_argz, &mandos_argz_length));
7445
g_assert_cmpstr(agent_directory, ==, "/tmp");
7446
g_assert_null(helper_directory);
7447
g_assert_true(user == 0);
7448
g_assert_true(group == 0);
7449
g_assert_null(mandos_argz);
7450
g_assert_true(mandos_argz_length == 0);
7452
for(char **arg = argv; *arg != NULL; arg++){
7458
void test_parse_arguments_helper_directory(__attribute__((unused))
7459
test_fixture *fixture,
7460
__attribute__((unused))
7461
gconstpointer user_data){
7464
strdup("--helper-directory"),
7467
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7469
char *agent_directory = NULL;
7470
__attribute__((cleanup(cleanup_string)))
7471
char *helper_directory = NULL;
7474
__attribute__((cleanup(cleanup_string)))
7475
char *mandos_argz = NULL;
7476
size_t mandos_argz_length = 0;
7478
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7479
&helper_directory, &user, &group,
7480
&mandos_argz, &mandos_argz_length));
7482
g_assert_cmpstr(helper_directory, ==, "/tmp");
7483
g_assert_null(agent_directory);
7484
g_assert_true(user == 0);
7485
g_assert_true(group == 0);
7486
g_assert_null(mandos_argz);
7487
g_assert_true(mandos_argz_length == 0);
7489
for(char **arg = argv; *arg != NULL; arg++){
7495
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7496
test_fixture *fixture,
7497
__attribute__((unused))
7498
gconstpointer user_data){
7501
strdup("--plugin-helper-dir"),
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_cmpstr(helper_directory, ==, "/tmp");
7520
g_assert_null(agent_directory);
7521
g_assert_true(user == 0);
7522
g_assert_true(group == 0);
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_user(__attribute__((unused))
7532
test_fixture *fixture,
7533
__attribute__((unused))
7534
gconstpointer user_data){
7540
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7542
char *agent_directory = NULL;
7543
__attribute__((cleanup(cleanup_string)))
7544
char *helper_directory = NULL;
7547
__attribute__((cleanup(cleanup_string)))
7548
char *mandos_argz = NULL;
7549
size_t mandos_argz_length = 0;
7551
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7552
&helper_directory, &user, &group,
7553
&mandos_argz, &mandos_argz_length));
7555
g_assert_null(helper_directory);
7556
g_assert_null(agent_directory);
7557
g_assert_cmpuint((unsigned int)user, ==, 1000);
7558
g_assert_true(group == 0);
7559
g_assert_null(mandos_argz);
7560
g_assert_true(mandos_argz_length == 0);
7562
for(char **arg = argv; *arg != NULL; arg++){
7567
static void test_parse_arguments_user_invalid(__attribute__((unused))
7568
test_fixture *fixture,
7569
__attribute__((unused))
7577
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7579
char *agent_directory = NULL;
7580
__attribute__((cleanup(cleanup_string)))
7581
char *helper_directory = NULL;
7584
__attribute__((cleanup(cleanup_string)))
7585
char *mandos_argz = NULL;
7586
size_t mandos_argz_length = 0;
7588
g_assert_false(parse_arguments_devnull(argc, argv, false,
7590
&helper_directory, &user,
7591
&group, &mandos_argz,
7592
&mandos_argz_length));
7594
g_assert_null(helper_directory);
7595
g_assert_null(agent_directory);
7596
g_assert_cmpuint((unsigned int)user, ==, 0);
7597
g_assert_true(group == 0);
7598
g_assert_null(mandos_argz);
7599
g_assert_true(mandos_argz_length == 0);
7601
for(char **arg = argv; *arg != NULL; arg++){
7607
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7608
test_fixture *fixture,
7609
__attribute__((unused))
7610
gconstpointer user_data){
7616
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7618
char *agent_directory = NULL;
7619
__attribute__((cleanup(cleanup_string)))
7620
char *helper_directory = NULL;
7623
__attribute__((cleanup(cleanup_string)))
7624
char *mandos_argz = NULL;
7625
size_t mandos_argz_length = 0;
7627
g_assert_false(parse_arguments_devnull(argc, argv, false,
7629
&helper_directory, &user,
7630
&group, &mandos_argz,
7631
&mandos_argz_length));
7633
g_assert_null(helper_directory);
7634
g_assert_null(agent_directory);
7635
g_assert_cmpuint((unsigned int)user, ==, 0);
7636
g_assert_true(group == 0);
7637
g_assert_null(mandos_argz);
7638
g_assert_true(mandos_argz_length == 0);
7640
for(char **arg = argv; *arg != NULL; arg++){
7645
static void test_parse_arguments_group(__attribute__((unused))
7646
test_fixture *fixture,
7647
__attribute__((unused))
7648
gconstpointer user_data){
7654
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7656
char *agent_directory = NULL;
7657
__attribute__((cleanup(cleanup_string)))
7658
char *helper_directory = NULL;
7661
__attribute__((cleanup(cleanup_string)))
7662
char *mandos_argz = NULL;
7663
size_t mandos_argz_length = 0;
7665
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7666
&helper_directory, &user, &group,
7667
&mandos_argz, &mandos_argz_length));
7669
g_assert_null(helper_directory);
7670
g_assert_null(agent_directory);
7671
g_assert_true(user == 0);
7672
g_assert_cmpuint((unsigned int)group, ==, 1000);
7673
g_assert_null(mandos_argz);
7674
g_assert_true(mandos_argz_length == 0);
7676
for(char **arg = argv; *arg != NULL; arg++){
7681
static void test_parse_arguments_group_invalid(__attribute__((unused))
7682
test_fixture *fixture,
7683
__attribute__((unused))
7691
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7693
char *agent_directory = NULL;
7694
__attribute__((cleanup(cleanup_string)))
7695
char *helper_directory = NULL;
7698
__attribute__((cleanup(cleanup_string)))
7699
char *mandos_argz = NULL;
7700
size_t mandos_argz_length = 0;
7702
g_assert_false(parse_arguments_devnull(argc, argv, false,
7704
&helper_directory, &user,
7705
&group, &mandos_argz,
7706
&mandos_argz_length));
7708
g_assert_null(helper_directory);
7709
g_assert_null(agent_directory);
7710
g_assert_true(user == 0);
7711
g_assert_true(group == 0);
7712
g_assert_null(mandos_argz);
7713
g_assert_true(mandos_argz_length == 0);
7715
for(char **arg = argv; *arg != NULL; arg++){
7721
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7722
test_fixture *fixture,
7723
__attribute__((unused))
7724
gconstpointer user_data){
7730
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7732
char *agent_directory = NULL;
7733
__attribute__((cleanup(cleanup_string)))
7734
char *helper_directory = NULL;
7737
__attribute__((cleanup(cleanup_string)))
7738
char *mandos_argz = NULL;
7739
size_t mandos_argz_length = 0;
7741
g_assert_false(parse_arguments_devnull(argc, argv, false,
7743
&helper_directory, &user,
7744
&group, &mandos_argz,
7745
&mandos_argz_length));
7747
g_assert_null(helper_directory);
7748
g_assert_null(agent_directory);
7749
g_assert_cmpuint((unsigned int)group, ==, 0);
7750
g_assert_true(group == 0);
7751
g_assert_null(mandos_argz);
7752
g_assert_true(mandos_argz_length == 0);
7754
for(char **arg = argv; *arg != NULL; arg++){
7759
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7760
test_fixture *fixture,
7761
__attribute__((unused))
7766
strdup("mandos-client"),
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_null(agent_directory);
7785
g_assert_null(helper_directory);
7786
g_assert_true(user == 0);
7787
g_assert_true(group == 0);
7788
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7789
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7790
mandos_argz_length),
7793
for(char **arg = argv; *arg != NULL; arg++){
7798
static void test_parse_arguments_mandos_args(__attribute__((unused))
7799
test_fixture *fixture,
7800
__attribute__((unused))
7801
gconstpointer user_data){
7804
strdup("mandos-client"),
7809
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7811
__attribute__((cleanup(cleanup_string)))
7812
char *agent_directory = NULL;
7813
__attribute__((cleanup(cleanup_string)))
7814
char *helper_directory = NULL;
7817
__attribute__((cleanup(cleanup_string)))
7818
char *mandos_argz = NULL;
7819
size_t mandos_argz_length = 0;
7821
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7822
&helper_directory, &user, &group,
7823
&mandos_argz, &mandos_argz_length));
7825
g_assert_null(agent_directory);
7826
g_assert_null(helper_directory);
7827
g_assert_true(user == 0);
7828
g_assert_true(group == 0);
7829
char *marg = mandos_argz;
7830
g_assert_cmpstr(marg, ==, "mandos-client");
7831
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7832
g_assert_cmpstr(marg, ==, "one");
7833
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7834
g_assert_cmpstr(marg, ==, "two");
7835
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7836
g_assert_cmpstr(marg, ==, "three");
7837
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7838
mandos_argz_length),
7841
for(char **arg = argv; *arg != NULL; arg++){
7846
static void test_parse_arguments_all_args(__attribute__((unused))
7847
test_fixture *fixture,
7848
__attribute__((unused))
7849
gconstpointer user_data){
7852
strdup("--agent-directory"),
7854
strdup("--helper-directory"),
7860
strdup("mandos-client"),
7865
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7867
__attribute__((cleanup(cleanup_string)))
7868
char *agent_directory = NULL;
7869
__attribute__((cleanup(cleanup_string)))
7870
char *helper_directory = NULL;
7873
__attribute__((cleanup(cleanup_string)))
7874
char *mandos_argz = NULL;
7875
size_t mandos_argz_length = 0;
7877
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7878
&helper_directory, &user, &group,
7879
&mandos_argz, &mandos_argz_length));
7881
g_assert_cmpstr(agent_directory, ==, "/tmp");
7882
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7883
g_assert_true(user == 1);
7884
g_assert_true(group == 2);
7885
char *marg = mandos_argz;
7886
g_assert_cmpstr(marg, ==, "mandos-client");
7887
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7888
g_assert_cmpstr(marg, ==, "one");
7889
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7890
g_assert_cmpstr(marg, ==, "two");
7891
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7892
g_assert_cmpstr(marg, ==, "three");
7893
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7894
mandos_argz_length),
7897
for(char **arg = argv; *arg != NULL; arg++){
7902
static void test_parse_arguments_mixed(__attribute__((unused))
7903
test_fixture *fixture,
7904
__attribute__((unused))
7905
gconstpointer user_data){
7908
strdup("mandos-client"),
7912
strdup("--agent-directory"),
7916
strdup("--helper-directory=/var/tmp"),
7918
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7920
__attribute__((cleanup(cleanup_string)))
7921
char *agent_directory = NULL;
7922
__attribute__((cleanup(cleanup_string)))
7923
char *helper_directory = NULL;
7926
__attribute__((cleanup(cleanup_string)))
7927
char *mandos_argz = NULL;
7928
size_t mandos_argz_length = 0;
7930
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7931
&helper_directory, &user, &group,
7932
&mandos_argz, &mandos_argz_length));
7934
g_assert_cmpstr(agent_directory, ==, "/tmp");
7935
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7936
g_assert_true(user == 1);
7937
g_assert_true(group == 0);
7938
char *marg = mandos_argz;
7939
g_assert_cmpstr(marg, ==, "mandos-client");
7940
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7941
g_assert_cmpstr(marg, ==, "one");
7942
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7943
g_assert_cmpstr(marg, ==, "two");
7944
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7945
g_assert_cmpstr(marg, ==, "three");
7946
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7947
mandos_argz_length),
7950
for(char **arg = argv; *arg != NULL; arg++){
7955
/* End of tests section */
7957
/* Test boilerplate section; New tests should be added to the test
7958
suite definition here, in the "run_tests" function.
7960
Finally, this section also contains the should_only_run_tests()
7961
function used by main() for deciding if tests should be run or to
7964
__attribute__((cold))
7965
static bool run_tests(int argc, char *argv[]){
7966
g_test_init(&argc, &argv, NULL);
7968
/* A macro to add a test with no setup or teardown functions */
7969
#define test_add(testpath, testfunc) \
7971
g_test_add((testpath), test_fixture, NULL, NULL, \
7972
(testfunc), NULL); \
7975
/* Test the signal-related functions first, since some other tests
7976
depend on these functions in their setups and teardowns */
7977
test_add("/signal-handling/setup", test_setup_signal_handler);
7978
test_add("/signal-handling/restore", test_restore_signal_handler);
7979
test_add("/signal-handling/block", test_block_sigchld);
7980
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7982
/* Regular non-signal-related tests; these use no setups or
7984
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7985
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7986
test_add("/parse_arguments/long-dir",
7987
test_parse_arguments_long_dir);
7988
test_add("/parse_arguments/short-dir",
7989
test_parse_arguments_short_dir);
7990
test_add("/parse_arguments/helper-directory",
7991
test_parse_arguments_helper_directory);
7992
test_add("/parse_arguments/plugin-helper-dir",
7993
test_parse_arguments_plugin_helper_dir);
7994
test_add("/parse_arguments/user", test_parse_arguments_user);
7995
test_add("/parse_arguments/user-invalid",
7996
test_parse_arguments_user_invalid);
7997
test_add("/parse_arguments/user-zero-invalid",
7998
test_parse_arguments_user_zero_invalid);
7999
test_add("/parse_arguments/group", test_parse_arguments_group);
8000
test_add("/parse_arguments/group-invalid",
8001
test_parse_arguments_group_invalid);
8002
test_add("/parse_arguments/group-zero-invalid",
8003
test_parse_arguments_group_zero_invalid);
8004
test_add("/parse_arguments/mandos-noargs",
8005
test_parse_arguments_mandos_noargs);
8006
test_add("/parse_arguments/mandos-args",
8007
test_parse_arguments_mandos_args);
8008
test_add("/parse_arguments/all-args",
8009
test_parse_arguments_all_args);
8010
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
8011
test_add("/queue/create", test_create_queue);
8012
test_add("/queue/add", test_add_to_queue);
8013
test_add("/queue/add/overflow", test_add_to_queue_overflow);
8014
test_add("/queue/has_question/empty",
8015
test_queue_has_question_empty);
8016
test_add("/queue/has_question/false",
8017
test_queue_has_question_false);
8018
test_add("/queue/has_question/true", test_queue_has_question_true);
8019
test_add("/queue/has_question/false2",
8020
test_queue_has_question_false2);
8021
test_add("/queue/has_question/true2",
8022
test_queue_has_question_true2);
8023
test_add("/buffer/cleanup", test_cleanup_buffer);
8024
test_add("/string_set/net-set-contains-nothing",
8025
test_string_set_new_set_contains_nothing);
8026
test_add("/string_set/with-added-string-contains-it",
8027
test_string_set_with_added_string_contains_it);
8028
test_add("/string_set/cleared-does-not-contain-string",
8029
test_string_set_cleared_does_not_contain_str);
8030
test_add("/string_set/swap/one-with-empty",
8031
test_string_set_swap_one_with_empty);
8032
test_add("/string_set/swap/empty-with-one",
8033
test_string_set_swap_empty_with_one);
8034
test_add("/string_set/swap/one-with-one",
8035
test_string_set_swap_one_with_one);
8037
/* A macro to add a test using the setup and teardown functions */
8038
#define test_add_st(path, func) \
8040
g_test_add((path), test_fixture, NULL, test_setup, (func), \
8044
/* Signal-related tests; these use setups and teardowns which
8045
establish, during each test run, a signal handler for, and a
8046
signal mask blocking, the SIGCHLD signal, just like main() */
8047
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
8048
test_add_st("/wait_for_event/event", test_wait_for_event_event);
8049
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
8050
test_add_st("/run_queue/zeroes-next-run",
8051
test_run_queue_zeroes_next_run);
8052
test_add_st("/run_queue/clears-cancelled_filenames",
8053
test_run_queue_clears_cancelled_filenames);
8054
test_add_st("/run_queue/skips-cancelled-filenames",
8055
test_run_queue_skips_cancelled_filenames);
8056
test_add_st("/run_queue/one-task", test_run_queue_one_task);
8057
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
8058
test_add_st("/run_queue/two-tasks/quit",
8059
test_run_queue_two_tasks_quit);
8060
test_add_st("/run_queue/two-tasks-cleanup",
8061
test_run_queue_two_tasks_cleanup);
8062
test_add_st("/task-creators/start_mandos_client",
8063
test_start_mandos_client);
8064
test_add_st("/task-creators/start_mandos_client/execv",
8065
test_start_mandos_client_execv);
8066
test_add_st("/task-creators/start_mandos_client/suid/euid",
8067
test_start_mandos_client_suid_euid);
8068
test_add_st("/task-creators/start_mandos_client/suid/egid",
8069
test_start_mandos_client_suid_egid);
8070
test_add_st("/task-creators/start_mandos_client/suid/ruid",
8071
test_start_mandos_client_suid_ruid);
8072
test_add_st("/task-creators/start_mandos_client/suid/rgid",
8073
test_start_mandos_client_suid_rgid);
8074
test_add_st("/task-creators/start_mandos_client/read",
8075
test_start_mandos_client_read);
8076
test_add_st("/task-creators/start_mandos_client/helper-directory",
8077
test_start_mandos_client_helper_directory);
8078
test_add_st("/task-creators/start_mandos_client/sigmask",
8079
test_start_mandos_client_sigmask);
8080
test_add_st("/task/wait_for_mandos_client_exit/badpid",
8081
test_wait_for_mandos_client_exit_badpid);
8082
test_add_st("/task/wait_for_mandos_client_exit/noexit",
8083
test_wait_for_mandos_client_exit_noexit);
8084
test_add_st("/task/wait_for_mandos_client_exit/success",
8085
test_wait_for_mandos_client_exit_success);
8086
test_add_st("/task/wait_for_mandos_client_exit/failure",
8087
test_wait_for_mandos_client_exit_failure);
8088
test_add_st("/task/wait_for_mandos_client_exit/killed",
8089
test_wait_for_mandos_client_exit_killed);
8090
test_add_st("/task/read_mandos_client_output/readerror",
8091
test_read_mandos_client_output_readerror);
8092
test_add_st("/task/read_mandos_client_output/nodata",
8093
test_read_mandos_client_output_nodata);
8094
test_add_st("/task/read_mandos_client_output/eof",
8095
test_read_mandos_client_output_eof);
8096
test_add_st("/task/read_mandos_client_output/once",
8097
test_read_mandos_client_output_once);
8098
test_add_st("/task/read_mandos_client_output/malloc",
8099
test_read_mandos_client_output_malloc);
8100
test_add_st("/task/read_mandos_client_output/append",
8101
test_read_mandos_client_output_append);
8102
test_add_st("/task-creators/add_inotify_dir_watch",
8103
test_add_inotify_dir_watch);
8104
test_add_st("/task-creators/add_inotify_dir_watch/fail",
8105
test_add_inotify_dir_watch_fail);
8106
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8107
test_add_inotify_dir_watch_nondir);
8108
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8109
test_add_inotify_dir_watch_EAGAIN);
8110
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8111
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8112
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8113
test_add_inotify_dir_watch_IN_MOVED_TO);
8114
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8115
test_add_inotify_dir_watch_IN_MOVED_FROM);
8116
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8117
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8118
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8119
test_add_inotify_dir_watch_IN_DELETE);
8120
test_add_st("/task/read_inotify_event/readerror",
8121
test_read_inotify_event_readerror);
8122
test_add_st("/task/read_inotify_event/bad-epoll",
8123
test_read_inotify_event_bad_epoll);
8124
test_add_st("/task/read_inotify_event/nodata",
8125
test_read_inotify_event_nodata);
8126
test_add_st("/task/read_inotify_event/eof",
8127
test_read_inotify_event_eof);
8128
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8129
test_read_inotify_event_IN_CLOSE_WRITE);
8130
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8131
test_read_inotify_event_IN_MOVED_TO);
8132
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8133
test_read_inotify_event_IN_MOVED_FROM);
8134
test_add_st("/task/read_inotify_event/IN_DELETE",
8135
test_read_inotify_event_IN_DELETE);
8136
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8137
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8138
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8139
test_read_inotify_event_IN_MOVED_TO_badname);
8140
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8141
test_read_inotify_event_IN_MOVED_FROM_badname);
8142
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8143
test_read_inotify_event_IN_DELETE_badname);
8144
test_add_st("/task/open_and_parse_question/ENOENT",
8145
test_open_and_parse_question_ENOENT);
8146
test_add_st("/task/open_and_parse_question/EIO",
8147
test_open_and_parse_question_EIO);
8148
test_add_st("/task/open_and_parse_question/parse-error",
8149
test_open_and_parse_question_parse_error);
8150
test_add_st("/task/open_and_parse_question/nosocket",
8151
test_open_and_parse_question_nosocket);
8152
test_add_st("/task/open_and_parse_question/badsocket",
8153
test_open_and_parse_question_badsocket);
8154
test_add_st("/task/open_and_parse_question/nopid",
8155
test_open_and_parse_question_nopid);
8156
test_add_st("/task/open_and_parse_question/badpid",
8157
test_open_and_parse_question_badpid);
8158
test_add_st("/task/open_and_parse_question/noexist_pid",
8159
test_open_and_parse_question_noexist_pid);
8160
test_add_st("/task/open_and_parse_question/no-notafter",
8161
test_open_and_parse_question_no_notafter);
8162
test_add_st("/task/open_and_parse_question/bad-notafter",
8163
test_open_and_parse_question_bad_notafter);
8164
test_add_st("/task/open_and_parse_question/notafter-0",
8165
test_open_and_parse_question_notafter_0);
8166
test_add_st("/task/open_and_parse_question/notafter-1",
8167
test_open_and_parse_question_notafter_1);
8168
test_add_st("/task/open_and_parse_question/notafter-1-1",
8169
test_open_and_parse_question_notafter_1_1);
8170
test_add_st("/task/open_and_parse_question/notafter-1-2",
8171
test_open_and_parse_question_notafter_1_2);
8172
test_add_st("/task/open_and_parse_question/equal-notafter",
8173
test_open_and_parse_question_equal_notafter);
8174
test_add_st("/task/open_and_parse_question/late-notafter",
8175
test_open_and_parse_question_late_notafter);
8176
test_add_st("/task/cancel_old_question/0-1-2",
8177
test_cancel_old_question_0_1_2);
8178
test_add_st("/task/cancel_old_question/0-2-1",
8179
test_cancel_old_question_0_2_1);
8180
test_add_st("/task/cancel_old_question/1-2-3",
8181
test_cancel_old_question_1_2_3);
8182
test_add_st("/task/cancel_old_question/1-3-2",
8183
test_cancel_old_question_1_3_2);
8184
test_add_st("/task/cancel_old_question/2-1-3",
8185
test_cancel_old_question_2_1_3);
8186
test_add_st("/task/cancel_old_question/2-3-1",
8187
test_cancel_old_question_2_3_1);
8188
test_add_st("/task/cancel_old_question/3-1-2",
8189
test_cancel_old_question_3_1_2);
8190
test_add_st("/task/cancel_old_question/3-2-1",
8191
test_cancel_old_question_3_2_1);
8192
test_add_st("/task/connect_question_socket/name-too-long",
8193
test_connect_question_socket_name_too_long);
8194
test_add_st("/task/connect_question_socket/connect-fail",
8195
test_connect_question_socket_connect_fail);
8196
test_add_st("/task/connect_question_socket/bad-epoll",
8197
test_connect_question_socket_bad_epoll);
8198
test_add_st("/task/connect_question_socket/usable",
8199
test_connect_question_socket_usable);
8200
test_add_st("/task/send_password_to_socket/client-not-exited",
8201
test_send_password_to_socket_client_not_exited);
8202
test_add_st("/task/send_password_to_socket/password-not-read",
8203
test_send_password_to_socket_password_not_read);
8204
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8205
test_send_password_to_socket_EMSGSIZE);
8206
test_add_st("/task/send_password_to_socket/retry",
8207
test_send_password_to_socket_retry);
8208
test_add_st("/task/send_password_to_socket/bad-epoll",
8209
test_send_password_to_socket_bad_epoll);
8210
test_add_st("/task/send_password_to_socket/null-password",
8211
test_send_password_to_socket_null_password);
8212
test_add_st("/task/send_password_to_socket/empty-password",
8213
test_send_password_to_socket_empty_password);
8214
test_add_st("/task/send_password_to_socket/empty-str-password",
8215
test_send_password_to_socket_empty_str_pass);
8216
test_add_st("/task/send_password_to_socket/text-password",
8217
test_send_password_to_socket_text_password);
8218
test_add_st("/task/send_password_to_socket/binary-password",
8219
test_send_password_to_socket_binary_password);
8220
test_add_st("/task/send_password_to_socket/nuls-in-password",
8221
test_send_password_to_socket_nuls_in_password);
8222
test_add_st("/task-creators/add_existing_questions/ENOENT",
8223
test_add_existing_questions_ENOENT);
8224
test_add_st("/task-creators/add_existing_questions/no-questions",
8225
test_add_existing_questions_no_questions);
8226
test_add_st("/task-creators/add_existing_questions/one-question",
8227
test_add_existing_questions_one_question);
8228
test_add_st("/task-creators/add_existing_questions/two-questions",
8229
test_add_existing_questions_two_questions);
8230
test_add_st("/task-creators/add_existing_questions/non-questions",
8231
test_add_existing_questions_non_questions);
8232
test_add_st("/task-creators/add_existing_questions/both-types",
8233
test_add_existing_questions_both_types);
8235
return g_test_run() == 0;
8238
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8239
GOptionContext *context = g_option_context_new("");
8241
g_option_context_set_help_enabled(context, FALSE);
8242
g_option_context_set_ignore_unknown_options(context, TRUE);
8244
gboolean should_run_tests = FALSE;
8245
GOptionEntry entries[] = {
8246
{ "test", 0, 0, G_OPTION_ARG_NONE,
8247
&should_run_tests, "Run tests", NULL },
8250
g_option_context_add_main_entries(context, entries, NULL);
8252
GError *error = NULL;
8254
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8255
g_option_context_free(context);
8256
g_error("Failed to parse options: %s", error->message);
8259
g_option_context_free(context);
8260
return should_run_tests != FALSE;
8267
(if (not (funcall run-tests-in-test-buffer default-directory))
8268
(funcall show-test-buffer-in-test-window)
8269
(funcall remove-test-window)))
8270
run-tests-in-test-buffer:
8272
(with-current-buffer (get-buffer-create "*Test*")
8273
(setq buffer-read-only nil
8274
default-directory dir)
8277
(let ((process-result
8278
(let ((inhibit-read-only t))
8279
(process-file-shell-command
8280
(funcall get-command-line) nil "*Test*"))))
8281
(and (numberp process-result)
8282
(= process-result 0))))
8287
(funcall find-build-directory (buffer-file-name)))
8288
(local-build-directory
8289
(if (fboundp 'file-local-name)
8290
(file-local-name build-directory)
8291
(or (file-remote-p build-directory 'localname)
8294
(file-relative-name (file-name-sans-extension
8295
(buffer-file-name)) build-directory))
8296
(qbdir (shell-quote-argument local-build-directory))
8297
(qcmd (shell-quote-argument command)))
8298
(format (concat "cd %s && CFLAGS=-Werror make --silent %s"
8299
" && %s --test --verbose") qbdir qcmd qcmd)))
8300
find-build-directory:
8301
(lambda (try-directory &optional base-directory)
8302
(let ((base-directory (or base-directory try-directory)))
8303
(cond ((equal try-directory "/") base-directory)
8305
(concat (file-name-as-directory try-directory)
8306
"Makefile")) try-directory)
8307
((funcall find-build-directory
8308
(directory-file-name (file-name-directory
8311
show-test-buffer-in-test-window:
8313
(when (not (get-buffer-window-list "*Test*"))
8314
(setq next-error-last-buffer (get-buffer "*Test*"))
8315
(let* ((side (if (>= (window-width) 146) 'right 'bottom))
8316
(display-buffer-overriding-action
8317
`((display-buffer-in-side-window) (side . ,side)
8318
(window-height . fit-window-to-buffer)
8319
(window-width . fit-window-to-buffer))))
8320
(display-buffer "*Test*"))))
8323
(let ((test-window (get-buffer-window "*Test*")))
8324
(if test-window (delete-window test-window))))
8325
eval: (add-hook 'after-save-hook run-tests 90 t)