1
 
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
 
3
 
 * Mandos password agent - Simple password agent to run Mandos client
 
5
 
 * Copyright © 2019 Teddy Hogeborn
 
6
 
 * Copyright © 2019 Björn Påhlsson
 
8
 
 * This file is part of Mandos.
 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 
 * under the terms of the GNU General Public License as published by
 
12
 
 * the Free Software Foundation, either version 3 of the License, or
 
13
 
 * (at your option) any later version.
 
15
 
 * Mandos is distributed in the hope that it will be useful, but
 
16
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 
 * General Public License for more details.
 
20
 
 * You should have received a copy of the GNU General Public License
 
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
 
28
 
                                   intmax_t, uint32_t, SCNx32,
 
30
 
#include <stddef.h>             /* size_t */
 
31
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
 
33
 
#include <stdbool.h>            /* bool, true, false */
 
34
 
#include <signal.h>             /* struct sigaction, sigset_t,
 
35
 
                                   sigemptyset(), sigaddset(),
 
36
 
                                   SIGCHLD, pthread_sigmask(),
 
37
 
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
 
38
 
                                   SA_NOCLDSTOP, sigfillset(), kill(),
 
39
 
                                   SIGTERM, sigdelset(), SIGKILL,
 
40
 
                                   NSIG, sigismember(), SA_ONSTACK,
 
41
 
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
 
42
 
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
43
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
44
 
                                   malloc(), free(), strtoumax(),
 
45
 
                                   realloc(), setenv(), calloc(),
 
46
 
                                   mkdtemp(), mkostemp() */
 
47
 
#include <iso646.h>             /* not, or, and, xor */
 
48
 
#include <error.h>              /* error() */
 
49
 
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
 
50
 
#include <errno.h>              /* errno, error_t, EACCES,
 
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
 
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
 
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
 
54
 
                                   ECONNREFUSED, ECONNRESET,
 
55
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
 
57
 
#include <string.h>             /* strdup(), memcpy(),
 
58
 
                                   explicit_bzero(), memset(),
 
59
 
                                   strcmp(), strlen(), strncpy(),
 
60
 
                                   memcmp(), basename() */
 
61
 
#include <argz.h>               /* argz_create(), argz_count(),
 
62
 
                                   argz_extract(), argz_next(),
 
64
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
 
65
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
 
66
 
                                   struct epoll_event, EPOLLIN,
 
69
 
#include <time.h>               /* struct timespec, clock_gettime(),
 
71
 
#include <argp.h>               /* struct argp_option, OPTION_HIDDEN,
 
72
 
                                   OPTION_ALIAS, struct argp_state,
 
73
 
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
 
74
 
                                   struct argp, argp_parse(),
 
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
77
 
                                   fork(), _exit(), dup2(),
 
78
 
                                   STDOUT_FILENO, setresgid(),
 
79
 
                                   setresuid(), execv(), ssize_t,
 
80
 
                                   read(), dup3(), getuid(), dup(),
 
81
 
                                   STDERR_FILENO, pause(), write(),
 
82
 
                                   rmdir(), unlink(), getpid() */
 
83
 
#include <sys/mman.h>           /* munlock(), mlock() */
 
84
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
85
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
86
 
                                   open(), O_WRONLY, O_RDONLY */
 
87
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
89
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
90
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
91
 
                                   IN_CLOEXEC, inotify_add_watch(),
 
92
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
93
 
                                   IN_DELETE, struct inotify_event */
 
94
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
95
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
96
 
                                   getline(), sscanf(), feof(),
 
97
 
                                   ferror(), fclose(), stderr,
 
98
 
                                   rename(), fdopen(), fprintf(),
 
100
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
 
101
 
                        GError, g_key_file_load_from_file(),
 
102
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
 
103
 
                        g_key_file_get_string(), guint64,
 
104
 
                        g_key_file_get_uint64(),
 
105
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
 
106
 
                        g_assert_true(), g_assert_nonnull(),
 
107
 
                        g_assert_null(), g_assert_false(),
 
108
 
                        g_assert_cmpint(), g_assert_cmpuint(),
 
109
 
                        g_test_skip(), g_assert_cmpstr(),
 
110
 
                        g_test_init(), g_test_add(), g_test_run(),
 
111
 
                        GOptionContext, g_option_context_new(),
 
112
 
                        g_option_context_set_help_enabled(), FALSE,
 
113
 
                        g_option_context_set_ignore_unknown_options(),
 
114
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
 
115
 
                        g_option_context_add_main_entries(),
 
116
 
                        g_option_context_parse(),
 
117
 
                        g_option_context_free(), g_error() */
 
118
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
 
119
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
 
120
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
 
121
 
                                   SOCK_CLOEXEC, connect(),
 
122
 
                                   struct sockaddr, socklen_t,
 
123
 
                                   shutdown(), SHUT_RD, send(),
 
124
 
                                   MSG_NOSIGNAL, bind(), recv(),
 
126
 
#include <glob.h>               /* globfree(), glob_t, glob(),
 
127
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
 
128
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
 
131
 
/* End of includes */
 
133
 
/* Start of declarations of private types and functions */
 
135
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
 
136
 
typedef uintmax_t mono_microsecs;
 
138
 
/* "task_queue" - A queue of tasks to be run */
 
140
 
  struct task_struct *tasks;    /* Tasks in this queue */
 
141
 
  size_t length;                /* Number of tasks */
 
142
 
  /* Memory allocated for "tasks", in bytes */
 
144
 
  /* Time when this queue should be run, at the latest */
 
145
 
  mono_microsecs next_run;
 
146
 
} __attribute__((designated_init)) task_queue;
 
148
 
/* "func_type" - A function type for task functions
 
150
 
   I.e. functions for the code which runs when a task is run, all have
 
152
 
typedef void (task_func) (const struct task_struct,
 
154
 
  __attribute__((nonnull));
 
156
 
/* "buffer" - A data buffer for a growing array of bytes
 
158
 
   Used for the "password" variable */
 
163
 
} __attribute__((designated_init)) buffer;
 
165
 
/* "string_set" - A set type which can contain strings
 
167
 
   Used by the "cancelled_filenames" variable */
 
169
 
  char *argz;                   /* Do not access these except in */
 
170
 
  size_t argz_len;              /* the string_set_* functions */
 
171
 
} __attribute__((designated_init)) string_set;
 
173
 
/* "task_context" - local variables for tasks
 
175
 
   This data structure distinguishes between different tasks which are
 
176
 
   using the same function.  This data structure is passed to every
 
177
 
   task function when each task is run.
 
179
 
   Note that not every task uses every struct member. */
 
180
 
typedef struct task_struct {
 
181
 
  task_func *const func;         /* The function run by this task */
 
182
 
  char *const question_filename; /* The question file */
 
183
 
  const pid_t pid;               /* Mandos client process ID */
 
184
 
  const int epoll_fd;            /* The epoll set file descriptor */
 
185
 
  bool *const quit_now;          /* Set to true on fatal errors */
 
186
 
  const int fd;                  /* General purpose file descriptor */
 
187
 
  bool *const mandos_client_exited; /* Set true when client exits */
 
188
 
  buffer *const password;           /* As read from client process */
 
189
 
  bool *const password_is_read;     /* "password" is done growing */
 
190
 
  char *filename;                   /* General purpose file name */
 
191
 
  /* A set of strings of all the file names of questions which have
 
192
 
     been cancelled for any reason; tasks pertaining to these question
 
193
 
     files should not be run */
 
194
 
  string_set *const cancelled_filenames;
 
195
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
 
196
 
  /* Updated before each queue run; is compared with queue.next_run */
 
197
 
  const mono_microsecs *const current_time;
 
198
 
} __attribute__((designated_init)) task_context;
 
200
 
/* Declare all our functions here so we can define them in any order
 
201
 
   below.  Note: test functions are *not* declared here, they are
 
202
 
   declared in the test section. */
 
203
 
__attribute__((warn_unused_result))
 
204
 
static bool should_only_run_tests(int *, char **[]);
 
205
 
__attribute__((warn_unused_result, cold))
 
206
 
static bool run_tests(int, char *[]);
 
207
 
static void handle_sigchld(__attribute__((unused)) int sig){}
 
208
 
__attribute__((warn_unused_result, malloc))
 
209
 
task_queue *create_queue(void);
 
210
 
__attribute__((nonnull, warn_unused_result))
 
211
 
bool add_to_queue(task_queue *const, const task_context);
 
212
 
__attribute__((nonnull))
 
213
 
void cleanup_task(const task_context *const);
 
214
 
__attribute__((nonnull))
 
215
 
void cleanup_queue(task_queue *const *const);
 
216
 
__attribute__((pure, nonnull, warn_unused_result))
 
217
 
bool queue_has_question(const task_queue *const);
 
218
 
__attribute__((nonnull))
 
219
 
void cleanup_close(const int *const);
 
220
 
__attribute__((nonnull))
 
221
 
void cleanup_string(char *const *const);
 
222
 
__attribute__((nonnull))
 
223
 
void cleanup_buffer(buffer *const);
 
224
 
__attribute__((pure, nonnull, warn_unused_result))
 
225
 
bool string_set_contains(const string_set, const char *const);
 
226
 
__attribute__((nonnull, warn_unused_result))
 
227
 
bool string_set_add(string_set *const, const char *const);
 
228
 
__attribute__((nonnull))
 
229
 
void string_set_clear(string_set *);
 
230
 
void string_set_swap(string_set *const, string_set *const);
 
231
 
__attribute__((nonnull, warn_unused_result))
 
232
 
bool start_mandos_client(task_queue *const, const int, bool *const,
 
233
 
                         bool *const, buffer *const, bool *const,
 
234
 
                         const struct sigaction *const,
 
235
 
                         const sigset_t, const char *const,
 
236
 
                         const uid_t, const gid_t,
 
237
 
                         const char *const *const);
 
238
 
__attribute__((nonnull))
 
239
 
task_func wait_for_mandos_client_exit;
 
240
 
__attribute__((nonnull))
 
241
 
task_func read_mandos_client_output;
 
242
 
__attribute__((warn_unused_result))
 
243
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
 
244
 
                           buffer *const, const char *const,
 
245
 
                           string_set *, const mono_microsecs *const,
 
246
 
                           bool *const, bool *const);
 
247
 
__attribute__((nonnull))
 
248
 
task_func read_inotify_event;
 
249
 
__attribute__((nonnull))
 
250
 
task_func open_and_parse_question;
 
251
 
__attribute__((nonnull))
 
252
 
task_func cancel_old_question;
 
253
 
__attribute__((nonnull))
 
254
 
task_func connect_question_socket;
 
255
 
__attribute__((nonnull))
 
256
 
task_func send_password_to_socket;
 
257
 
__attribute__((warn_unused_result))
 
258
 
bool add_existing_questions(task_queue *const, const int,
 
259
 
                            buffer *const, string_set *,
 
260
 
                            const mono_microsecs *const,
 
261
 
                            bool *const, bool *const,
 
263
 
__attribute__((nonnull, warn_unused_result))
 
264
 
bool wait_for_event(const int, const mono_microsecs,
 
265
 
                    const mono_microsecs);
 
266
 
bool run_queue(task_queue **const, string_set *const, bool *const);
 
267
 
bool clear_all_fds_from_epoll_set(const int);
 
268
 
mono_microsecs get_current_time(void);
 
269
 
__attribute__((nonnull, warn_unused_result))
 
270
 
bool setup_signal_handler(struct sigaction *const);
 
271
 
__attribute__((nonnull))
 
272
 
bool restore_signal_handler(const struct sigaction *const);
 
273
 
__attribute__((nonnull, warn_unused_result))
 
274
 
bool block_sigchld(sigset_t *const);
 
275
 
__attribute__((nonnull))
 
276
 
bool restore_sigmask(const sigset_t *const);
 
277
 
__attribute__((nonnull))
 
278
 
bool parse_arguments(int, char *[], const bool, char **, char **,
 
279
 
                     uid_t *const , gid_t *const, char **, size_t *);
 
281
 
/* End of declarations of private types and functions */
 
283
 
/* Start of "main" section; this section LACKS TESTS!
 
285
 
   Code here should be as simple as possible. */
 
287
 
/* These are required to be global by Argp */
 
288
 
const char *argp_program_version = "password-agent " VERSION;
 
289
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
291
 
int main(int argc, char *argv[]){
 
293
 
  /* If the --test option is passed, skip all normal operations and
 
294
 
     instead only run the run_tests() function, which also does all
 
295
 
     its own option parsing, so we don't have to do anything here. */
 
296
 
  if(should_only_run_tests(&argc, &argv)){
 
297
 
    if(run_tests(argc, argv)){
 
298
 
      return EXIT_SUCCESS;      /* All tests successful */
 
300
 
    return EXIT_FAILURE;        /* Some test(s) failed */
 
303
 
  __attribute__((cleanup(cleanup_string)))
 
304
 
    char *agent_directory = NULL;
 
306
 
  __attribute__((cleanup(cleanup_string)))
 
307
 
    char *helper_directory = NULL;
 
312
 
  __attribute__((cleanup(cleanup_string)))
 
313
 
    char *mandos_argz = NULL;
 
314
 
  size_t mandos_argz_length = 0;
 
316
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
 
317
 
                         &helper_directory, &user, &group,
 
318
 
                         &mandos_argz, &mandos_argz_length)){
 
319
 
    /* This should never happen, since "true" is passed as the third
 
320
 
       argument to parse_arguments() above, which should make
 
321
 
       argp_parse() call exit() if any parsing error occurs. */
 
322
 
    error(EX_USAGE, errno, "Failed to parse arguments");
 
325
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
 
326
 
  const char default_helper_directory[]
 
327
 
    = "/lib/mandos/plugin-helpers";
 
328
 
  const char *const default_argv[]
 
329
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
 
331
 
  /* Set variables to default values if unset */
 
332
 
  if(agent_directory == NULL){
 
333
 
    agent_directory = strdup(default_agent_directory);
 
334
 
    if(agent_directory == NULL){
 
335
 
      error(EX_OSERR, errno, "Failed strdup()");
 
338
 
  if(helper_directory == NULL){
 
339
 
    helper_directory = strdup(default_helper_directory);
 
340
 
    if(helper_directory == NULL){
 
341
 
      error(EX_OSERR, errno, "Failed strdup()");
 
345
 
    user = 65534;               /* nobody */
 
348
 
    group = 65534;              /* nogroup */
 
350
 
  /* If parse_opt did not create an argz vector, create one with
 
352
 
  if(mandos_argz == NULL){
 
354
 
#pragma GCC diagnostic push
 
355
 
    /* argz_create() takes a non-const argv for some unknown reason -
 
356
 
       argz_create() isn't modifying the strings, just copying them.
 
357
 
       Therefore, this cast to non-const should be safe. */
 
358
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
360
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
 
361
 
                        &mandos_argz_length);
 
363
 
#pragma GCC diagnostic pop
 
366
 
      error(EX_OSERR, errno, "Failed argz_create()");
 
369
 
  /* Use argz vector to create a normal argv, usable by execv() */
 
371
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
 
373
 
                               + 1) * sizeof(char *));
 
374
 
  if(mandos_argv == NULL){
 
375
 
    error_t saved_errno = errno;
 
377
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
 
379
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
 
381
 
  sigset_t orig_sigmask;
 
382
 
  if(not block_sigchld(&orig_sigmask)){
 
386
 
  struct sigaction old_sigchld_action;
 
387
 
  if(not setup_signal_handler(&old_sigchld_action)){
 
391
 
  mono_microsecs current_time = 0;
 
393
 
  bool mandos_client_exited = false;
 
394
 
  bool quit_now = false;
 
395
 
  __attribute__((cleanup(cleanup_close)))
 
396
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
398
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
 
400
 
  __attribute__((cleanup(cleanup_queue)))
 
401
 
    task_queue *queue = create_queue();
 
403
 
    error(EX_OSERR, errno, "Failed to create task queue");
 
406
 
  __attribute__((cleanup(cleanup_buffer)))
 
407
 
    buffer password = {};
 
408
 
  bool password_is_read = false;
 
410
 
  __attribute__((cleanup(string_set_clear)))
 
411
 
    string_set cancelled_filenames = {};
 
413
 
  /* Add tasks to queue */
 
414
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
 
415
 
                             &quit_now, &password, &password_is_read,
 
416
 
                             &old_sigchld_action, orig_sigmask,
 
417
 
                             helper_directory, user, group,
 
418
 
                             (const char *const *)mandos_argv)){
 
419
 
    return EX_OSERR;            /* Error has already been printed */
 
421
 
  /* These variables were only for start_mandos_client() and are not
 
426
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
 
427
 
                               agent_directory, &cancelled_filenames,
 
428
 
                               ¤t_time, &mandos_client_exited,
 
430
 
    switch(errno){              /* Error has already been printed */
 
439
 
  if(not add_existing_questions(queue, epoll_fd, &password,
 
440
 
                                &cancelled_filenames, ¤t_time,
 
441
 
                                &mandos_client_exited,
 
442
 
                                &password_is_read, agent_directory)){
 
443
 
    return EXIT_FAILURE;        /* Error has already been printed */
 
448
 
    current_time = get_current_time();
 
449
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
 
450
 
      const error_t saved_errno = errno;
 
451
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
 
455
 
    current_time = get_current_time();
 
456
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
 
457
 
      const error_t saved_errno = errno;
 
458
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
 
461
 
    /*  When no tasks about questions are left in the queue, break out
 
462
 
        of the loop (and implicitly exit the program) */
 
463
 
  } while(queue_has_question(queue));
 
465
 
  restore_signal_handler(&old_sigchld_action);
 
466
 
  restore_sigmask(&orig_sigmask);
 
471
 
__attribute__((warn_unused_result))
 
472
 
mono_microsecs get_current_time(void){
 
473
 
  struct timespec currtime;
 
474
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
 
475
 
    error(0, errno, "Failed to get current time");
 
478
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
 
479
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
 
482
 
/* End of "main" section */
 
484
 
/* Start of regular code section; ALL this code has tests */
 
486
 
__attribute__((nonnull))
 
487
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
 
488
 
                     char **agent_directory, char **helper_directory,
 
489
 
                     uid_t *const user, gid_t *const group,
 
490
 
                     char **mandos_argz, size_t *mandos_argz_length){
 
492
 
  const struct argp_option options[] = {
 
493
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
 
494
 
      .doc="Systemd password agent directory" },
 
495
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
 
496
 
      .doc="Mandos Client password helper directory" },
 
497
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
 
498
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
499
 
    { .name="user", .key='u', .arg="USERID",
 
500
 
      .doc="User ID the Mandos Client will use as its unprivileged"
 
502
 
    { .name="userid", .key=130, /* From plugin--runner */
 
503
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
504
 
    { .name="group", .key='g', .arg="GROUPID",
 
505
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
 
507
 
    { .name="groupid", .key=131, /* From plugin--runner */
 
508
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
509
 
    { .name="test", .key=255, /* See should_only_run_tests() */
 
510
 
      .doc="Skip normal operation, and only run self-tests.  See"
 
511
 
      " --test --help.", .group=10, },
 
515
 
  __attribute__((nonnull(3)))
 
516
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
 
519
 
    case 'd':                   /* --agent-directory */
 
520
 
      *agent_directory = strdup(arg);
 
522
 
    case 128:                   /* --helper-directory */
 
523
 
    case 129:                   /* --plugin-helper-dir */
 
524
 
      *helper_directory = strdup(arg);
 
526
 
    case 'u':                   /* --user */
 
527
 
    case 130:                   /* --userid */
 
530
 
        uintmax_t tmp_id = 0;
 
532
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
533
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
534
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
 
535
 
          return ARGP_ERR_UNKNOWN;
 
537
 
        *user = (uid_t)tmp_id;
 
541
 
    case 'g':                   /* --group */
 
542
 
    case 131:                   /* --groupid */
 
545
 
        uintmax_t tmp_id = 0;
 
547
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
548
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
549
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
 
550
 
          return ARGP_ERR_UNKNOWN;
 
552
 
        *group = (gid_t)tmp_id;
 
557
 
      /* Copy arguments into argz vector */
 
558
 
      return argz_create(state->argv + state->next, mandos_argz,
 
561
 
      return ARGP_ERR_UNKNOWN;
 
566
 
  const struct argp argp = {
 
569
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
 
570
 
    .doc = "Mandos password agent -- runs Mandos client as a"
 
571
 
    " systemd password agent",
 
574
 
  errno = argp_parse(&argp, argc, argv,
 
575
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
 
580
 
__attribute__((nonnull, warn_unused_result))
 
581
 
bool block_sigchld(sigset_t *const orig_sigmask){
 
582
 
  sigset_t sigchld_sigmask;
 
583
 
  if(sigemptyset(&sigchld_sigmask) < 0){
 
584
 
    error(0, errno, "Failed to empty signal set");
 
587
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
 
588
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
 
591
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
 
592
 
    error(0, errno, "Failed to block SIGCHLD signal");
 
598
 
__attribute__((nonnull, warn_unused_result, const))
 
599
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
 
600
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
 
601
 
    error(0, errno, "Failed to restore blocked signals");
 
607
 
__attribute__((nonnull, warn_unused_result))
 
608
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
 
609
 
  struct sigaction sigchld_action = {
 
610
 
    .sa_handler=handle_sigchld,
 
611
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
613
 
  /* Set all signals in "sa_mask" struct member; this makes all
 
614
 
     signals automatically blocked during signal handler */
 
615
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
 
616
 
    error(0, errno, "Failed to do sigfillset()");
 
619
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
 
620
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
 
626
 
__attribute__((nonnull, warn_unused_result))
 
627
 
bool restore_signal_handler(const struct sigaction *const
 
629
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
 
630
 
    error(0, errno, "Failed to restore signal handler");
 
636
 
__attribute__((warn_unused_result, malloc))
 
637
 
task_queue *create_queue(void){
 
638
 
  task_queue *queue = malloc(sizeof(task_queue));
 
642
 
    queue->allocated = 0;
 
648
 
__attribute__((nonnull, warn_unused_result))
 
649
 
bool add_to_queue(task_queue *const queue, const task_context task){
 
650
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
651
 
  if(needed_size > (queue->allocated)){
 
652
 
    task_context *const new_tasks = realloc(queue->tasks,
 
654
 
    if(new_tasks == NULL){
 
655
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
656
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
659
 
    queue->tasks = new_tasks;
 
660
 
    queue->allocated = needed_size;
 
662
 
  /* Using memcpy here is necessary because doing */
 
663
 
  /* queue->tasks[queue->length++] = task; */
 
664
 
  /* would violate const-ness of task members */
 
665
 
  memcpy(&(queue->tasks[queue->length++]), &task,
 
666
 
         sizeof(task_context));
 
670
 
__attribute__((nonnull))
 
671
 
void cleanup_task(const task_context *const task){
 
672
 
  const error_t saved_errno = errno;
 
673
 
  /* free and close all task data */
 
674
 
  free(task->question_filename);
 
675
 
  if(task->filename != task->question_filename){
 
676
 
    free(task->filename);
 
679
 
    kill(task->pid, SIGTERM);
 
687
 
__attribute__((nonnull))
 
688
 
void free_queue(task_queue *const queue){
 
693
 
__attribute__((nonnull))
 
694
 
void cleanup_queue(task_queue *const *const queue){
 
698
 
  for(size_t i = 0; i < (*queue)->length; i++){
 
699
 
    const task_context *const task = ((*queue)->tasks)+i;
 
705
 
__attribute__((pure, nonnull, warn_unused_result))
 
706
 
bool queue_has_question(const task_queue *const queue){
 
707
 
  for(size_t i=0; i < queue->length; i++){
 
708
 
    if(queue->tasks[i].question_filename != NULL){
 
715
 
__attribute__((nonnull))
 
716
 
void cleanup_close(const int *const fd){
 
717
 
  const error_t saved_errno = errno;
 
722
 
__attribute__((nonnull))
 
723
 
void cleanup_string(char *const *const ptr){
 
727
 
__attribute__((nonnull))
 
728
 
void cleanup_buffer(buffer *buf){
 
729
 
  if(buf->allocated > 0){
 
730
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
731
 
    explicit_bzero(buf->data, buf->allocated);
 
733
 
    memset(buf->data, '\0', buf->allocated);
 
736
 
  if(buf->data != NULL){
 
737
 
    if(munlock(buf->data, buf->allocated) != 0){
 
738
 
      error(0, errno, "Failed to unlock memory of old buffer");
 
747
 
__attribute__((pure, nonnull, warn_unused_result))
 
748
 
bool string_set_contains(const string_set set, const char *const str){
 
749
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
750
 
      s = argz_next(set.argz, set.argz_len, s)){
 
751
 
    if(strcmp(s, str) == 0){
 
758
 
__attribute__((nonnull, warn_unused_result))
 
759
 
bool string_set_add(string_set *const set, const char *const str){
 
760
 
  if(string_set_contains(*set, str)){
 
763
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
771
 
__attribute__((nonnull))
 
772
 
void string_set_clear(string_set *set){
 
778
 
__attribute__((nonnull))
 
779
 
void string_set_swap(string_set *const set1, string_set *const set2){
 
780
 
  /* Swap contents of two string sets */
 
782
 
    char *const tmp_argz = set1->argz;
 
783
 
    set1->argz = set2->argz;
 
784
 
    set2->argz = tmp_argz;
 
787
 
    const size_t tmp_argz_len = set1->argz_len;
 
788
 
    set1->argz_len = set2->argz_len;
 
789
 
    set2->argz_len = tmp_argz_len;
 
793
 
__attribute__((nonnull, warn_unused_result))
 
794
 
bool start_mandos_client(task_queue *const queue,
 
796
 
                         bool *const mandos_client_exited,
 
797
 
                         bool *const quit_now, buffer *const password,
 
798
 
                         bool *const password_is_read,
 
799
 
                         const struct sigaction *const
 
800
 
                         old_sigchld_action, const sigset_t sigmask,
 
801
 
                         const char *const helper_directory,
 
802
 
                         const uid_t user, const gid_t group,
 
803
 
                         const char *const *const argv){
 
805
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
806
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
810
 
  const pid_t pid = fork();
 
812
 
    if(not restore_signal_handler(old_sigchld_action)){
 
815
 
    if(not restore_sigmask(&sigmask)){
 
818
 
    if(close(pipefds[0]) != 0){
 
819
 
      error(0, errno, "Failed to close() parent pipe fd");
 
822
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
823
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
826
 
    if(close(pipefds[1]) != 0){
 
827
 
      error(0, errno, "Failed to close() old child pipe fd");
 
830
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
831
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
832
 
            " \"%s\", 1)", helper_directory);
 
835
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
836
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
837
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
840
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
841
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
842
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
846
 
#pragma GCC diagnostic push
 
847
 
    /* For historical reasons, the "argv" argument to execv() is not
 
848
 
       const, but it is safe to override this. */
 
849
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
851
 
    execv(argv[0], (char **)argv);
 
853
 
#pragma GCC diagnostic pop
 
855
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
860
 
  if(not add_to_queue(queue, (task_context){
 
861
 
        .func=wait_for_mandos_client_exit,
 
863
 
        .mandos_client_exited=mandos_client_exited,
 
866
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
871
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
872
 
                            &(struct epoll_event)
 
873
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
874
 
  if(ret != 0 and errno != EEXIST){
 
875
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
880
 
  return add_to_queue(queue, (task_context){
 
881
 
      .func=read_mandos_client_output,
 
886
 
      .password_is_read=password_is_read,
 
890
 
__attribute__((nonnull))
 
891
 
void wait_for_mandos_client_exit(const task_context task,
 
892
 
                                 task_queue *const queue){
 
893
 
  const pid_t pid = task.pid;
 
894
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
895
 
  bool *const quit_now = task.quit_now;
 
898
 
  switch(waitpid(pid, &status, WNOHANG)){
 
899
 
  case 0:                       /* Not exited yet */
 
900
 
    if(not add_to_queue(queue, task)){
 
901
 
      error(0, errno, "Failed to add myself to queue");
 
906
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
912
 
  default:                      /* Has exited */
 
913
 
    *mandos_client_exited = true;
 
914
 
    if((not WIFEXITED(status))
 
915
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
916
 
      error(0, 0, "Mandos client failed or was killed");
 
922
 
__attribute__((nonnull))
 
923
 
void read_mandos_client_output(const task_context task,
 
924
 
                               task_queue *const queue){
 
925
 
  buffer *const password = task.password;
 
926
 
  bool *const quit_now = task.quit_now;
 
927
 
  bool *const password_is_read = task.password_is_read;
 
928
 
  const int fd = task.fd;
 
929
 
  const int epoll_fd = task.epoll_fd;
 
931
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
932
 
  if(password->allocated < new_potential_size){
 
933
 
    char *const new_buffer = calloc(new_potential_size, 1);
 
934
 
    if(new_buffer == NULL){
 
935
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
936
 
            " bytes for password", (uintmax_t)new_potential_size);
 
941
 
    if(mlock(new_buffer, new_potential_size) != 0){
 
942
 
      /* Warn but do not treat as fatal error */
 
943
 
      if(errno != EPERM and errno != ENOMEM){
 
944
 
        error(0, errno, "Failed to lock memory for password");
 
947
 
    if(password->length > 0){
 
948
 
      memcpy(new_buffer, password->data, password->length);
 
949
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
950
 
      explicit_bzero(password->data, password->allocated);
 
952
 
      memset(password->data, '\0', password->allocated);
 
955
 
    if(password->data != NULL){
 
956
 
      if(munlock(password->data, password->allocated) != 0){
 
957
 
        error(0, errno, "Failed to unlock memory of old buffer");
 
959
 
      free(password->data);
 
961
 
    password->data = new_buffer;
 
962
 
    password->allocated = new_potential_size;
 
965
 
  const ssize_t read_length = read(fd, password->data
 
966
 
                                   + password->length, PIPE_BUF);
 
968
 
  if(read_length == 0){ /* EOF */
 
969
 
    *password_is_read = true;
 
973
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
974
 
    error(0, errno, "Failed to read password from Mandos client");
 
979
 
  if(read_length > 0){          /* Data has been read */
 
980
 
    password->length += (size_t)read_length;
 
983
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
986
 
  /* Re-add the fd to the epoll set */
 
987
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
988
 
                            &(struct epoll_event)
 
989
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
990
 
  if(ret != 0 and errno != EEXIST){
 
991
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
997
 
  /* Re-add myself to the queue */
 
998
 
  if(not add_to_queue(queue, task)){
 
999
 
    error(0, errno, "Failed to add myself to queue");
 
1005
 
__attribute__((nonnull, warn_unused_result))
 
1006
 
bool add_inotify_dir_watch(task_queue *const queue,
 
1007
 
                           const int epoll_fd, bool *const quit_now,
 
1008
 
                           buffer *const password,
 
1009
 
                           const char *const dir,
 
1010
 
                           string_set *cancelled_filenames,
 
1011
 
                           const mono_microsecs *const current_time,
 
1012
 
                           bool *const mandos_client_exited,
 
1013
 
                           bool *const password_is_read){
 
1014
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
1016
 
    error(0, errno, "Failed to create inotify instance");
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
1023
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
1027
 
  /* Add the inotify fd to the epoll set */
 
1028
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1029
 
                            &(struct epoll_event)
 
1030
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1031
 
  if(ret != 0 and errno != EEXIST){
 
1032
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
1037
 
  const task_context read_inotify_event_task = {
 
1038
 
    .func=read_inotify_event,
 
1043
 
    .filename=strdup(dir),
 
1044
 
    .cancelled_filenames=cancelled_filenames,
 
1045
 
    .current_time=current_time,
 
1046
 
    .mandos_client_exited=mandos_client_exited,
 
1047
 
    .password_is_read=password_is_read,
 
1049
 
  if(read_inotify_event_task.filename == NULL){
 
1050
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
1055
 
  return add_to_queue(queue, read_inotify_event_task);
 
1058
 
__attribute__((nonnull))
 
1059
 
void read_inotify_event(const task_context task,
 
1060
 
                        task_queue *const queue){
 
1061
 
  const int fd = task.fd;
 
1062
 
  const int epoll_fd = task.epoll_fd;
 
1063
 
  char *const filename = task.filename;
 
1064
 
  bool *quit_now = task.quit_now;
 
1065
 
  buffer *const password = task.password;
 
1066
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1067
 
  const mono_microsecs *const current_time = task.current_time;
 
1068
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1069
 
  bool *const password_is_read = task.password_is_read;
 
1071
 
  /* "sufficient to read at least one event." - inotify(7) */
 
1072
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
1074
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
1075
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
1078
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
1079
 
  if(read_length == 0){ /* EOF */
 
1080
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
1082
 
    cleanup_task(&task);
 
1085
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
1086
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
1089
 
    cleanup_task(&task);
 
1092
 
  if(read_length > 0            /* Data has been read */
 
1093
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
1094
 
    char *question_filename = NULL;
 
1095
 
    const ssize_t question_filename_length
 
1096
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
1097
 
    if(question_filename_length < 0){
 
1098
 
      error(0, errno, "Failed to create file name from directory name"
 
1099
 
            " %s and file name %s", filename, ievent->name);
 
1101
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
1102
 
        if(not add_to_queue(queue, (task_context){
 
1103
 
              .func=open_and_parse_question,
 
1105
 
              .question_filename=question_filename,
 
1106
 
              .filename=question_filename,
 
1108
 
              .cancelled_filenames=cancelled_filenames,
 
1109
 
              .current_time=current_time,
 
1110
 
              .mandos_client_exited=mandos_client_exited,
 
1111
 
              .password_is_read=password_is_read,
 
1113
 
          error(0, errno, "Failed to add open_and_parse_question task"
 
1114
 
                " for file name %s to queue", filename);
 
1116
 
          /* Force the added task (open_and_parse_question) to run
 
1118
 
          queue->next_run = 1;
 
1120
 
      } else if(ievent->mask & IN_DELETE){
 
1121
 
        if(not string_set_add(cancelled_filenames,
 
1122
 
                              question_filename)){
 
1123
 
          error(0, errno, "Could not add question %s to"
 
1124
 
                " cancelled_questions", question_filename);
 
1126
 
          free(question_filename);
 
1127
 
          cleanup_task(&task);
 
1130
 
        free(question_filename);
 
1135
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1138
 
  /* Re-add myself to the queue */
 
1139
 
  if(not add_to_queue(queue, task)){
 
1140
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
1141
 
          " queue", filename);
 
1143
 
    cleanup_task(&task);
 
1147
 
  /* Re-add the fd to the epoll set */
 
1148
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1149
 
                            &(struct epoll_event)
 
1150
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1151
 
  if(ret != 0 and errno != EEXIST){
 
1152
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
1153
 
          " directory %s to epoll set", fd, filename);
 
1154
 
    /* Force the added task (read_inotify_event) to run again, at most
 
1155
 
       one second from now */
 
1156
 
    if((queue->next_run == 0)
 
1157
 
       or (queue->next_run > (*current_time + 1000000))){
 
1158
 
      queue->next_run = *current_time + 1000000;
 
1163
 
__attribute__((nonnull))
 
1164
 
void open_and_parse_question(const task_context task,
 
1165
 
                             task_queue *const queue){
 
1166
 
  __attribute__((cleanup(cleanup_string)))
 
1167
 
    char *question_filename = task.question_filename;
 
1168
 
  const int epoll_fd = task.epoll_fd;
 
1169
 
  buffer *const password = task.password;
 
1170
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1171
 
  const mono_microsecs *const current_time = task.current_time;
 
1172
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1173
 
  bool *const password_is_read = task.password_is_read;
 
1175
 
  /* We use the GLib "Key-value file parser" functions to parse the
 
1176
 
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1177
 
     /systemd/PasswordAgents/> for specification of contents */
 
1178
 
  __attribute__((nonnull))
 
1179
 
    void cleanup_g_key_file(GKeyFile **key_file){
 
1180
 
    if(*key_file != NULL){
 
1181
 
      g_key_file_free(*key_file);
 
1185
 
  __attribute__((cleanup(cleanup_g_key_file)))
 
1186
 
    GKeyFile *key_file = g_key_file_new();
 
1187
 
  if(key_file == NULL){
 
1188
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
1192
 
  GError *glib_error = NULL;
 
1193
 
  if(g_key_file_load_from_file(key_file, question_filename,
 
1194
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
1195
 
    /* If a file was removed, we should ignore it, so */
 
1196
 
    /* only show error message if file actually existed */
 
1197
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
1198
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
1199
 
            question_filename, glib_error->message);
 
1204
 
  __attribute__((cleanup(cleanup_string)))
 
1205
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
1208
 
  if(socket_name == NULL){
 
1209
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
1210
 
          question_filename, glib_error->message);
 
1214
 
  if(strlen(socket_name) == 0){
 
1215
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
1220
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
1222
 
  if(glib_error != NULL){
 
1223
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
1224
 
          question_filename, glib_error->message);
 
1228
 
  if((pid != (guint64)((pid_t)pid))
 
1229
 
     or (kill((pid_t)pid, 0) != 0)){
 
1230
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
1231
 
          " does not exist", (uintmax_t)pid, question_filename);
 
1235
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
1236
 
                                           "NotAfter", &glib_error);
 
1237
 
  if(glib_error != NULL){
 
1238
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
1239
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
1240
 
            " %s", question_filename, glib_error->message);
 
1245
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
1246
 
      queue->next_run = notafter;
 
1248
 
    if(*current_time >= notafter){
 
1253
 
  const task_context connect_question_socket_task = {
 
1254
 
    .func=connect_question_socket,
 
1255
 
    .question_filename=strdup(question_filename),
 
1258
 
    .filename=strdup(socket_name),
 
1259
 
    .cancelled_filenames=task.cancelled_filenames,
 
1260
 
    .mandos_client_exited=mandos_client_exited,
 
1261
 
    .password_is_read=password_is_read,
 
1262
 
    .current_time=current_time,
 
1264
 
  if(connect_question_socket_task.question_filename == NULL
 
1265
 
     or connect_question_socket_task.filename == NULL
 
1266
 
     or not add_to_queue(queue, connect_question_socket_task)){
 
1267
 
    error(0, errno, "Failed to add connect_question_socket for socket"
 
1268
 
          " %s (from \"%s\") to queue", socket_name,
 
1270
 
    cleanup_task(&connect_question_socket_task);
 
1273
 
  /* Force the added task (connect_question_socket) to run
 
1275
 
  queue->next_run = 1;
 
1278
 
    char *const dup_filename = strdup(question_filename);
 
1279
 
    const task_context cancel_old_question_task = {
 
1280
 
      .func=cancel_old_question,
 
1281
 
      .question_filename=dup_filename,
 
1283
 
      .filename=dup_filename,
 
1284
 
      .cancelled_filenames=cancelled_filenames,
 
1285
 
      .current_time=current_time,
 
1287
 
    if(cancel_old_question_task.question_filename == NULL
 
1288
 
       or not add_to_queue(queue, cancel_old_question_task)){
 
1289
 
      error(0, errno, "Failed to add cancel_old_question for file "
 
1290
 
            "\"%s\" to queue", question_filename);
 
1291
 
      cleanup_task(&cancel_old_question_task);
 
1297
 
__attribute__((nonnull))
 
1298
 
void cancel_old_question(const task_context task,
 
1299
 
                         task_queue *const queue){
 
1300
 
  char *const question_filename = task.question_filename;
 
1301
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1302
 
  const mono_microsecs notafter = task.notafter;
 
1303
 
  const mono_microsecs *const current_time = task.current_time;
 
1305
 
  if(*current_time >= notafter){
 
1306
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1307
 
      error(0, errno, "Failed to cancel question for file %s",
 
1310
 
    cleanup_task(&task);
 
1314
 
  if(not add_to_queue(queue, task)){
 
1315
 
    error(0, errno, "Failed to add cancel_old_question for file "
 
1316
 
          "%s to queue", question_filename);
 
1317
 
    cleanup_task(&task);
 
1321
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
1322
 
    queue->next_run = notafter;
 
1326
 
__attribute__((nonnull))
 
1327
 
void connect_question_socket(const task_context task,
 
1328
 
                             task_queue *const queue){
 
1329
 
  char *const question_filename = task.question_filename;
 
1330
 
  char *const filename = task.filename;
 
1331
 
  const int epoll_fd = task.epoll_fd;
 
1332
 
  buffer *const password = task.password;
 
1333
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1334
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1335
 
  bool *const password_is_read = task.password_is_read;
 
1336
 
  const mono_microsecs *const current_time = task.current_time;
 
1338
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
1340
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
1341
 
    error(0, 0, "Socket filename is larger than"
 
1342
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
1343
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
1344
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1345
 
      error(0, errno, "Failed to cancel question for file %s",
 
1348
 
    cleanup_task(&task);
 
1352
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
1353
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
1356
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
1357
 
    if(not add_to_queue(queue, task)){
 
1358
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1359
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1361
 
      cleanup_task(&task);
 
1363
 
      /* Force the added task (connect_question_socket) to run
 
1365
 
      queue->next_run = 1;
 
1370
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
1371
 
  if(connect(fd, (struct sockaddr *)&sock_name,
 
1372
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
1373
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
1374
 
    if(not add_to_queue(queue, task)){
 
1375
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1376
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1378
 
      cleanup_task(&task);
 
1380
 
      /* Force the added task (connect_question_socket) to run again,
 
1381
 
         at most one second from now */
 
1382
 
      if((queue->next_run == 0)
 
1383
 
         or (queue->next_run > (*current_time + 1000000))){
 
1384
 
        queue->next_run = *current_time + 1000000;
 
1390
 
  /* Not necessary, but we can try, and merely warn on failure */
 
1391
 
  if(shutdown(fd, SHUT_RD) != 0){
 
1392
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
1396
 
  /* Add the fd to the epoll set */
 
1397
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1398
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1400
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
1401
 
          " socket %s to epoll set", fd, filename);
 
1402
 
    if(not add_to_queue(queue, task)){
 
1403
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1404
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1406
 
      cleanup_task(&task);
 
1408
 
      /* Force the added task (connect_question_socket) to run again,
 
1409
 
         at most one second from now */
 
1410
 
      if((queue->next_run == 0)
 
1411
 
         or (queue->next_run > (*current_time + 1000000))){
 
1412
 
        queue->next_run = *current_time + 1000000;
 
1418
 
  /* add task send_password_to_socket to queue */
 
1419
 
  const task_context send_password_to_socket_task = {
 
1420
 
    .func=send_password_to_socket,
 
1421
 
    .question_filename=question_filename,
 
1426
 
    .cancelled_filenames=cancelled_filenames,
 
1427
 
    .mandos_client_exited=mandos_client_exited,
 
1428
 
    .password_is_read=password_is_read,
 
1429
 
    .current_time=current_time,
 
1432
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
1433
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1434
 
          " file \"%s\" and socket \"%s\" to queue",
 
1435
 
          question_filename, filename);
 
1436
 
    cleanup_task(&send_password_to_socket_task);
 
1440
 
__attribute__((nonnull))
 
1441
 
void send_password_to_socket(const task_context task,
 
1442
 
                             task_queue *const queue){
 
1443
 
  char *const question_filename=task.question_filename;
 
1444
 
  char *const filename=task.filename;
 
1445
 
  const int epoll_fd=task.epoll_fd;
 
1446
 
  const int fd=task.fd;
 
1447
 
  buffer *const password=task.password;
 
1448
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
1449
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1450
 
  bool *const password_is_read = task.password_is_read;
 
1451
 
  const mono_microsecs *const current_time = task.current_time;
 
1453
 
  if(*mandos_client_exited and *password_is_read){
 
1455
 
    const size_t send_buffer_length = password->length + 2;
 
1456
 
    char *send_buffer = malloc(send_buffer_length);
 
1457
 
    if(send_buffer == NULL){
 
1458
 
      error(0, errno, "Failed to allocate send_buffer");
 
1460
 
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1461
 
        /* Warn but do not treat as fatal error */
 
1462
 
        if(errno != EPERM and errno != ENOMEM){
 
1463
 
          error(0, errno, "Failed to lock memory for password"
 
1467
 
      /* “[…] send a single datagram to the socket consisting of the
 
1468
 
         password string either prefixed with "+" or with "-"
 
1469
 
         depending on whether the password entry was successful or
 
1470
 
         not. You may but don't have to include a final NUL byte in
 
1473
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1474
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1476
 
      send_buffer[0] = '+';     /* Prefix with "+" */
 
1477
 
      /* Always add an extra NUL */
 
1478
 
      send_buffer[password->length + 1] = '\0';
 
1479
 
      if(password->length > 0){
 
1480
 
        memcpy(send_buffer + 1, password->data, password->length);
 
1483
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
1485
 
      const error_t saved_errno = errno;
 
1486
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
1487
 
      explicit_bzero(send_buffer, send_buffer_length);
 
1489
 
      memset(send_buffer, '\0', send_buffer_length);
 
1491
 
      if(munlock(send_buffer, send_buffer_length) != 0){
 
1492
 
        error(0, errno, "Failed to unlock memory of send buffer");
 
1495
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
1496
 
        switch(saved_errno){
 
1509
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1510
 
                (uintmax_t)password->length);
 
1514
 
          __attribute__((fallthrough));
 
1517
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
1518
 
            error(0, 0, "Password only partially sent to socket");
 
1523
 
          __attribute__((fallthrough));
 
1526
 
          error(0, saved_errno, "Failed to send() to socket %s",
 
1528
 
          if(not string_set_add(cancelled_filenames,
 
1529
 
                                question_filename)){
 
1530
 
            error(0, errno, "Failed to cancel question for file %s",
 
1533
 
          cleanup_task(&task);
 
1538
 
        cleanup_task(&task);
 
1544
 
  /* We failed or are not ready yet; retry later */
 
1546
 
  if(not add_to_queue(queue, task)){
 
1547
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1548
 
          " file %s and socket %s to queue", question_filename,
 
1550
 
    cleanup_task(&task);
 
1553
 
  /* Add the fd to the epoll set */
 
1554
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1555
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1557
 
    error(0, errno, "Failed to add socket file descriptor %d for"
 
1558
 
          " socket %s to epoll set", fd, filename);
 
1559
 
    /* Force the added task (send_password_to_socket) to run again, at
 
1560
 
       most one second from now */
 
1561
 
    if((queue->next_run == 0)
 
1562
 
       or (queue->next_run > (*current_time + 1000000))){
 
1563
 
      queue->next_run = *current_time + 1000000;
 
1568
 
__attribute__((warn_unused_result))
 
1569
 
bool add_existing_questions(task_queue *const queue,
 
1571
 
                            buffer *const password,
 
1572
 
                            string_set *cancelled_filenames,
 
1573
 
                            const mono_microsecs *const current_time,
 
1574
 
                            bool *const mandos_client_exited,
 
1575
 
                            bool *const password_is_read,
 
1576
 
                            const char *const dirname){
 
1577
 
  __attribute__((cleanup(cleanup_string)))
 
1578
 
    char *dir_pattern = NULL;
 
1579
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
1580
 
  if(ret < 0 or dir_pattern == NULL){
 
1581
 
    error(0, errno, "Could not create glob pattern for directory %s",
 
1585
 
  __attribute__((cleanup(globfree)))
 
1586
 
    glob_t question_filenames = {};
 
1587
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
1588
 
              NULL, &question_filenames)){
 
1591
 
    error(0, errno, "Failed to open directory %s", dirname);
 
1594
 
    error(0, errno, "There are no question files in %s", dirname);
 
1597
 
    error(0, errno, "Could not allocate memory for question file"
 
1598
 
          " names in %s", dirname);
 
1602
 
    __attribute__((fallthrough));
 
1605
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
1606
 
      char *const question_filename = strdup(question_filenames
 
1608
 
      const task_context task = {
 
1609
 
        .func=open_and_parse_question,
 
1611
 
        .question_filename=question_filename,
 
1612
 
        .filename=question_filename,
 
1614
 
        .cancelled_filenames=cancelled_filenames,
 
1615
 
        .current_time=current_time,
 
1616
 
        .mandos_client_exited=mandos_client_exited,
 
1617
 
        .password_is_read=password_is_read,
 
1620
 
      if(question_filename == NULL
 
1621
 
         or not add_to_queue(queue, task)){
 
1622
 
        error(0, errno, "Failed to add open_and_parse_question for"
 
1623
 
              " file %s to queue",
 
1624
 
              question_filenames.gl_pathv[i]);
 
1625
 
        free(question_filename);
 
1627
 
        queue->next_run = 1;
 
1634
 
__attribute__((nonnull, warn_unused_result))
 
1635
 
bool wait_for_event(const int epoll_fd,
 
1636
 
                    const mono_microsecs queue_next_run,
 
1637
 
                    const mono_microsecs current_time){
 
1638
 
  __attribute__((const))
 
1639
 
    int milliseconds_to_wait(const mono_microsecs currtime,
 
1640
 
                             const mono_microsecs nextrun){
 
1641
 
    if(currtime >= nextrun){
 
1644
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
1645
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
1648
 
    return (int)wait_time_ms;
 
1651
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
1654
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
1655
 
  sigset_t temporary_unblocked_sigmask;
 
1656
 
  /* Get current signal mask */
 
1657
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
1660
 
  /* Remove SIGCHLD from the signal mask */
 
1661
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
1664
 
  struct epoll_event events[8]; /* Ignored */
 
1665
 
  int ret = epoll_pwait(epoll_fd, events,
 
1666
 
                        sizeof(events) / sizeof(struct epoll_event),
 
1667
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
1668
 
                        &temporary_unblocked_sigmask);
 
1669
 
  if(ret < 0 and errno != EINTR){
 
1670
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
1672
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
1675
 
  return clear_all_fds_from_epoll_set(epoll_fd);
 
1678
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
1679
 
  /* Create a new empty epoll set */
 
1680
 
  __attribute__((cleanup(cleanup_close)))
 
1681
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
1682
 
  if(new_epoll_fd < 0){
 
1685
 
  /* dup3() the new epoll set fd over the old one, replacing it */
 
1686
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
1692
 
__attribute__((nonnull, warn_unused_result))
 
1693
 
bool run_queue(task_queue **const queue,
 
1694
 
               string_set *const cancelled_filenames,
 
1695
 
               bool *const quit_now){
 
1697
 
  task_queue *new_queue = create_queue();
 
1698
 
  if(new_queue == NULL){
 
1702
 
  __attribute__((cleanup(string_set_clear)))
 
1703
 
    string_set old_cancelled_filenames = {};
 
1704
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
1706
 
  /* Declare i outside the for loop, since we might need i after the
 
1707
 
     loop in case we aborted in the middle */
 
1709
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
1710
 
    task_context *const task = &((*queue)->tasks[i]);
 
1711
 
    const char *const question_filename = task->question_filename;
 
1712
 
    /* Skip any task referencing a cancelled question filename */
 
1713
 
    if(question_filename != NULL
 
1714
 
       and string_set_contains(old_cancelled_filenames,
 
1715
 
                               question_filename)){
 
1719
 
    task->func(*task, new_queue);
 
1723
 
    /* we might be in the middle of the queue, so clean up any
 
1724
 
       remaining tasks in the current queue */
 
1725
 
    for(; i < (*queue)->length; i++){
 
1726
 
      cleanup_task(&((*queue)->tasks[i]));
 
1740
 
/* End of regular code section */
 
1742
 
/* Start of tests section; here are the tests for the above code */
 
1744
 
/* This "fixture" data structure is used by the test setup and
 
1745
 
   teardown functions */
 
1747
 
  struct sigaction orig_sigaction;
 
1748
 
  sigset_t orig_sigmask;
 
1751
 
static void test_setup(test_fixture *fixture,
 
1752
 
                       __attribute__((unused))
 
1753
 
                       gconstpointer user_data){
 
1754
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
1755
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
1758
 
static void test_teardown(test_fixture *fixture,
 
1759
 
                          __attribute__((unused))
 
1760
 
                          gconstpointer user_data){
 
1761
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
1762
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
1765
 
/* Utility function used by tests to search queue for matching task */
 
1766
 
__attribute__((pure, nonnull, warn_unused_result))
 
1767
 
static task_context *find_matching_task(const task_queue *const queue,
 
1768
 
                                        const task_context task){
 
1769
 
  /* The argument "task" structure is a pattern to match; 0 in any
 
1770
 
     member means any value matches, otherwise the value must match.
 
1771
 
     The filename strings are compared by strcmp(), not by pointer. */
 
1772
 
  for(size_t i = 0; i < queue->length; i++){
 
1773
 
    task_context *const current_task = queue->tasks+i;
 
1774
 
    /* Check all members of task_context, if set to a non-zero value.
 
1775
 
       If a member does not match, continue to next task in queue */
 
1777
 
    /* task_func *const func */
 
1778
 
    if(task.func != NULL and current_task->func != task.func){
 
1781
 
    /* char *const question_filename; */
 
1782
 
    if(task.question_filename != NULL
 
1783
 
       and (current_task->question_filename == NULL
 
1784
 
            or strcmp(current_task->question_filename,
 
1785
 
                      task.question_filename) != 0)){
 
1788
 
    /* const pid_t pid; */
 
1789
 
    if(task.pid != 0 and current_task->pid != task.pid){
 
1792
 
    /* const int epoll_fd; */
 
1793
 
    if(task.epoll_fd != 0
 
1794
 
       and current_task->epoll_fd != task.epoll_fd){
 
1797
 
    /* bool *const quit_now; */
 
1798
 
    if(task.quit_now != NULL
 
1799
 
       and current_task->quit_now != task.quit_now){
 
1803
 
    if(task.fd != 0 and current_task->fd != task.fd){
 
1806
 
    /* bool *const mandos_client_exited; */
 
1807
 
    if(task.mandos_client_exited != NULL
 
1808
 
       and current_task->mandos_client_exited
 
1809
 
       != task.mandos_client_exited){
 
1812
 
    /* buffer *const password; */
 
1813
 
    if(task.password != NULL
 
1814
 
       and current_task->password != task.password){
 
1817
 
    /* bool *const password_is_read; */
 
1818
 
    if(task.password_is_read != NULL
 
1819
 
       and current_task->password_is_read != task.password_is_read){
 
1822
 
    /* char *filename; */
 
1823
 
    if(task.filename != NULL
 
1824
 
       and (current_task->filename == NULL
 
1825
 
            or strcmp(current_task->filename, task.filename) != 0)){
 
1828
 
    /* string_set *const cancelled_filenames; */
 
1829
 
    if(task.cancelled_filenames != NULL
 
1830
 
       and current_task->cancelled_filenames
 
1831
 
       != task.cancelled_filenames){
 
1834
 
    /* const mono_microsecs notafter; */
 
1835
 
    if(task.notafter != 0
 
1836
 
       and current_task->notafter != task.notafter){
 
1839
 
    /* const mono_microsecs *const current_time; */
 
1840
 
    if(task.current_time != NULL
 
1841
 
       and current_task->current_time != task.current_time){
 
1844
 
    /* Current task matches all members; return it */
 
1845
 
    return current_task;
 
1847
 
  /* No task in queue matches passed pattern task */
 
1851
 
static void test_create_queue(__attribute__((unused))
 
1852
 
                              test_fixture *fixture,
 
1853
 
                              __attribute__((unused))
 
1854
 
                              gconstpointer user_data){
 
1855
 
  __attribute__((cleanup(cleanup_queue)))
 
1856
 
    task_queue *const queue = create_queue();
 
1857
 
  g_assert_nonnull(queue);
 
1858
 
  g_assert_null(queue->tasks);
 
1859
 
  g_assert_true(queue->length == 0);
 
1860
 
  g_assert_true(queue->next_run == 0);
 
1863
 
static task_func dummy_func;
 
1865
 
static void test_add_to_queue(__attribute__((unused))
 
1866
 
                              test_fixture *fixture,
 
1867
 
                              __attribute__((unused))
 
1868
 
                              gconstpointer user_data){
 
1869
 
  __attribute__((cleanup(cleanup_queue)))
 
1870
 
    task_queue *queue = create_queue();
 
1871
 
  g_assert_nonnull(queue);
 
1873
 
  g_assert_true(add_to_queue(queue,
 
1874
 
                             (task_context){ .func=dummy_func }));
 
1875
 
  g_assert_true(queue->length == 1);
 
1876
 
  g_assert_nonnull(queue->tasks);
 
1877
 
  g_assert_true(queue->tasks[0].func == dummy_func);
 
1880
 
static void dummy_func(__attribute__((unused))
 
1881
 
                       const task_context task,
 
1882
 
                       __attribute__((unused))
 
1883
 
                       task_queue *const queue){
 
1886
 
static void test_queue_has_question_empty(__attribute__((unused))
 
1887
 
                                          test_fixture *fixture,
 
1888
 
                                          __attribute__((unused))
 
1889
 
                                          gconstpointer user_data){
 
1890
 
  __attribute__((cleanup(cleanup_queue)))
 
1891
 
    task_queue *queue = create_queue();
 
1892
 
  g_assert_nonnull(queue);
 
1893
 
  g_assert_false(queue_has_question(queue));
 
1896
 
static void test_queue_has_question_false(__attribute__((unused))
 
1897
 
                                          test_fixture *fixture,
 
1898
 
                                          __attribute__((unused))
 
1899
 
                                          gconstpointer user_data){
 
1900
 
  __attribute__((cleanup(cleanup_queue)))
 
1901
 
    task_queue *queue = create_queue();
 
1902
 
  g_assert_nonnull(queue);
 
1903
 
  g_assert_true(add_to_queue(queue,
 
1904
 
                             (task_context){ .func=dummy_func }));
 
1905
 
  g_assert_false(queue_has_question(queue));
 
1908
 
static void test_queue_has_question_true(__attribute__((unused))
 
1909
 
                                         test_fixture *fixture,
 
1910
 
                                         __attribute__((unused))
 
1911
 
                                         gconstpointer user_data){
 
1912
 
  __attribute__((cleanup(cleanup_queue)))
 
1913
 
    task_queue *queue = create_queue();
 
1914
 
  g_assert_nonnull(queue);
 
1915
 
  char *const question_filename
 
1916
 
    = strdup("/nonexistent/question_filename");
 
1917
 
  g_assert_nonnull(question_filename);
 
1918
 
  task_context task = {
 
1920
 
    .question_filename=question_filename,
 
1922
 
  g_assert_true(add_to_queue(queue, task));
 
1923
 
  g_assert_true(queue_has_question(queue));
 
1926
 
static void test_queue_has_question_false2(__attribute__((unused))
 
1927
 
                                           test_fixture *fixture,
 
1928
 
                                           __attribute__((unused))
 
1929
 
                                           gconstpointer user_data){
 
1930
 
  __attribute__((cleanup(cleanup_queue)))
 
1931
 
    task_queue *queue = create_queue();
 
1932
 
  g_assert_nonnull(queue);
 
1933
 
  task_context task = { .func=dummy_func };
 
1934
 
  g_assert_true(add_to_queue(queue, task));
 
1935
 
  g_assert_true(add_to_queue(queue, task));
 
1936
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1937
 
  g_assert_false(queue_has_question(queue));
 
1940
 
static void test_queue_has_question_true2(__attribute__((unused))
 
1941
 
                                          test_fixture *fixture,
 
1942
 
                                          __attribute__((unused))
 
1943
 
                                          gconstpointer user_data){
 
1944
 
  __attribute__((cleanup(cleanup_queue)))
 
1945
 
    task_queue *queue = create_queue();
 
1946
 
  g_assert_nonnull(queue);
 
1947
 
  task_context task1 = { .func=dummy_func };
 
1948
 
  g_assert_true(add_to_queue(queue, task1));
 
1949
 
  char *const question_filename
 
1950
 
    = strdup("/nonexistent/question_filename");
 
1951
 
  g_assert_nonnull(question_filename);
 
1952
 
  task_context task2 = {
 
1954
 
    .question_filename=question_filename,
 
1956
 
  g_assert_true(add_to_queue(queue, task2));
 
1957
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1958
 
  g_assert_true(queue_has_question(queue));
 
1961
 
static void test_cleanup_buffer(__attribute__((unused))
 
1962
 
                                test_fixture *fixture,
 
1963
 
                                __attribute__((unused))
 
1964
 
                                gconstpointer user_data){
 
1967
 
  const size_t buffersize = 10;
 
1969
 
  buf.data = malloc(buffersize);
 
1970
 
  g_assert_nonnull(buf.data);
 
1971
 
  if(mlock(buf.data, buffersize) != 0){
 
1972
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
1975
 
  cleanup_buffer(&buf);
 
1976
 
  g_assert_null(buf.data);
 
1980
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
1981
 
                                              test_fixture *fixture,
 
1982
 
                                              __attribute__((unused))
 
1985
 
  __attribute__((cleanup(string_set_clear)))
 
1986
 
    string_set set = {};
 
1987
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
1988
 
  g_assert_false(string_set_contains(set, "test_string"));
 
1992
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
1993
 
                                              test_fixture *fixture,
 
1994
 
                                              __attribute__((unused))
 
1997
 
  __attribute__((cleanup(string_set_clear)))
 
1998
 
    string_set set = {};
 
1999
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2000
 
  g_assert_true(string_set_contains(set, "test_string"));
 
2004
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
2005
 
                                             test_fixture *fixture,
 
2006
 
                                             __attribute__((unused))
 
2007
 
                                             gconstpointer user_data){
 
2008
 
  __attribute__((cleanup(string_set_clear)))
 
2009
 
    string_set set = {};
 
2010
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2011
 
  string_set_clear(&set);
 
2012
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2016
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
2017
 
                                         test_fixture *fixture,
 
2018
 
                                         __attribute__((unused))
 
2019
 
                                         gconstpointer user_data){
 
2020
 
  __attribute__((cleanup(string_set_clear)))
 
2021
 
    string_set set1 = {};
 
2022
 
  __attribute__((cleanup(string_set_clear)))
 
2023
 
    string_set set2 = {};
 
2024
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2025
 
  string_set_swap(&set1, &set2);
 
2026
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2027
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2031
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
2032
 
                                         test_fixture *fixture,
 
2033
 
                                         __attribute__((unused))
 
2034
 
                                         gconstpointer user_data){
 
2035
 
  __attribute__((cleanup(string_set_clear)))
 
2036
 
    string_set set1 = {};
 
2037
 
  __attribute__((cleanup(string_set_clear)))
 
2038
 
    string_set set2 = {};
 
2039
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2040
 
  string_set_swap(&set1, &set2);
 
2041
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2042
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2045
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
2046
 
                                              test_fixture *fixture,
 
2047
 
                                              __attribute__((unused))
 
2050
 
  __attribute__((cleanup(string_set_clear)))
 
2051
 
    string_set set1 = {};
 
2052
 
  __attribute__((cleanup(string_set_clear)))
 
2053
 
    string_set set2 = {};
 
2054
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2055
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2056
 
  string_set_swap(&set1, &set2);
 
2057
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2058
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2059
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2060
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2063
 
static bool fd_has_cloexec_and_nonblock(const int);
 
2065
 
static bool epoll_set_contains(int, int, uint32_t);
 
2067
 
static void test_start_mandos_client(test_fixture *fixture,
 
2068
 
                                     __attribute__((unused))
 
2069
 
                                     gconstpointer user_data){
 
2071
 
  bool mandos_client_exited = false;
 
2072
 
  bool quit_now = false;
 
2073
 
  __attribute__((cleanup(cleanup_close)))
 
2074
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2075
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2076
 
  __attribute__((cleanup(cleanup_queue)))
 
2077
 
    task_queue *queue = create_queue();
 
2078
 
  g_assert_nonnull(queue);
 
2079
 
  buffer password = {};
 
2080
 
  bool password_is_read = false;
 
2081
 
  const char helper_directory[] = "/nonexistent";
 
2082
 
  const char *const argv[] = { "/bin/true", NULL };
 
2084
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2085
 
                                    &mandos_client_exited, &quit_now,
 
2086
 
                                    &password, &password_is_read,
 
2087
 
                                    &fixture->orig_sigaction,
 
2088
 
                                    fixture->orig_sigmask,
 
2089
 
                                    helper_directory, 0, 0, argv));
 
2091
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
2093
 
  const task_context *const added_wait_task
 
2094
 
    = find_matching_task(queue, (task_context){
 
2095
 
        .func=wait_for_mandos_client_exit,
 
2096
 
        .mandos_client_exited=&mandos_client_exited,
 
2097
 
        .quit_now=&quit_now,
 
2099
 
  g_assert_nonnull(added_wait_task);
 
2100
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
2101
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
2102
 
  waitpid(added_wait_task->pid, NULL, 0);
 
2104
 
  const task_context *const added_read_task
 
2105
 
    = find_matching_task(queue, (task_context){
 
2106
 
        .func=read_mandos_client_output,
 
2108
 
        .password=&password,
 
2109
 
        .password_is_read=&password_is_read,
 
2110
 
        .quit_now=&quit_now,
 
2112
 
  g_assert_nonnull(added_read_task);
 
2113
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
2114
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
2115
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
2116
 
                                   EPOLLIN | EPOLLRDHUP));
 
2119
 
static bool fd_has_cloexec_and_nonblock(const int fd){
 
2120
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
2121
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
2122
 
  return ((socket_fd_flags >= 0)
 
2123
 
          and (socket_fd_flags & FD_CLOEXEC)
 
2124
 
          and (socket_file_flags >= 0)
 
2125
 
          and (socket_file_flags & O_NONBLOCK));
 
2128
 
__attribute__((const))
 
2129
 
bool is_privileged(void){
 
2130
 
  uid_t user = getuid() + 1;
 
2131
 
  if(user == 0){                /* Overflow check */
 
2134
 
  gid_t group = getuid() + 1;
 
2135
 
  if(group == 0){               /* Overflow check */
 
2138
 
  const pid_t pid = fork();
 
2139
 
  if(pid == 0){                 /* Child */
 
2140
 
    if(setresgid((uid_t)-1, group, group) == -1){
 
2142
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
2143
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
2147
 
    if(setresuid((uid_t)-1, user, user) == -1){
 
2149
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
2150
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
2157
 
  waitpid(pid, &status, 0);
 
2158
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
2164
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
2165
 
  /* Only scan for events in this eventmask */
 
2166
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
2167
 
  __attribute__((cleanup(cleanup_string)))
 
2168
 
    char *fdinfo_name = NULL;
 
2169
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
2170
 
  g_assert_cmpint(ret, >, 0);
 
2171
 
  g_assert_nonnull(fdinfo_name);
 
2173
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
2174
 
  g_assert_nonnull(fdinfo);
 
2175
 
  uint32_t reported_events;
 
2180
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
2183
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
2184
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
2185
 
              &found_fd, &reported_events) == 2){
 
2190
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
2191
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
2198
 
    /* Don't check events if none are given */
 
2201
 
  return (reported_events & eventmask) == (events & eventmask);
 
2204
 
static void test_start_mandos_client_execv(test_fixture *fixture,
 
2205
 
                                           __attribute__((unused))
 
2206
 
                                           gconstpointer user_data){
 
2207
 
  bool mandos_client_exited = false;
 
2208
 
  bool quit_now = false;
 
2209
 
  __attribute__((cleanup(cleanup_close)))
 
2210
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2211
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2212
 
  __attribute__((cleanup(cleanup_queue)))
 
2213
 
    task_queue *queue = create_queue();
 
2214
 
  g_assert_nonnull(queue);
 
2215
 
  __attribute__((cleanup(cleanup_buffer)))
 
2216
 
    buffer password = {};
 
2217
 
  const char helper_directory[] = "/nonexistent";
 
2218
 
  /* Can't execv("/", ...), so this should fail */
 
2219
 
  const char *const argv[] = { "/", NULL };
 
2222
 
    __attribute__((cleanup(cleanup_close)))
 
2223
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
2224
 
    g_assert_cmpint(devnull_fd, >=, 0);
 
2225
 
    __attribute__((cleanup(cleanup_close)))
 
2226
 
      const int real_stderr_fd = dup(STDERR_FILENO);
 
2227
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
2228
 
    dup2(devnull_fd, STDERR_FILENO);
 
2230
 
    const bool success = start_mandos_client(queue, epoll_fd,
 
2231
 
                                             &mandos_client_exited,
 
2235
 
                                             &fixture->orig_sigaction,
 
2236
 
                                             fixture->orig_sigmask,
 
2237
 
                                             helper_directory, 0, 0,
 
2239
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2240
 
    g_assert_true(success);
 
2242
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
2244
 
  struct timespec starttime, currtime;
 
2245
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2247
 
    queue->next_run = 0;
 
2248
 
    string_set cancelled_filenames = {};
 
2251
 
      __attribute__((cleanup(cleanup_close)))
 
2252
 
        const int devnull_fd = open("/dev/null",
 
2253
 
                                    O_WRONLY | O_CLOEXEC);
 
2254
 
      g_assert_cmpint(devnull_fd, >=, 0);
 
2255
 
      __attribute__((cleanup(cleanup_close)))
 
2256
 
        const int real_stderr_fd = dup(STDERR_FILENO);
 
2257
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
2258
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2259
 
      dup2(devnull_fd, STDERR_FILENO);
 
2260
 
      const bool success = run_queue(&queue, &cancelled_filenames,
 
2262
 
      dup2(real_stderr_fd, STDERR_FILENO);
 
2267
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2268
 
  } while(((queue->length) > 0)
 
2270
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2272
 
  g_assert_true(quit_now);
 
2273
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2274
 
  g_assert_true(mandos_client_exited);
 
2277
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
2278
 
                                               __attribute__((unused))
 
2281
 
  if(not is_privileged()){
 
2282
 
    g_test_skip("Not privileged");
 
2286
 
  bool mandos_client_exited = false;
 
2287
 
  bool quit_now = false;
 
2288
 
  __attribute__((cleanup(cleanup_close)))
 
2289
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2290
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2291
 
  __attribute__((cleanup(cleanup_queue)))
 
2292
 
    task_queue *queue = create_queue();
 
2293
 
  g_assert_nonnull(queue);
 
2294
 
  __attribute__((cleanup(cleanup_buffer)))
 
2295
 
    buffer password = {};
 
2296
 
  bool password_is_read = false;
 
2297
 
  const char helper_directory[] = "/nonexistent";
 
2298
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
2302
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2303
 
                                           &mandos_client_exited,
 
2304
 
                                           &quit_now, &password,
 
2306
 
                                           &fixture->orig_sigaction,
 
2307
 
                                           fixture->orig_sigmask,
 
2308
 
                                           helper_directory, user,
 
2310
 
  g_assert_true(success);
 
2311
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2313
 
  struct timespec starttime, currtime;
 
2314
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2316
 
    queue->next_run = 0;
 
2317
 
    string_set cancelled_filenames = {};
 
2318
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2319
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2320
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2321
 
  } while(((queue->length) > 0)
 
2323
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2325
 
  g_assert_false(quit_now);
 
2326
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2327
 
  g_assert_true(mandos_client_exited);
 
2329
 
  g_assert_true(password_is_read);
 
2330
 
  g_assert_nonnull(password.data);
 
2333
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2335
 
  g_assert_true((uid_t)id == id);
 
2337
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2340
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
2341
 
                                               __attribute__((unused))
 
2344
 
  if(not is_privileged()){
 
2345
 
    g_test_skip("Not privileged");
 
2349
 
  bool mandos_client_exited = false;
 
2350
 
  bool quit_now = false;
 
2351
 
  __attribute__((cleanup(cleanup_close)))
 
2352
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2353
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2354
 
  __attribute__((cleanup(cleanup_queue)))
 
2355
 
    task_queue *queue = create_queue();
 
2356
 
  g_assert_nonnull(queue);
 
2357
 
  __attribute__((cleanup(cleanup_buffer)))
 
2358
 
    buffer password = {};
 
2359
 
  bool password_is_read = false;
 
2360
 
  const char helper_directory[] = "/nonexistent";
 
2361
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
2365
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2366
 
                                           &mandos_client_exited,
 
2367
 
                                           &quit_now, &password,
 
2369
 
                                           &fixture->orig_sigaction,
 
2370
 
                                           fixture->orig_sigmask,
 
2371
 
                                           helper_directory, user,
 
2373
 
  g_assert_true(success);
 
2374
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2376
 
  struct timespec starttime, currtime;
 
2377
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2379
 
    queue->next_run = 0;
 
2380
 
    string_set cancelled_filenames = {};
 
2381
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2382
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2383
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2384
 
  } while(((queue->length) > 0)
 
2386
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2388
 
  g_assert_false(quit_now);
 
2389
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2390
 
  g_assert_true(mandos_client_exited);
 
2392
 
  g_assert_true(password_is_read);
 
2393
 
  g_assert_nonnull(password.data);
 
2396
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2398
 
  g_assert_true((gid_t)id == id);
 
2400
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2403
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
2404
 
                                               __attribute__((unused))
 
2407
 
  if(not is_privileged()){
 
2408
 
    g_test_skip("Not privileged");
 
2412
 
  bool mandos_client_exited = false;
 
2413
 
  bool quit_now = false;
 
2414
 
  __attribute__((cleanup(cleanup_close)))
 
2415
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2416
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2417
 
  __attribute__((cleanup(cleanup_queue)))
 
2418
 
    task_queue *queue = create_queue();
 
2419
 
  g_assert_nonnull(queue);
 
2420
 
  __attribute__((cleanup(cleanup_buffer)))
 
2421
 
    buffer password = {};
 
2422
 
  bool password_is_read = false;
 
2423
 
  const char helper_directory[] = "/nonexistent";
 
2424
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
2429
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2430
 
                                           &mandos_client_exited,
 
2431
 
                                           &quit_now, &password,
 
2433
 
                                           &fixture->orig_sigaction,
 
2434
 
                                           fixture->orig_sigmask,
 
2435
 
                                           helper_directory, user,
 
2437
 
  g_assert_true(success);
 
2438
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2440
 
  struct timespec starttime, currtime;
 
2441
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2443
 
    queue->next_run = 0;
 
2444
 
    string_set cancelled_filenames = {};
 
2445
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2446
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2447
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2448
 
  } while(((queue->length) > 0)
 
2450
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2452
 
  g_assert_false(quit_now);
 
2453
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2454
 
  g_assert_true(mandos_client_exited);
 
2456
 
  g_assert_true(password_is_read);
 
2457
 
  g_assert_nonnull(password.data);
 
2460
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2462
 
  g_assert_true((uid_t)id == id);
 
2464
 
  g_assert_cmpuint((unsigned int)id, ==, user);
 
2467
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
2468
 
                                               __attribute__((unused))
 
2471
 
  if(not is_privileged()){
 
2472
 
    g_test_skip("Not privileged");
 
2476
 
  bool mandos_client_exited = false;
 
2477
 
  bool quit_now = false;
 
2478
 
  __attribute__((cleanup(cleanup_close)))
 
2479
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2480
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2481
 
  __attribute__((cleanup(cleanup_queue)))
 
2482
 
    task_queue *queue = create_queue();
 
2483
 
  g_assert_nonnull(queue);
 
2484
 
  __attribute__((cleanup(cleanup_buffer)))
 
2485
 
    buffer password = {};
 
2486
 
  bool password_is_read = false;
 
2487
 
  const char helper_directory[] = "/nonexistent";
 
2488
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
2493
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2494
 
                                           &mandos_client_exited,
 
2495
 
                                           &quit_now, &password,
 
2497
 
                                           &fixture->orig_sigaction,
 
2498
 
                                           fixture->orig_sigmask,
 
2499
 
                                           helper_directory, user,
 
2501
 
  g_assert_true(success);
 
2502
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2504
 
  struct timespec starttime, currtime;
 
2505
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2507
 
    queue->next_run = 0;
 
2508
 
    string_set cancelled_filenames = {};
 
2509
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2510
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2511
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2512
 
  } while(((queue->length) > 0)
 
2514
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2516
 
  g_assert_false(quit_now);
 
2517
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2518
 
  g_assert_true(mandos_client_exited);
 
2520
 
  g_assert_true(password_is_read);
 
2521
 
  g_assert_nonnull(password.data);
 
2524
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2526
 
  g_assert_true((gid_t)id == id);
 
2528
 
  g_assert_cmpuint((unsigned int)id, ==, group);
 
2531
 
static void test_start_mandos_client_read(test_fixture *fixture,
 
2532
 
                                          __attribute__((unused))
 
2533
 
                                          gconstpointer user_data){
 
2534
 
  bool mandos_client_exited = false;
 
2535
 
  bool quit_now = false;
 
2536
 
  __attribute__((cleanup(cleanup_close)))
 
2537
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2538
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2539
 
  __attribute__((cleanup(cleanup_queue)))
 
2540
 
    task_queue *queue = create_queue();
 
2541
 
  g_assert_nonnull(queue);
 
2542
 
  __attribute__((cleanup(cleanup_buffer)))
 
2543
 
    buffer password = {};
 
2544
 
  bool password_is_read = false;
 
2545
 
  const char dummy_test_password[] = "dummy test password";
 
2546
 
  const char helper_directory[] = "/nonexistent";
 
2547
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
2550
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2551
 
                                           &mandos_client_exited,
 
2552
 
                                           &quit_now, &password,
 
2554
 
                                           &fixture->orig_sigaction,
 
2555
 
                                           fixture->orig_sigmask,
 
2556
 
                                           helper_directory, 0, 0,
 
2558
 
  g_assert_true(success);
 
2559
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2561
 
  struct timespec starttime, currtime;
 
2562
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2564
 
    queue->next_run = 0;
 
2565
 
    string_set cancelled_filenames = {};
 
2566
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2567
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2568
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2569
 
  } while(((queue->length) > 0)
 
2571
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2573
 
  g_assert_false(quit_now);
 
2574
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2575
 
  g_assert_true(mandos_client_exited);
 
2577
 
  g_assert_true(password_is_read);
 
2578
 
  g_assert_cmpint((int)password.length, ==,
 
2579
 
                  sizeof(dummy_test_password)-1);
 
2580
 
  g_assert_nonnull(password.data);
 
2581
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
2582
 
                         sizeof(dummy_test_password)-1), ==, 0);
 
2586
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
2587
 
                                               __attribute__((unused))
 
2590
 
  bool mandos_client_exited = false;
 
2591
 
  bool quit_now = false;
 
2592
 
  __attribute__((cleanup(cleanup_close)))
 
2593
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2594
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2595
 
  __attribute__((cleanup(cleanup_queue)))
 
2596
 
    task_queue *queue = create_queue();
 
2597
 
  g_assert_nonnull(queue);
 
2598
 
  __attribute__((cleanup(cleanup_buffer)))
 
2599
 
    buffer password = {};
 
2600
 
  bool password_is_read = false;
 
2601
 
  const char helper_directory[] = "/nonexistent";
 
2602
 
  const char *const argv[] = { "/bin/sh", "-c",
 
2603
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
2605
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2606
 
                                           &mandos_client_exited,
 
2607
 
                                           &quit_now, &password,
 
2609
 
                                           &fixture->orig_sigaction,
 
2610
 
                                           fixture->orig_sigmask,
 
2611
 
                                           helper_directory, 0, 0,
 
2613
 
  g_assert_true(success);
 
2614
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2616
 
  struct timespec starttime, currtime;
 
2617
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2619
 
    queue->next_run = 0;
 
2620
 
    string_set cancelled_filenames = {};
 
2621
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2622
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2623
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2624
 
  } while(((queue->length) > 0)
 
2626
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2628
 
  g_assert_false(quit_now);
 
2629
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2630
 
  g_assert_true(mandos_client_exited);
 
2632
 
  g_assert_true(password_is_read);
 
2633
 
  g_assert_cmpint((int)password.length, ==,
 
2634
 
                  sizeof(helper_directory)-1);
 
2635
 
  g_assert_nonnull(password.data);
 
2636
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
2637
 
                         sizeof(helper_directory)-1), ==, 0);
 
2640
 
__attribute__((nonnull, warn_unused_result))
 
2641
 
static bool proc_status_sigblk_to_sigset(const char *const,
 
2644
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
2645
 
                                             __attribute__((unused))
 
2646
 
                                             gconstpointer user_data){
 
2647
 
  bool mandos_client_exited = false;
 
2648
 
  bool quit_now = false;
 
2649
 
  __attribute__((cleanup(cleanup_close)))
 
2650
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2651
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2652
 
  __attribute__((cleanup(cleanup_queue)))
 
2653
 
    task_queue *queue = create_queue();
 
2654
 
  g_assert_nonnull(queue);
 
2655
 
  __attribute__((cleanup(cleanup_buffer)))
 
2656
 
    buffer password = {};
 
2657
 
  bool password_is_read = false;
 
2658
 
  const char helper_directory[] = "/nonexistent";
 
2659
 
  /* see proc(5) for format of /proc/self/status */
 
2660
 
  const char *const argv[] = { "/usr/bin/awk",
 
2661
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
2663
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2664
 
                                    &mandos_client_exited, &quit_now,
 
2665
 
                                    &password, &password_is_read,
 
2666
 
                                    &fixture->orig_sigaction,
 
2667
 
                                    fixture->orig_sigmask,
 
2668
 
                                    helper_directory, 0, 0, argv));
 
2670
 
  struct timespec starttime, currtime;
 
2671
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2673
 
    queue->next_run = 0;
 
2674
 
    string_set cancelled_filenames = {};
 
2675
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2676
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2677
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2678
 
  } while((not (mandos_client_exited and password_is_read))
 
2680
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2681
 
  g_assert_true(mandos_client_exited);
 
2682
 
  g_assert_true(password_is_read);
 
2684
 
  sigset_t parsed_sigmask;
 
2685
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
2688
 
  for(int signum = 1; signum < NSIG; signum++){
 
2689
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
2690
 
    if(sigismember(&fixture->orig_sigmask, signum)){
 
2691
 
      g_assert_true(has_signal);
 
2693
 
      g_assert_false(has_signal);
 
2698
 
__attribute__((nonnull, warn_unused_result))
 
2699
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
2700
 
                                         sigset_t *const sigmask){
 
2701
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
2702
 
  uintmax_t scanned_sigmask;
 
2703
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
2706
 
  if(sigemptyset(sigmask) != 0){
 
2709
 
  for(int signum = 1; signum < NSIG; signum++){
 
2710
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
2711
 
      if(sigaddset(sigmask, signum) != 0){
 
2719
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2720
 
                                             task_queue *const queue);
 
2723
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
2724
 
                                             test_fixture *fixture,
 
2725
 
                                             __attribute__((unused))
 
2726
 
                                             gconstpointer user_data){
 
2728
 
  bool mandos_client_exited = false;
 
2729
 
  bool quit_now = false;
 
2731
 
  __attribute__((cleanup(cleanup_queue)))
 
2732
 
    task_queue *queue = create_queue();
 
2733
 
  g_assert_nonnull(queue);
 
2734
 
  const task_context task = {
 
2735
 
    .func=wait_for_mandos_client_exit,
 
2737
 
    .mandos_client_exited=&mandos_client_exited,
 
2738
 
    .quit_now=&quit_now,
 
2740
 
  run_task_with_stderr_to_dev_null(task, queue);
 
2742
 
  g_assert_false(mandos_client_exited);
 
2743
 
  g_assert_true(quit_now);
 
2744
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2747
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2748
 
                                             task_queue *const queue){
 
2749
 
  FILE *real_stderr = stderr;
 
2750
 
  FILE *devnull = fopen("/dev/null", "we");
 
2751
 
  g_assert_nonnull(devnull);
 
2754
 
  task.func(task, queue);
 
2755
 
  stderr = real_stderr;
 
2757
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
2761
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
2762
 
                                             __attribute__((unused))
 
2763
 
                                             gconstpointer user_data){
 
2764
 
  bool mandos_client_exited = false;
 
2765
 
  bool quit_now = false;
 
2767
 
  pid_t create_eternal_process(void){
 
2768
 
    const pid_t pid = fork();
 
2769
 
    if(pid == 0){               /* Child */
 
2770
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2771
 
        _exit(EXIT_FAILURE);
 
2773
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2774
 
        _exit(EXIT_FAILURE);
 
2782
 
  pid_t pid = create_eternal_process();
 
2783
 
  g_assert_true(pid != -1);
 
2785
 
  __attribute__((cleanup(cleanup_queue)))
 
2786
 
    task_queue *queue = create_queue();
 
2787
 
  g_assert_nonnull(queue);
 
2788
 
  const task_context task = {
 
2789
 
    .func=wait_for_mandos_client_exit,
 
2791
 
    .mandos_client_exited=&mandos_client_exited,
 
2792
 
    .quit_now=&quit_now,
 
2794
 
  task.func(task, queue);
 
2796
 
  g_assert_false(mandos_client_exited);
 
2797
 
  g_assert_false(quit_now);
 
2798
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
2800
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
2801
 
        .func=wait_for_mandos_client_exit,
 
2803
 
        .mandos_client_exited=&mandos_client_exited,
 
2804
 
        .quit_now=&quit_now,
 
2809
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
2810
 
                                              __attribute__((unused))
 
2813
 
  bool mandos_client_exited = false;
 
2814
 
  bool quit_now = false;
 
2816
 
  pid_t create_successful_process(void){
 
2817
 
    const pid_t pid = fork();
 
2818
 
    if(pid == 0){               /* Child */
 
2819
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2820
 
        _exit(EXIT_FAILURE);
 
2822
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2823
 
        _exit(EXIT_FAILURE);
 
2829
 
  const pid_t pid = create_successful_process();
 
2830
 
  g_assert_true(pid != -1);
 
2832
 
  __attribute__((cleanup(cleanup_queue)))
 
2833
 
    task_queue *queue = create_queue();
 
2834
 
  g_assert_nonnull(queue);
 
2835
 
  const task_context initial_task = {
 
2836
 
    .func=wait_for_mandos_client_exit,
 
2838
 
    .mandos_client_exited=&mandos_client_exited,
 
2839
 
    .quit_now=&quit_now,
 
2841
 
  g_assert_true(add_to_queue(queue, initial_task));
 
2843
 
  struct timespec starttime, currtime;
 
2844
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2845
 
  __attribute__((cleanup(cleanup_close)))
 
2846
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2848
 
    queue->next_run = 0;
 
2849
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2850
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
2851
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2852
 
  } while((not mandos_client_exited)
 
2854
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2856
 
  g_assert_true(mandos_client_exited);
 
2857
 
  g_assert_false(quit_now);
 
2858
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2862
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
2863
 
                                              __attribute__((unused))
 
2866
 
  bool mandos_client_exited = false;
 
2867
 
  bool quit_now = false;
 
2869
 
  pid_t create_failing_process(void){
 
2870
 
    const pid_t pid = fork();
 
2871
 
    if(pid == 0){               /* Child */
 
2872
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2873
 
        _exit(EXIT_FAILURE);
 
2875
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2876
 
        _exit(EXIT_FAILURE);
 
2882
 
  const pid_t pid = create_failing_process();
 
2883
 
  g_assert_true(pid != -1);
 
2885
 
  __attribute__((cleanup(string_set_clear)))
 
2886
 
    string_set cancelled_filenames = {};
 
2887
 
  __attribute__((cleanup(cleanup_close)))
 
2888
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2889
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2890
 
  __attribute__((cleanup(cleanup_queue)))
 
2891
 
    task_queue *queue = create_queue();
 
2892
 
  g_assert_nonnull(queue);
 
2893
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2894
 
        .func=wait_for_mandos_client_exit,
 
2896
 
        .mandos_client_exited=&mandos_client_exited,
 
2897
 
        .quit_now=&quit_now,
 
2900
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
2902
 
  __attribute__((cleanup(cleanup_close)))
 
2903
 
    const int devnull_fd = open("/dev/null",
 
2904
 
                                O_WRONLY | O_CLOEXEC);
 
2905
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2906
 
  __attribute__((cleanup(cleanup_close)))
 
2907
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2908
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2910
 
  struct timespec starttime, currtime;
 
2911
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2913
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2914
 
    dup2(devnull_fd, STDERR_FILENO);
 
2915
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2917
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2922
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2923
 
  } while((not mandos_client_exited)
 
2925
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2927
 
  g_assert_true(quit_now);
 
2928
 
  g_assert_true(mandos_client_exited);
 
2929
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2933
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
2934
 
                                             __attribute__((unused))
 
2935
 
                                             gconstpointer user_data){
 
2936
 
  bool mandos_client_exited = false;
 
2937
 
  bool quit_now = false;
 
2939
 
  pid_t create_killed_process(void){
 
2940
 
    const pid_t pid = fork();
 
2941
 
    if(pid == 0){               /* Child */
 
2942
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2943
 
        _exit(EXIT_FAILURE);
 
2945
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2946
 
        _exit(EXIT_FAILURE);
 
2955
 
  const pid_t pid = create_killed_process();
 
2956
 
  g_assert_true(pid != -1);
 
2958
 
  __attribute__((cleanup(string_set_clear)))
 
2959
 
    string_set cancelled_filenames = {};
 
2960
 
  __attribute__((cleanup(cleanup_close)))
 
2961
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2962
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2963
 
  __attribute__((cleanup(cleanup_queue)))
 
2964
 
    task_queue *queue = create_queue();
 
2965
 
  g_assert_nonnull(queue);
 
2966
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2967
 
        .func=wait_for_mandos_client_exit,
 
2969
 
        .mandos_client_exited=&mandos_client_exited,
 
2970
 
        .quit_now=&quit_now,
 
2973
 
  __attribute__((cleanup(cleanup_close)))
 
2974
 
    const int devnull_fd = open("/dev/null",
 
2975
 
                                O_WRONLY | O_CLOEXEC);
 
2976
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2977
 
  __attribute__((cleanup(cleanup_close)))
 
2978
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2979
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2981
 
  struct timespec starttime, currtime;
 
2982
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2984
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2985
 
    dup2(devnull_fd, STDERR_FILENO);
 
2986
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2988
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2993
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2994
 
  } while((not mandos_client_exited)
 
2996
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2998
 
  g_assert_true(mandos_client_exited);
 
2999
 
  g_assert_true(quit_now);
 
3000
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3003
 
static bool epoll_set_does_not_contain(int, int);
 
3006
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
3007
 
                                              test_fixture *fixture,
 
3008
 
                                              __attribute__((unused))
 
3011
 
  __attribute__((cleanup(cleanup_close)))
 
3012
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3013
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3015
 
  __attribute__((cleanup(cleanup_buffer)))
 
3016
 
    buffer password = {};
 
3018
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
3019
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3021
 
  bool password_is_read = false;
 
3022
 
  bool quit_now = false;
 
3023
 
  __attribute__((cleanup(cleanup_queue)))
 
3024
 
    task_queue *queue = create_queue();
 
3025
 
  g_assert_nonnull(queue);
 
3027
 
  task_context task = {
 
3028
 
    .func=read_mandos_client_output,
 
3031
 
    .password=&password,
 
3032
 
    .password_is_read=&password_is_read,
 
3033
 
    .quit_now=&quit_now,
 
3035
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3036
 
  g_assert_false(password_is_read);
 
3037
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3038
 
  g_assert_true(quit_now);
 
3039
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3041
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3043
 
  g_assert_cmpint(close(fd), ==, -1);
 
3046
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
3047
 
  return not epoll_set_contains(epoll_fd, fd, 0);
 
3051
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
3052
 
                                           test_fixture *fixture,
 
3053
 
                                           __attribute__((unused))
 
3054
 
                                           gconstpointer user_data){
 
3055
 
  __attribute__((cleanup(cleanup_close)))
 
3056
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3057
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3060
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3062
 
  __attribute__((cleanup(cleanup_buffer)))
 
3063
 
    buffer password = {};
 
3065
 
  bool password_is_read = false;
 
3066
 
  bool quit_now = false;
 
3067
 
  __attribute__((cleanup(cleanup_queue)))
 
3068
 
    task_queue *queue = create_queue();
 
3069
 
  g_assert_nonnull(queue);
 
3071
 
  task_context task = {
 
3072
 
    .func=read_mandos_client_output,
 
3075
 
    .password=&password,
 
3076
 
    .password_is_read=&password_is_read,
 
3077
 
    .quit_now=&quit_now,
 
3079
 
  task.func(task, queue);
 
3080
 
  g_assert_false(password_is_read);
 
3081
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3082
 
  g_assert_false(quit_now);
 
3083
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3085
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3086
 
        .func=read_mandos_client_output,
 
3089
 
        .password=&password,
 
3090
 
        .password_is_read=&password_is_read,
 
3091
 
        .quit_now=&quit_now,
 
3094
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3095
 
                                   EPOLLIN | EPOLLRDHUP));
 
3097
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3100
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
3101
 
                                               test_fixture *fixture,
 
3102
 
                                               __attribute__((unused))
 
3105
 
  __attribute__((cleanup(cleanup_close)))
 
3106
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3107
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3110
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3111
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3113
 
  __attribute__((cleanup(cleanup_buffer)))
 
3114
 
    buffer password = {};
 
3116
 
  bool password_is_read = false;
 
3117
 
  bool quit_now = false;
 
3118
 
  __attribute__((cleanup(cleanup_queue)))
 
3119
 
    task_queue *queue = create_queue();
 
3120
 
  g_assert_nonnull(queue);
 
3122
 
  task_context task = {
 
3123
 
    .func=read_mandos_client_output,
 
3126
 
    .password=&password,
 
3127
 
    .password_is_read=&password_is_read,
 
3128
 
    .quit_now=&quit_now,
 
3130
 
  task.func(task, queue);
 
3131
 
  g_assert_true(password_is_read);
 
3132
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3133
 
  g_assert_false(quit_now);
 
3134
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3136
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3138
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3142
 
void test_read_mandos_client_output_once(__attribute__((unused))
 
3143
 
                                         test_fixture *fixture,
 
3144
 
                                         __attribute__((unused))
 
3145
 
                                         gconstpointer user_data){
 
3146
 
  __attribute__((cleanup(cleanup_close)))
 
3147
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3148
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3151
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3153
 
  const char dummy_test_password[] = "dummy test password";
 
3154
 
  /* Start with a pre-allocated buffer */
 
3155
 
  __attribute__((cleanup(cleanup_buffer)))
 
3157
 
    .data=malloc(sizeof(dummy_test_password)),
 
3159
 
    .allocated=sizeof(dummy_test_password),
 
3161
 
  g_assert_nonnull(password.data);
 
3162
 
  if(mlock(password.data, password.allocated) != 0){
 
3163
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3166
 
  bool password_is_read = false;
 
3167
 
  bool quit_now = false;
 
3168
 
  __attribute__((cleanup(cleanup_queue)))
 
3169
 
    task_queue *queue = create_queue();
 
3170
 
  g_assert_nonnull(queue);
 
3172
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3173
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3174
 
                             sizeof(dummy_test_password)),
 
3175
 
                  ==, (int)sizeof(dummy_test_password));
 
3177
 
  task_context task = {
 
3178
 
    .func=read_mandos_client_output,
 
3181
 
    .password=&password,
 
3182
 
    .password_is_read=&password_is_read,
 
3183
 
    .quit_now=&quit_now,
 
3185
 
  task.func(task, queue);
 
3187
 
  g_assert_false(password_is_read);
 
3188
 
  g_assert_cmpint((int)password.length, ==,
 
3189
 
                  (int)sizeof(dummy_test_password));
 
3190
 
  g_assert_nonnull(password.data);
 
3191
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3192
 
                         sizeof(dummy_test_password)), ==, 0);
 
3194
 
  g_assert_false(quit_now);
 
3195
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3197
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3198
 
        .func=read_mandos_client_output,
 
3201
 
        .password=&password,
 
3202
 
        .password_is_read=&password_is_read,
 
3203
 
        .quit_now=&quit_now,
 
3206
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3207
 
                                   EPOLLIN | EPOLLRDHUP));
 
3209
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3213
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
3214
 
                                           test_fixture *fixture,
 
3215
 
                                           __attribute__((unused))
 
3216
 
                                           gconstpointer user_data){
 
3217
 
  __attribute__((cleanup(cleanup_close)))
 
3218
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3219
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3222
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3224
 
  const char dummy_test_password[] = "dummy test password";
 
3225
 
  /* Start with an empty buffer */
 
3226
 
  __attribute__((cleanup(cleanup_buffer)))
 
3227
 
    buffer password = {};
 
3229
 
  bool password_is_read = false;
 
3230
 
  bool quit_now = false;
 
3231
 
  __attribute__((cleanup(cleanup_queue)))
 
3232
 
    task_queue *queue = create_queue();
 
3233
 
  g_assert_nonnull(queue);
 
3235
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3236
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3237
 
                             sizeof(dummy_test_password)),
 
3238
 
                  ==, (int)sizeof(dummy_test_password));
 
3240
 
  task_context task = {
 
3241
 
    .func=read_mandos_client_output,
 
3244
 
    .password=&password,
 
3245
 
    .password_is_read=&password_is_read,
 
3246
 
    .quit_now=&quit_now,
 
3248
 
  task.func(task, queue);
 
3250
 
  g_assert_false(password_is_read);
 
3251
 
  g_assert_cmpint((int)password.length, ==,
 
3252
 
                  (int)sizeof(dummy_test_password));
 
3253
 
  g_assert_nonnull(password.data);
 
3254
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3255
 
                         sizeof(dummy_test_password)), ==, 0);
 
3257
 
  g_assert_false(quit_now);
 
3258
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3260
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3261
 
        .func=read_mandos_client_output,
 
3264
 
        .password=&password,
 
3265
 
        .password_is_read=&password_is_read,
 
3266
 
        .quit_now=&quit_now,
 
3269
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3270
 
                                   EPOLLIN | EPOLLRDHUP));
 
3272
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3276
 
void test_read_mandos_client_output_append(__attribute__((unused))
 
3277
 
                                           test_fixture *fixture,
 
3278
 
                                           __attribute__((unused))
 
3279
 
                                           gconstpointer user_data){
 
3280
 
  __attribute__((cleanup(cleanup_close)))
 
3281
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3282
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3285
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3287
 
  const char dummy_test_password[] = "dummy test password";
 
3288
 
  __attribute__((cleanup(cleanup_buffer)))
 
3290
 
    .data=malloc(PIPE_BUF),
 
3292
 
    .allocated=PIPE_BUF,
 
3294
 
  g_assert_nonnull(password.data);
 
3295
 
  if(mlock(password.data, password.allocated) != 0){
 
3296
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3299
 
  memset(password.data, 'x', PIPE_BUF);
 
3300
 
  char password_expected[PIPE_BUF];
 
3301
 
  memcpy(password_expected, password.data, PIPE_BUF);
 
3303
 
  bool password_is_read = false;
 
3304
 
  bool quit_now = false;
 
3305
 
  __attribute__((cleanup(cleanup_queue)))
 
3306
 
    task_queue *queue = create_queue();
 
3307
 
  g_assert_nonnull(queue);
 
3309
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3310
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3311
 
                             sizeof(dummy_test_password)),
 
3312
 
                  ==, (int)sizeof(dummy_test_password));
 
3314
 
  task_context task = {
 
3315
 
    .func=read_mandos_client_output,
 
3318
 
    .password=&password,
 
3319
 
    .password_is_read=&password_is_read,
 
3320
 
    .quit_now=&quit_now,
 
3322
 
  task.func(task, queue);
 
3324
 
  g_assert_false(password_is_read);
 
3325
 
  g_assert_cmpint((int)password.length, ==,
 
3326
 
                  PIPE_BUF + sizeof(dummy_test_password));
 
3327
 
  g_assert_nonnull(password.data);
 
3328
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
3330
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
3331
 
                         dummy_test_password,
 
3332
 
                         sizeof(dummy_test_password)), ==, 0);
 
3333
 
  g_assert_false(quit_now);
 
3334
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3336
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3337
 
        .func=read_mandos_client_output,
 
3340
 
        .password=&password,
 
3341
 
        .password_is_read=&password_is_read,
 
3342
 
        .quit_now=&quit_now,
 
3345
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3346
 
                                   EPOLLIN | EPOLLRDHUP));
 
3349
 
static char *make_temporary_directory(void);
 
3351
 
static void test_add_inotify_dir_watch(__attribute__((unused))
 
3352
 
                                       test_fixture *fixture,
 
3353
 
                                       __attribute__((unused))
 
3354
 
                                       gconstpointer user_data){
 
3355
 
  __attribute__((cleanup(cleanup_close)))
 
3356
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3357
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3358
 
  __attribute__((cleanup(cleanup_queue)))
 
3359
 
    task_queue *queue = create_queue();
 
3360
 
  g_assert_nonnull(queue);
 
3361
 
  __attribute__((cleanup(string_set_clear)))
 
3362
 
    string_set cancelled_filenames = {};
 
3363
 
  const mono_microsecs current_time = 0;
 
3365
 
  bool quit_now = false;
 
3366
 
  buffer password = {};
 
3367
 
  bool mandos_client_exited = false;
 
3368
 
  bool password_is_read = false;
 
3370
 
  __attribute__((cleanup(cleanup_string)))
 
3371
 
    char *tempdir = make_temporary_directory();
 
3372
 
  g_assert_nonnull(tempdir);
 
3374
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3376
 
                                      &cancelled_filenames,
 
3378
 
                                      &mandos_client_exited,
 
3379
 
                                      &password_is_read));
 
3381
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3383
 
  const task_context *const added_read_task
 
3384
 
    = find_matching_task(queue, (task_context){
 
3385
 
        .func=read_inotify_event,
 
3387
 
        .quit_now=&quit_now,
 
3388
 
        .password=&password,
 
3390
 
        .cancelled_filenames=&cancelled_filenames,
 
3391
 
        .current_time=¤t_time,
 
3392
 
        .mandos_client_exited=&mandos_client_exited,
 
3393
 
        .password_is_read=&password_is_read,
 
3395
 
  g_assert_nonnull(added_read_task);
 
3397
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3398
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3399
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
3400
 
                                   added_read_task->fd,
 
3401
 
                                   EPOLLIN | EPOLLRDHUP));
 
3403
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3406
 
static char *make_temporary_directory(void){
 
3407
 
  char *name = strdup("/tmp/mandosXXXXXX");
 
3408
 
  g_assert_nonnull(name);
 
3409
 
  char *result = mkdtemp(name);
 
3416
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
3417
 
                                            test_fixture *fixture,
 
3418
 
                                            __attribute__((unused))
 
3419
 
                                            gconstpointer user_data){
 
3420
 
  __attribute__((cleanup(cleanup_close)))
 
3421
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3422
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3423
 
  __attribute__((cleanup(cleanup_queue)))
 
3424
 
    task_queue *queue = create_queue();
 
3425
 
  g_assert_nonnull(queue);
 
3426
 
  __attribute__((cleanup(string_set_clear)))
 
3427
 
    string_set cancelled_filenames = {};
 
3428
 
  const mono_microsecs current_time = 0;
 
3430
 
  bool quit_now = false;
 
3431
 
  buffer password = {};
 
3432
 
  bool mandos_client_exited = false;
 
3433
 
  bool password_is_read = false;
 
3435
 
  const char nonexistent_dir[] = "/nonexistent";
 
3437
 
  FILE *real_stderr = stderr;
 
3438
 
  FILE *devnull = fopen("/dev/null", "we");
 
3439
 
  g_assert_nonnull(devnull);
 
3441
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3442
 
                                       &password, nonexistent_dir,
 
3443
 
                                       &cancelled_filenames,
 
3445
 
                                       &mandos_client_exited,
 
3446
 
                                       &password_is_read));
 
3447
 
  stderr = real_stderr;
 
3448
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3450
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3453
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
3454
 
                                              test_fixture *fixture,
 
3455
 
                                              __attribute__((unused))
 
3458
 
  __attribute__((cleanup(cleanup_close)))
 
3459
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3460
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3461
 
  __attribute__((cleanup(cleanup_queue)))
 
3462
 
    task_queue *queue = create_queue();
 
3463
 
  g_assert_nonnull(queue);
 
3464
 
  __attribute__((cleanup(string_set_clear)))
 
3465
 
    string_set cancelled_filenames = {};
 
3466
 
  const mono_microsecs current_time = 0;
 
3468
 
  bool quit_now = false;
 
3469
 
  buffer password = {};
 
3470
 
  bool mandos_client_exited = false;
 
3471
 
  bool password_is_read = false;
 
3473
 
  __attribute__((cleanup(cleanup_string)))
 
3474
 
    char *tempdir = make_temporary_directory();
 
3475
 
  g_assert_nonnull(tempdir);
 
3477
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3479
 
                                      &cancelled_filenames,
 
3481
 
                                      &mandos_client_exited,
 
3482
 
                                      &password_is_read));
 
3484
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3486
 
  const task_context *const added_read_task
 
3487
 
    = find_matching_task(queue,
 
3488
 
                         (task_context){ .func=read_inotify_event });
 
3489
 
  g_assert_nonnull(added_read_task);
 
3491
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3492
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3494
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3495
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3497
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3498
 
  g_assert_nonnull(ievent);
 
3500
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
3502
 
  g_assert_cmpint(errno, ==, EAGAIN);
 
3506
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3509
 
static char *make_temporary_file_in_directory(const char
 
3513
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
3514
 
                                               test_fixture *fixture,
 
3515
 
                                               __attribute__((unused))
 
3518
 
  __attribute__((cleanup(cleanup_close)))
 
3519
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3520
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3521
 
  __attribute__((cleanup(cleanup_queue)))
 
3522
 
    task_queue *queue = create_queue();
 
3523
 
  g_assert_nonnull(queue);
 
3524
 
  __attribute__((cleanup(string_set_clear)))
 
3525
 
    string_set cancelled_filenames = {};
 
3526
 
  const mono_microsecs current_time = 0;
 
3528
 
  bool quit_now = false;
 
3529
 
  buffer password = {};
 
3530
 
  bool mandos_client_exited = false;
 
3531
 
  bool password_is_read = false;
 
3533
 
  __attribute__((cleanup(cleanup_string)))
 
3534
 
    char *tempdir = make_temporary_directory();
 
3535
 
  g_assert_nonnull(tempdir);
 
3537
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3539
 
                                      &cancelled_filenames,
 
3541
 
                                      &mandos_client_exited,
 
3542
 
                                      &password_is_read));
 
3544
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3546
 
  const task_context *const added_read_task
 
3547
 
    = find_matching_task(queue,
 
3548
 
                         (task_context){ .func=read_inotify_event });
 
3549
 
  g_assert_nonnull(added_read_task);
 
3551
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3552
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3554
 
  __attribute__((cleanup(cleanup_string)))
 
3555
 
    char *filename = make_temporary_file_in_directory(tempdir);
 
3556
 
  g_assert_nonnull(filename);
 
3558
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3559
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3561
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3562
 
  g_assert_nonnull(ievent);
 
3564
 
  ssize_t read_size = 0;
 
3565
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3567
 
  g_assert_cmpint((int)read_size, >, 0);
 
3568
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
3569
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
3573
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
3574
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3577
 
static char *make_temporary_prefixed_file_in_directory(const char
 
3581
 
  char *filename = NULL;
 
3582
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
3584
 
  g_assert_nonnull(filename);
 
3585
 
  const int fd = mkostemp(filename, O_CLOEXEC);
 
3586
 
  g_assert_cmpint(fd, >=, 0);
 
3587
 
  g_assert_cmpint(close(fd), ==, 0);
 
3591
 
static char *make_temporary_file_in_directory(const char
 
3593
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
3597
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
3598
 
                                            test_fixture *fixture,
 
3599
 
                                            __attribute__((unused))
 
3600
 
                                            gconstpointer user_data){
 
3601
 
  __attribute__((cleanup(cleanup_close)))
 
3602
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3603
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3604
 
  __attribute__((cleanup(cleanup_queue)))
 
3605
 
    task_queue *queue = create_queue();
 
3606
 
  g_assert_nonnull(queue);
 
3607
 
  __attribute__((cleanup(string_set_clear)))
 
3608
 
    string_set cancelled_filenames = {};
 
3609
 
  const mono_microsecs current_time = 0;
 
3611
 
  bool quit_now = false;
 
3612
 
  buffer password = {};
 
3613
 
  bool mandos_client_exited = false;
 
3614
 
  bool password_is_read = false;
 
3616
 
  __attribute__((cleanup(cleanup_string)))
 
3617
 
    char *watchdir = make_temporary_directory();
 
3618
 
  g_assert_nonnull(watchdir);
 
3620
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3621
 
                                      &password, watchdir,
 
3622
 
                                      &cancelled_filenames,
 
3624
 
                                      &mandos_client_exited,
 
3625
 
                                      &password_is_read));
 
3627
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3629
 
  const task_context *const added_read_task
 
3630
 
    = find_matching_task(queue,
 
3631
 
                         (task_context){ .func=read_inotify_event });
 
3632
 
  g_assert_nonnull(added_read_task);
 
3634
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3635
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3637
 
  char *sourcedir = make_temporary_directory();
 
3638
 
  g_assert_nonnull(sourcedir);
 
3640
 
  __attribute__((cleanup(cleanup_string)))
 
3641
 
    char *filename = make_temporary_file_in_directory(sourcedir);
 
3642
 
  g_assert_nonnull(filename);
 
3644
 
  __attribute__((cleanup(cleanup_string)))
 
3645
 
    char *targetfilename = NULL;
 
3646
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
3647
 
                           basename(filename)), >, 0);
 
3648
 
  g_assert_nonnull(targetfilename);
 
3650
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
3651
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
3654
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3655
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3657
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3658
 
  g_assert_nonnull(ievent);
 
3660
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3662
 
  g_assert_cmpint((int)read_size, >, 0);
 
3663
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
3664
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
3668
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3669
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
3673
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
3674
 
                                          test_fixture *fixture,
 
3675
 
                                          __attribute__((unused))
 
3676
 
                                          gconstpointer user_data){
 
3677
 
  __attribute__((cleanup(cleanup_close)))
 
3678
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3679
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3680
 
  __attribute__((cleanup(cleanup_queue)))
 
3681
 
    task_queue *queue = create_queue();
 
3682
 
  g_assert_nonnull(queue);
 
3683
 
  __attribute__((cleanup(string_set_clear)))
 
3684
 
    string_set cancelled_filenames = {};
 
3685
 
  const mono_microsecs current_time = 0;
 
3687
 
  bool quit_now = false;
 
3688
 
  buffer password = {};
 
3689
 
  bool mandos_client_exited = false;
 
3690
 
  bool password_is_read = false;
 
3692
 
  __attribute__((cleanup(cleanup_string)))
 
3693
 
    char *tempdir = make_temporary_directory();
 
3694
 
  g_assert_nonnull(tempdir);
 
3696
 
  __attribute__((cleanup(cleanup_string)))
 
3697
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3698
 
  g_assert_nonnull(tempfile);
 
3700
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3702
 
                                      &cancelled_filenames,
 
3704
 
                                      &mandos_client_exited,
 
3705
 
                                      &password_is_read));
 
3706
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3708
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3710
 
  const task_context *const added_read_task
 
3711
 
    = find_matching_task(queue,
 
3712
 
                         (task_context){ .func=read_inotify_event });
 
3713
 
  g_assert_nonnull(added_read_task);
 
3715
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3716
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3718
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3719
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3721
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3722
 
  g_assert_nonnull(ievent);
 
3724
 
  ssize_t read_size = 0;
 
3725
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3727
 
  g_assert_cmpint((int)read_size, >, 0);
 
3728
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3729
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3733
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3736
 
static void test_read_inotify_event_readerror(__attribute__((unused))
 
3737
 
                                              test_fixture *fixture,
 
3738
 
                                              __attribute__((unused))
 
3741
 
  __attribute__((cleanup(cleanup_close)))
 
3742
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3743
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3744
 
  const mono_microsecs current_time = 0;
 
3746
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
3747
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3749
 
  bool quit_now = false;
 
3750
 
  __attribute__((cleanup(cleanup_queue)))
 
3751
 
    task_queue *queue = create_queue();
 
3752
 
  g_assert_nonnull(queue);
 
3754
 
  task_context task = {
 
3755
 
    .func=read_inotify_event,
 
3758
 
    .quit_now=&quit_now,
 
3759
 
    .filename=strdup("/nonexistent"),
 
3760
 
    .cancelled_filenames = &(string_set){},
 
3762
 
    .current_time=¤t_time,
 
3764
 
  g_assert_nonnull(task.filename);
 
3765
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3766
 
  g_assert_true(quit_now);
 
3767
 
  g_assert_true(queue->next_run == 0);
 
3768
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3770
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3772
 
  g_assert_cmpint(close(fd), ==, -1);
 
3775
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
3776
 
                                              test_fixture *fixture,
 
3777
 
                                              __attribute__((unused))
 
3780
 
  const mono_microsecs current_time = 17;
 
3783
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3784
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
3786
 
  bool quit_now = false;
 
3787
 
  buffer password = {};
 
3788
 
  bool mandos_client_exited = false;
 
3789
 
  bool password_is_read = false;
 
3790
 
  __attribute__((cleanup(cleanup_queue)))
 
3791
 
    task_queue *queue = create_queue();
 
3792
 
  g_assert_nonnull(queue);
 
3794
 
  task_context task = {
 
3795
 
    .func=read_inotify_event,
 
3798
 
    .quit_now=&quit_now,
 
3799
 
    .password=&password,
 
3800
 
    .filename=strdup("/nonexistent"),
 
3801
 
    .cancelled_filenames = &(string_set){},
 
3803
 
    .current_time=¤t_time,
 
3804
 
    .mandos_client_exited=&mandos_client_exited,
 
3805
 
    .password_is_read=&password_is_read,
 
3807
 
  g_assert_nonnull(task.filename);
 
3808
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3810
 
  g_assert_nonnull(find_matching_task(queue, task));
 
3811
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
3813
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
3814
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3817
 
static void test_read_inotify_event_nodata(__attribute__((unused))
 
3818
 
                                           test_fixture *fixture,
 
3819
 
                                           __attribute__((unused))
 
3820
 
                                           gconstpointer user_data){
 
3821
 
  __attribute__((cleanup(cleanup_close)))
 
3822
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3823
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3824
 
  const mono_microsecs current_time = 0;
 
3827
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3829
 
  bool quit_now = false;
 
3830
 
  buffer password = {};
 
3831
 
  bool mandos_client_exited = false;
 
3832
 
  bool password_is_read = false;
 
3833
 
  __attribute__((cleanup(cleanup_queue)))
 
3834
 
    task_queue *queue = create_queue();
 
3835
 
  g_assert_nonnull(queue);
 
3837
 
  task_context task = {
 
3838
 
    .func=read_inotify_event,
 
3841
 
    .quit_now=&quit_now,
 
3842
 
    .password=&password,
 
3843
 
    .filename=strdup("/nonexistent"),
 
3844
 
    .cancelled_filenames = &(string_set){},
 
3846
 
    .current_time=¤t_time,
 
3847
 
    .mandos_client_exited=&mandos_client_exited,
 
3848
 
    .password_is_read=&password_is_read,
 
3850
 
  g_assert_nonnull(task.filename);
 
3851
 
  task.func(task, queue);
 
3852
 
  g_assert_false(quit_now);
 
3853
 
  g_assert_true(queue->next_run == 0);
 
3854
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3856
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3857
 
        .func=read_inotify_event,
 
3860
 
        .quit_now=&quit_now,
 
3861
 
        .password=&password,
 
3862
 
        .filename=task.filename,
 
3863
 
        .cancelled_filenames=task.cancelled_filenames,
 
3864
 
        .current_time=¤t_time,
 
3865
 
        .mandos_client_exited=&mandos_client_exited,
 
3866
 
        .password_is_read=&password_is_read,
 
3869
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3870
 
                                   EPOLLIN | EPOLLRDHUP));
 
3872
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3875
 
static void test_read_inotify_event_eof(__attribute__((unused))
 
3876
 
                                        test_fixture *fixture,
 
3877
 
                                        __attribute__((unused))
 
3878
 
                                        gconstpointer user_data){
 
3879
 
  __attribute__((cleanup(cleanup_close)))
 
3880
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3881
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3882
 
  const mono_microsecs current_time = 0;
 
3885
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3886
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3888
 
  bool quit_now = false;
 
3889
 
  buffer password = {};
 
3890
 
  __attribute__((cleanup(cleanup_queue)))
 
3891
 
    task_queue *queue = create_queue();
 
3892
 
  g_assert_nonnull(queue);
 
3894
 
  task_context task = {
 
3895
 
    .func=read_inotify_event,
 
3898
 
    .quit_now=&quit_now,
 
3899
 
    .password=&password,
 
3900
 
    .filename=strdup("/nonexistent"),
 
3901
 
    .cancelled_filenames = &(string_set){},
 
3903
 
    .current_time=¤t_time,
 
3905
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3906
 
  g_assert_true(quit_now);
 
3907
 
  g_assert_true(queue->next_run == 0);
 
3908
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3910
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3912
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3916
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
3917
 
                                            test_fixture *fixture,
 
3918
 
                                            __attribute__((unused))
 
3919
 
                                            gconstpointer user_data){
 
3920
 
  __attribute__((cleanup(cleanup_close)))
 
3921
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3922
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3923
 
  const mono_microsecs current_time = 0;
 
3926
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3928
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3929
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
3931
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
3932
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
3933
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
3936
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
3937
 
  ievent->mask = IN_CLOSE_WRITE;
 
3938
 
  ievent->len = sizeof(dummy_file_name);
 
3939
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
3940
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3941
 
                              + sizeof(dummy_file_name));
 
3942
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
3944
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3946
 
  bool quit_now = false;
 
3947
 
  buffer password = {};
 
3948
 
  bool mandos_client_exited = false;
 
3949
 
  bool password_is_read = false;
 
3950
 
  __attribute__((cleanup(cleanup_queue)))
 
3951
 
    task_queue *queue = create_queue();
 
3952
 
  g_assert_nonnull(queue);
 
3954
 
  task_context task = {
 
3955
 
    .func=read_inotify_event,
 
3958
 
    .quit_now=&quit_now,
 
3959
 
    .password=&password,
 
3960
 
    .filename=strdup("/nonexistent"),
 
3961
 
    .cancelled_filenames = &(string_set){},
 
3963
 
    .current_time=¤t_time,
 
3964
 
    .mandos_client_exited=&mandos_client_exited,
 
3965
 
    .password_is_read=&password_is_read,
 
3967
 
  task.func(task, queue);
 
3968
 
  g_assert_false(quit_now);
 
3969
 
  g_assert_true(queue->next_run != 0);
 
3970
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
3972
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3973
 
        .func=read_inotify_event,
 
3976
 
        .quit_now=&quit_now,
 
3977
 
        .password=&password,
 
3978
 
        .filename=task.filename,
 
3979
 
        .cancelled_filenames=task.cancelled_filenames,
 
3980
 
        .current_time=¤t_time,
 
3981
 
        .mandos_client_exited=&mandos_client_exited,
 
3982
 
        .password_is_read=&password_is_read,
 
3985
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3986
 
                                   EPOLLIN | EPOLLRDHUP));
 
3988
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
3990
 
  __attribute__((cleanup(cleanup_string)))
 
3991
 
    char *filename = NULL;
 
3992
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
3993
 
                           dummy_file_name), >, 0);
 
3994
 
  g_assert_nonnull(filename);
 
3995
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3996
 
        .func=open_and_parse_question,
 
3999
 
        .question_filename=filename,
 
4000
 
        .password=&password,
 
4001
 
        .cancelled_filenames=task.cancelled_filenames,
 
4002
 
        .current_time=¤t_time,
 
4003
 
        .mandos_client_exited=&mandos_client_exited,
 
4004
 
        .password_is_read=&password_is_read,
 
4009
 
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
4010
 
                                         test_fixture *fixture,
 
4011
 
                                         __attribute__((unused))
 
4012
 
                                         gconstpointer user_data){
 
4013
 
  __attribute__((cleanup(cleanup_close)))
 
4014
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4015
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4016
 
  const mono_microsecs current_time = 0;
 
4019
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4021
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4022
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4024
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4025
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4026
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
4029
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4030
 
  ievent->mask = IN_MOVED_TO;
 
4031
 
  ievent->len = sizeof(dummy_file_name);
 
4032
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4033
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4034
 
                              + sizeof(dummy_file_name));
 
4035
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4037
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4039
 
  bool quit_now = false;
 
4040
 
  buffer password = {};
 
4041
 
  bool mandos_client_exited = false;
 
4042
 
  bool password_is_read = false;
 
4043
 
  __attribute__((cleanup(cleanup_queue)))
 
4044
 
    task_queue *queue = create_queue();
 
4045
 
  g_assert_nonnull(queue);
 
4047
 
  task_context task = {
 
4048
 
    .func=read_inotify_event,
 
4051
 
    .quit_now=&quit_now,
 
4052
 
    .password=&password,
 
4053
 
    .filename=strdup("/nonexistent"),
 
4054
 
    .cancelled_filenames = &(string_set){},
 
4056
 
    .current_time=¤t_time,
 
4057
 
    .mandos_client_exited=&mandos_client_exited,
 
4058
 
    .password_is_read=&password_is_read,
 
4060
 
  task.func(task, queue);
 
4061
 
  g_assert_false(quit_now);
 
4062
 
  g_assert_true(queue->next_run != 0);
 
4063
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4065
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4066
 
        .func=read_inotify_event,
 
4069
 
        .quit_now=&quit_now,
 
4070
 
        .password=&password,
 
4071
 
        .filename=task.filename,
 
4072
 
        .cancelled_filenames=task.cancelled_filenames,
 
4073
 
        .current_time=¤t_time,
 
4074
 
        .mandos_client_exited=&mandos_client_exited,
 
4075
 
        .password_is_read=&password_is_read,
 
4078
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4079
 
                                   EPOLLIN | EPOLLRDHUP));
 
4081
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4083
 
  __attribute__((cleanup(cleanup_string)))
 
4084
 
    char *filename = NULL;
 
4085
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4086
 
                           dummy_file_name), >, 0);
 
4087
 
  g_assert_nonnull(filename);
 
4088
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4089
 
        .func=open_and_parse_question,
 
4092
 
        .question_filename=filename,
 
4093
 
        .password=&password,
 
4094
 
        .cancelled_filenames=task.cancelled_filenames,
 
4095
 
        .current_time=¤t_time,
 
4096
 
        .mandos_client_exited=&mandos_client_exited,
 
4097
 
        .password_is_read=&password_is_read,
 
4101
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
4102
 
                                              test_fixture *fixture,
 
4103
 
                                              __attribute__((unused))
 
4106
 
  __attribute__((cleanup(cleanup_close)))
 
4107
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4108
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4109
 
  __attribute__((cleanup(string_set_clear)))
 
4110
 
    string_set cancelled_filenames = {};
 
4111
 
  const mono_microsecs current_time = 0;
 
4114
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4116
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4117
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4119
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4120
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4121
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
4124
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4125
 
  ievent->mask = IN_DELETE;
 
4126
 
  ievent->len = sizeof(dummy_file_name);
 
4127
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4128
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4129
 
                              + sizeof(dummy_file_name));
 
4130
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4132
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4134
 
  bool quit_now = false;
 
4135
 
  buffer password = {};
 
4136
 
  bool mandos_client_exited = false;
 
4137
 
  bool password_is_read = false;
 
4138
 
  __attribute__((cleanup(cleanup_queue)))
 
4139
 
    task_queue *queue = create_queue();
 
4140
 
  g_assert_nonnull(queue);
 
4142
 
  task_context task = {
 
4143
 
    .func=read_inotify_event,
 
4146
 
    .quit_now=&quit_now,
 
4147
 
    .password=&password,
 
4148
 
    .filename=strdup("/nonexistent"),
 
4149
 
    .cancelled_filenames=&cancelled_filenames,
 
4150
 
    .current_time=¤t_time,
 
4151
 
    .mandos_client_exited=&mandos_client_exited,
 
4152
 
    .password_is_read=&password_is_read,
 
4154
 
  task.func(task, queue);
 
4155
 
  g_assert_false(quit_now);
 
4156
 
  g_assert_true(queue->next_run == 0);
 
4157
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4159
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4160
 
        .func=read_inotify_event,
 
4163
 
        .quit_now=&quit_now,
 
4164
 
        .password=&password,
 
4165
 
        .filename=task.filename,
 
4166
 
        .cancelled_filenames=&cancelled_filenames,
 
4167
 
        .current_time=¤t_time,
 
4168
 
        .mandos_client_exited=&mandos_client_exited,
 
4169
 
        .password_is_read=&password_is_read,
 
4172
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4173
 
                                   EPOLLIN | EPOLLRDHUP));
 
4175
 
  __attribute__((cleanup(cleanup_string)))
 
4176
 
    char *filename = NULL;
 
4177
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4178
 
                           dummy_file_name), >, 0);
 
4179
 
  g_assert_nonnull(filename);
 
4180
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4185
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
4186
 
                                               test_fixture *fixture,
 
4187
 
                                               __attribute__((unused))
 
4190
 
  __attribute__((cleanup(cleanup_close)))
 
4191
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4192
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4193
 
  const mono_microsecs current_time = 0;
 
4196
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4198
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4199
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4201
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4202
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4203
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
4206
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4207
 
  ievent->mask = IN_CLOSE_WRITE;
 
4208
 
  ievent->len = sizeof(dummy_file_name);
 
4209
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4210
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4211
 
                              + sizeof(dummy_file_name));
 
4212
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4214
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4216
 
  bool quit_now = false;
 
4217
 
  buffer password = {};
 
4218
 
  bool mandos_client_exited = false;
 
4219
 
  bool password_is_read = false;
 
4220
 
  __attribute__((cleanup(cleanup_queue)))
 
4221
 
    task_queue *queue = create_queue();
 
4222
 
  g_assert_nonnull(queue);
 
4224
 
  task_context task = {
 
4225
 
    .func=read_inotify_event,
 
4228
 
    .quit_now=&quit_now,
 
4229
 
    .password=&password,
 
4230
 
    .filename=strdup("/nonexistent"),
 
4231
 
    .cancelled_filenames = &(string_set){},
 
4233
 
    .current_time=¤t_time,
 
4234
 
    .mandos_client_exited=&mandos_client_exited,
 
4235
 
    .password_is_read=&password_is_read,
 
4237
 
  task.func(task, queue);
 
4238
 
  g_assert_false(quit_now);
 
4239
 
  g_assert_true(queue->next_run == 0);
 
4240
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4242
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4243
 
        .func=read_inotify_event,
 
4246
 
        .quit_now=&quit_now,
 
4247
 
        .password=&password,
 
4248
 
        .filename=task.filename,
 
4249
 
        .cancelled_filenames=task.cancelled_filenames,
 
4250
 
        .current_time=¤t_time,
 
4251
 
        .mandos_client_exited=&mandos_client_exited,
 
4252
 
        .password_is_read=&password_is_read,
 
4255
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4256
 
                                   EPOLLIN | EPOLLRDHUP));
 
4260
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
4261
 
                                            test_fixture *fixture,
 
4262
 
                                            __attribute__((unused))
 
4263
 
                                            gconstpointer user_data){
 
4264
 
  __attribute__((cleanup(cleanup_close)))
 
4265
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4266
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4267
 
  const mono_microsecs current_time = 0;
 
4270
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4272
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4273
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4275
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4276
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4277
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
4280
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4281
 
  ievent->mask = IN_MOVED_TO;
 
4282
 
  ievent->len = sizeof(dummy_file_name);
 
4283
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4284
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4285
 
                              + sizeof(dummy_file_name));
 
4286
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4288
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4290
 
  bool quit_now = false;
 
4291
 
  buffer password = {};
 
4292
 
  bool mandos_client_exited = false;
 
4293
 
  bool password_is_read = false;
 
4294
 
  __attribute__((cleanup(cleanup_queue)))
 
4295
 
    task_queue *queue = create_queue();
 
4296
 
  g_assert_nonnull(queue);
 
4298
 
  task_context task = {
 
4299
 
    .func=read_inotify_event,
 
4302
 
    .quit_now=&quit_now,
 
4303
 
    .password=&password,
 
4304
 
    .filename=strdup("/nonexistent"),
 
4305
 
    .cancelled_filenames = &(string_set){},
 
4307
 
    .current_time=¤t_time,
 
4308
 
    .mandos_client_exited=&mandos_client_exited,
 
4309
 
    .password_is_read=&password_is_read,
 
4311
 
  task.func(task, queue);
 
4312
 
  g_assert_false(quit_now);
 
4313
 
  g_assert_true(queue->next_run == 0);
 
4314
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4316
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4317
 
        .func=read_inotify_event,
 
4320
 
        .quit_now=&quit_now,
 
4321
 
        .password=&password,
 
4322
 
        .filename=task.filename,
 
4323
 
        .cancelled_filenames=task.cancelled_filenames,
 
4324
 
        .current_time=¤t_time,
 
4325
 
        .mandos_client_exited=&mandos_client_exited,
 
4326
 
        .password_is_read=&password_is_read,
 
4329
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4330
 
                                   EPOLLIN | EPOLLRDHUP));
 
4334
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
4335
 
                                               test_fixture *fixture,
 
4336
 
                                               __attribute__((unused))
 
4339
 
  __attribute__((cleanup(cleanup_close)))
 
4340
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4341
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4342
 
  __attribute__((cleanup(string_set_clear)))
 
4343
 
    string_set cancelled_filenames = {};
 
4344
 
  const mono_microsecs current_time = 0;
 
4347
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4349
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4350
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4352
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4353
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4354
 
  struct inotify_event *ievent = ((struct inotify_event *)
 
4357
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4358
 
  ievent->mask = IN_DELETE;
 
4359
 
  ievent->len = sizeof(dummy_file_name);
 
4360
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4361
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4362
 
                              + sizeof(dummy_file_name));
 
4363
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4365
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4367
 
  bool quit_now = false;
 
4368
 
  buffer password = {};
 
4369
 
  bool mandos_client_exited = false;
 
4370
 
  bool password_is_read = false;
 
4371
 
  __attribute__((cleanup(cleanup_queue)))
 
4372
 
    task_queue *queue = create_queue();
 
4373
 
  g_assert_nonnull(queue);
 
4375
 
  task_context task = {
 
4376
 
    .func=read_inotify_event,
 
4379
 
    .quit_now=&quit_now,
 
4380
 
    .password=&password,
 
4381
 
    .filename=strdup("/nonexistent"),
 
4382
 
    .cancelled_filenames=&cancelled_filenames,
 
4383
 
    .current_time=¤t_time,
 
4384
 
    .mandos_client_exited=&mandos_client_exited,
 
4385
 
    .password_is_read=&password_is_read,
 
4387
 
  task.func(task, queue);
 
4388
 
  g_assert_false(quit_now);
 
4389
 
  g_assert_true(queue->next_run == 0);
 
4390
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4392
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4393
 
        .func=read_inotify_event,
 
4396
 
        .quit_now=&quit_now,
 
4397
 
        .password=&password,
 
4398
 
        .filename=task.filename,
 
4399
 
        .cancelled_filenames=&cancelled_filenames,
 
4400
 
        .current_time=¤t_time,
 
4401
 
        .mandos_client_exited=&mandos_client_exited,
 
4402
 
        .password_is_read=&password_is_read,
 
4405
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4406
 
                                   EPOLLIN | EPOLLRDHUP));
 
4408
 
  __attribute__((cleanup(cleanup_string)))
 
4409
 
    char *filename = NULL;
 
4410
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4411
 
                           dummy_file_name), >, 0);
 
4412
 
  g_assert_nonnull(filename);
 
4413
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4417
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
4418
 
                                         test_fixture *fixture,
 
4419
 
                                         __attribute__((unused))
 
4420
 
                                         gconstpointer user_data){
 
4421
 
  __attribute__((cleanup(cleanup_close)))
 
4422
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4423
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4424
 
  __attribute__((cleanup(string_set_clear)))
 
4425
 
    string_set cancelled_filenames = {};
 
4426
 
  bool mandos_client_exited = false;
 
4427
 
  bool password_is_read = false;
 
4428
 
  __attribute__((cleanup(cleanup_queue)))
 
4429
 
    task_queue *queue = create_queue();
 
4430
 
  g_assert_nonnull(queue);
 
4432
 
  char *const filename = strdup("/nonexistent");
 
4433
 
  g_assert_nonnull(filename);
 
4434
 
  task_context task = {
 
4435
 
    .func=open_and_parse_question,
 
4436
 
    .question_filename=filename,
 
4438
 
    .password=(buffer[]){{}},
 
4440
 
    .cancelled_filenames=&cancelled_filenames,
 
4441
 
    .current_time=(mono_microsecs[]){0},
 
4442
 
    .mandos_client_exited=&mandos_client_exited,
 
4443
 
    .password_is_read=&password_is_read,
 
4445
 
  task.func(task, queue);
 
4446
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4449
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
4450
 
                                             test_fixture *fixture,
 
4451
 
                                             __attribute__((unused))
 
4452
 
                                             gconstpointer user_data){
 
4453
 
  __attribute__((cleanup(cleanup_close)))
 
4454
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4455
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4456
 
  __attribute__((cleanup(string_set_clear)))
 
4457
 
    string_set cancelled_filenames = {};
 
4458
 
  buffer password = {};
 
4459
 
  bool mandos_client_exited = false;
 
4460
 
  bool password_is_read = false;
 
4461
 
  __attribute__((cleanup(cleanup_queue)))
 
4462
 
    task_queue *queue = create_queue();
 
4463
 
  g_assert_nonnull(queue);
 
4464
 
  const mono_microsecs current_time = 0;
 
4466
 
  char *filename = strdup("/proc/self/mem");
 
4467
 
  task_context task = {
 
4468
 
    .func=open_and_parse_question,
 
4469
 
    .question_filename=filename,
 
4471
 
    .password=&password,
 
4473
 
    .cancelled_filenames=&cancelled_filenames,
 
4474
 
    .current_time=¤t_time,
 
4475
 
    .mandos_client_exited=&mandos_client_exited,
 
4476
 
    .password_is_read=&password_is_read,
 
4478
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4479
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4483
 
test_open_and_parse_question_parse_error(__attribute__((unused))
 
4484
 
                                         test_fixture *fixture,
 
4485
 
                                         __attribute__((unused))
 
4486
 
                                         gconstpointer user_data){
 
4487
 
  __attribute__((cleanup(cleanup_close)))
 
4488
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4489
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4490
 
  __attribute__((cleanup(string_set_clear)))
 
4491
 
    string_set cancelled_filenames = {};
 
4492
 
  __attribute__((cleanup(cleanup_queue)))
 
4493
 
    task_queue *queue = create_queue();
 
4494
 
  g_assert_nonnull(queue);
 
4496
 
  __attribute__((cleanup(cleanup_string)))
 
4497
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4498
 
  g_assert_nonnull(tempfilename);
 
4499
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
4500
 
  g_assert_cmpint(tempfile, >, 0);
 
4501
 
  const char bad_data[] = "this is bad syntax\n";
 
4502
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
4503
 
                  ==, sizeof(bad_data));
 
4504
 
  g_assert_cmpint(close(tempfile), ==, 0);
 
4506
 
  char *const filename = strdup(tempfilename);
 
4507
 
  g_assert_nonnull(filename);
 
4508
 
  task_context task = {
 
4509
 
    .func=open_and_parse_question,
 
4510
 
    .question_filename=filename,
 
4512
 
    .password=(buffer[]){{}},
 
4514
 
    .cancelled_filenames=&cancelled_filenames,
 
4515
 
    .current_time=(mono_microsecs[]){0},
 
4516
 
    .mandos_client_exited=(bool[]){false},
 
4517
 
    .password_is_read=(bool[]){false},
 
4519
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4521
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4523
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4527
 
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
4528
 
                                           test_fixture *fixture,
 
4529
 
                                           __attribute__((unused))
 
4530
 
                                           gconstpointer user_data){
 
4531
 
  __attribute__((cleanup(cleanup_close)))
 
4532
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4533
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4534
 
  __attribute__((cleanup(string_set_clear)))
 
4535
 
    string_set cancelled_filenames = {};
 
4536
 
  __attribute__((cleanup(cleanup_queue)))
 
4537
 
    task_queue *queue = create_queue();
 
4538
 
  g_assert_nonnull(queue);
 
4540
 
  __attribute__((cleanup(cleanup_string)))
 
4541
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4542
 
  g_assert_nonnull(tempfilename);
 
4543
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4544
 
  g_assert_cmpint(questionfile, >, 0);
 
4545
 
  FILE *qf = fdopen(questionfile, "w");
 
4546
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
4547
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4549
 
  char *const filename = strdup(tempfilename);
 
4550
 
  g_assert_nonnull(filename);
 
4551
 
  task_context task = {
 
4552
 
    .func=open_and_parse_question,
 
4553
 
    .question_filename=filename,
 
4555
 
    .password=(buffer[]){{}},
 
4557
 
    .cancelled_filenames=&cancelled_filenames,
 
4558
 
    .current_time=(mono_microsecs[]){0},
 
4559
 
    .mandos_client_exited=(bool[]){false},
 
4560
 
    .password_is_read=(bool[]){false},
 
4562
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4563
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4565
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4569
 
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
4570
 
                                            test_fixture *fixture,
 
4571
 
                                            __attribute__((unused))
 
4572
 
                                            gconstpointer user_data){
 
4573
 
  __attribute__((cleanup(cleanup_close)))
 
4574
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4575
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4576
 
  __attribute__((cleanup(string_set_clear)))
 
4577
 
    string_set cancelled_filenames = {};
 
4578
 
  __attribute__((cleanup(cleanup_queue)))
 
4579
 
    task_queue *queue = create_queue();
 
4580
 
  g_assert_nonnull(queue);
 
4582
 
  __attribute__((cleanup(cleanup_string)))
 
4583
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4584
 
  g_assert_nonnull(tempfilename);
 
4585
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4586
 
  g_assert_cmpint(questionfile, >, 0);
 
4587
 
  FILE *qf = fdopen(questionfile, "w");
 
4588
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
4589
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4591
 
  char *const filename = strdup(tempfilename);
 
4592
 
  g_assert_nonnull(filename);
 
4593
 
  task_context task = {
 
4594
 
    .func=open_and_parse_question,
 
4595
 
    .question_filename=filename,
 
4597
 
    .password=(buffer[]){{}},
 
4599
 
    .cancelled_filenames=&cancelled_filenames,
 
4600
 
    .current_time=(mono_microsecs[]){0},
 
4601
 
    .mandos_client_exited=(bool[]){false},
 
4602
 
    .password_is_read=(bool[]){false},
 
4604
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4605
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4607
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4611
 
void test_open_and_parse_question_nopid(__attribute__((unused))
 
4612
 
                                        test_fixture *fixture,
 
4613
 
                                        __attribute__((unused))
 
4614
 
                                        gconstpointer user_data){
 
4615
 
  __attribute__((cleanup(cleanup_close)))
 
4616
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4617
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4618
 
  __attribute__((cleanup(string_set_clear)))
 
4619
 
    string_set cancelled_filenames = {};
 
4620
 
  __attribute__((cleanup(cleanup_queue)))
 
4621
 
    task_queue *queue = create_queue();
 
4622
 
  g_assert_nonnull(queue);
 
4624
 
  __attribute__((cleanup(cleanup_string)))
 
4625
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4626
 
  g_assert_nonnull(tempfilename);
 
4627
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4628
 
  g_assert_cmpint(questionfile, >, 0);
 
4629
 
  FILE *qf = fdopen(questionfile, "w");
 
4630
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
4631
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4633
 
  char *const filename = strdup(tempfilename);
 
4634
 
  g_assert_nonnull(filename);
 
4635
 
  task_context task = {
 
4636
 
    .func=open_and_parse_question,
 
4637
 
    .question_filename=filename,
 
4639
 
    .password=(buffer[]){{}},
 
4641
 
    .cancelled_filenames=&cancelled_filenames,
 
4642
 
    .current_time=(mono_microsecs[]){0},
 
4643
 
    .mandos_client_exited=(bool[]){false},
 
4644
 
    .password_is_read=(bool[]){false},
 
4646
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4647
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4649
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4653
 
void test_open_and_parse_question_badpid(__attribute__((unused))
 
4654
 
                                         test_fixture *fixture,
 
4655
 
                                         __attribute__((unused))
 
4656
 
                                         gconstpointer user_data){
 
4657
 
  __attribute__((cleanup(cleanup_close)))
 
4658
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4659
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4660
 
  __attribute__((cleanup(string_set_clear)))
 
4661
 
    string_set cancelled_filenames = {};
 
4662
 
  __attribute__((cleanup(cleanup_queue)))
 
4663
 
    task_queue *queue = create_queue();
 
4664
 
  g_assert_nonnull(queue);
 
4666
 
  __attribute__((cleanup(cleanup_string)))
 
4667
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4668
 
  g_assert_nonnull(tempfilename);
 
4669
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4670
 
  g_assert_cmpint(questionfile, >, 0);
 
4671
 
  FILE *qf = fdopen(questionfile, "w");
 
4672
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
4674
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4676
 
  char *const filename = strdup(tempfilename);
 
4677
 
  g_assert_nonnull(filename);
 
4678
 
  task_context task = {
 
4679
 
    .func=open_and_parse_question,
 
4680
 
    .question_filename=filename,
 
4682
 
    .password=(buffer[]){{}},
 
4684
 
    .cancelled_filenames=&cancelled_filenames,
 
4685
 
    .current_time=(mono_microsecs[]){0},
 
4686
 
    .mandos_client_exited=(bool[]){false},
 
4687
 
    .password_is_read=(bool[]){false},
 
4689
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4690
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4692
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4696
 
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
4697
 
                                         test_fixture *fixture,
 
4698
 
                                         __attribute__((unused))
 
4699
 
                                         gconstpointer user_data){
 
4700
 
  __attribute__((cleanup(cleanup_close)))
 
4701
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4702
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4703
 
  __attribute__((cleanup(string_set_clear)))
 
4704
 
    string_set cancelled_filenames = {};
 
4705
 
  buffer password = {};
 
4706
 
  bool mandos_client_exited = false;
 
4707
 
  bool password_is_read = false;
 
4708
 
  __attribute__((cleanup(cleanup_queue)))
 
4709
 
    task_queue *queue = create_queue();
 
4710
 
  g_assert_nonnull(queue);
 
4711
 
  const mono_microsecs current_time = 0;
 
4713
 
  /* Find value of sysctl kernel.pid_max */
 
4714
 
  uintmax_t pid_max = 0;
 
4715
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
4716
 
  g_assert_nonnull(sysctl_pid_max);
 
4717
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
4719
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
4721
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
4722
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
4724
 
  __attribute__((cleanup(cleanup_string)))
 
4725
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4726
 
  g_assert_nonnull(tempfilename);
 
4727
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4728
 
  g_assert_cmpint(questionfile, >, 0);
 
4729
 
  FILE *qf = fdopen(questionfile, "w");
 
4730
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4731
 
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
4733
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4735
 
  char *const question_filename = strdup(tempfilename);
 
4736
 
  g_assert_nonnull(question_filename);
 
4737
 
  task_context task = {
 
4738
 
    .func=open_and_parse_question,
 
4739
 
    .question_filename=question_filename,
 
4741
 
    .password=&password,
 
4742
 
    .filename=question_filename,
 
4743
 
    .cancelled_filenames=&cancelled_filenames,
 
4744
 
    .current_time=¤t_time,
 
4745
 
    .mandos_client_exited=&mandos_client_exited,
 
4746
 
    .password_is_read=&password_is_read,
 
4748
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4749
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4751
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4755
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
4756
 
                                         test_fixture *fixture,
 
4757
 
                                         __attribute__((unused))
 
4758
 
                                         gconstpointer user_data){
 
4759
 
  __attribute__((cleanup(cleanup_close)))
 
4760
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4761
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4762
 
  __attribute__((cleanup(string_set_clear)))
 
4763
 
    string_set cancelled_filenames = {};
 
4764
 
  buffer password = {};
 
4765
 
  bool mandos_client_exited = false;
 
4766
 
  bool password_is_read = false;
 
4767
 
  __attribute__((cleanup(cleanup_queue)))
 
4768
 
    task_queue *queue = create_queue();
 
4769
 
  g_assert_nonnull(queue);
 
4770
 
  const mono_microsecs current_time = 0;
 
4772
 
  __attribute__((cleanup(cleanup_string)))
 
4773
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4774
 
  g_assert_nonnull(tempfilename);
 
4775
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4776
 
  g_assert_cmpint(questionfile, >, 0);
 
4777
 
  FILE *qf = fdopen(questionfile, "w");
 
4778
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4779
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
4780
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4782
 
  char *const filename = strdup(tempfilename);
 
4783
 
  g_assert_nonnull(filename);
 
4784
 
  task_context task = {
 
4785
 
    .func=open_and_parse_question,
 
4786
 
    .question_filename=filename,
 
4788
 
    .password=&password,
 
4790
 
    .cancelled_filenames=&cancelled_filenames,
 
4791
 
    .current_time=¤t_time,
 
4792
 
    .mandos_client_exited=&mandos_client_exited,
 
4793
 
    .password_is_read=&password_is_read,
 
4795
 
  task.func(task, queue);
 
4796
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4798
 
  __attribute__((cleanup(cleanup_string)))
 
4799
 
    char *socket_filename = strdup("/nonexistent");
 
4800
 
  g_assert_nonnull(socket_filename);
 
4801
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4802
 
        .func=connect_question_socket,
 
4803
 
        .question_filename=tempfilename,
 
4804
 
        .filename=socket_filename,
 
4806
 
        .password=&password,
 
4807
 
        .current_time=¤t_time,
 
4808
 
        .mandos_client_exited=&mandos_client_exited,
 
4809
 
        .password_is_read=&password_is_read,
 
4812
 
  g_assert_true(queue->next_run != 0);
 
4814
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4818
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
4819
 
                                          test_fixture *fixture,
 
4820
 
                                          __attribute__((unused))
 
4821
 
                                          gconstpointer user_data){
 
4822
 
  __attribute__((cleanup(cleanup_close)))
 
4823
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4824
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4825
 
  __attribute__((cleanup(string_set_clear)))
 
4826
 
    string_set cancelled_filenames = {};
 
4827
 
  buffer password = {};
 
4828
 
  bool mandos_client_exited = false;
 
4829
 
  bool password_is_read = false;
 
4830
 
  __attribute__((cleanup(cleanup_queue)))
 
4831
 
    task_queue *queue = create_queue();
 
4832
 
  g_assert_nonnull(queue);
 
4833
 
  const mono_microsecs current_time = 0;
 
4835
 
  __attribute__((cleanup(cleanup_string)))
 
4836
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4837
 
  g_assert_nonnull(tempfilename);
 
4838
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4839
 
  g_assert_cmpint(questionfile, >, 0);
 
4840
 
  FILE *qf = fdopen(questionfile, "w");
 
4841
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4842
 
                          PRIuMAX "\nNotAfter=\n",
 
4843
 
                          (uintmax_t)getpid()), >, 0);
 
4844
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4846
 
  char *const filename = strdup(tempfilename);
 
4847
 
  g_assert_nonnull(filename);
 
4848
 
  task_context task = {
 
4849
 
    .func=open_and_parse_question,
 
4850
 
    .question_filename=filename,
 
4852
 
    .password=&password,
 
4854
 
    .cancelled_filenames=&cancelled_filenames,
 
4855
 
    .current_time=¤t_time,
 
4856
 
    .mandos_client_exited=&mandos_client_exited,
 
4857
 
    .password_is_read=&password_is_read,
 
4859
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4860
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4862
 
  __attribute__((cleanup(cleanup_string)))
 
4863
 
    char *socket_filename = strdup("/nonexistent");
 
4864
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4865
 
        .func=connect_question_socket,
 
4866
 
        .question_filename=tempfilename,
 
4867
 
        .filename=socket_filename,
 
4869
 
        .password=&password,
 
4870
 
        .current_time=¤t_time,
 
4871
 
        .mandos_client_exited=&mandos_client_exited,
 
4872
 
        .password_is_read=&password_is_read,
 
4874
 
  g_assert_true(queue->next_run != 0);
 
4876
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4880
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
4882
 
                                                  const mono_microsecs
 
4884
 
                                                  const mono_microsecs
 
4886
 
  __attribute__((cleanup(cleanup_close)))
 
4887
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4888
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4889
 
  __attribute__((cleanup(string_set_clear)))
 
4890
 
    string_set cancelled_filenames = {};
 
4891
 
  buffer password = {};
 
4892
 
  bool mandos_client_exited = false;
 
4893
 
  bool password_is_read = false;
 
4894
 
  __attribute__((cleanup(cleanup_queue)))
 
4895
 
    task_queue *queue = create_queue();
 
4896
 
  g_assert_nonnull(queue);
 
4897
 
  queue->next_run = next_queue_run;
 
4899
 
  __attribute__((cleanup(cleanup_string)))
 
4900
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4901
 
  g_assert_nonnull(tempfilename);
 
4902
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4903
 
  g_assert_cmpint(questionfile, >, 0);
 
4904
 
  FILE *qf = fdopen(questionfile, "w");
 
4905
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4906
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
4907
 
                          (uintmax_t)getpid(), notafter), >, 0);
 
4908
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4910
 
  char *const filename = strdup(tempfilename);
 
4911
 
  g_assert_nonnull(filename);
 
4912
 
  task_context task = {
 
4913
 
    .func=open_and_parse_question,
 
4914
 
    .question_filename=filename,
 
4916
 
    .password=&password,
 
4918
 
    .cancelled_filenames=&cancelled_filenames,
 
4919
 
    .current_time=¤t_time,
 
4920
 
    .mandos_client_exited=&mandos_client_exited,
 
4921
 
    .password_is_read=&password_is_read,
 
4923
 
  task.func(task, queue);
 
4925
 
  if(queue->length >= 1){
 
4926
 
    __attribute__((cleanup(cleanup_string)))
 
4927
 
      char *socket_filename = strdup("/nonexistent");
 
4928
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
4929
 
          .func=connect_question_socket,
 
4930
 
          .filename=socket_filename,
 
4932
 
          .password=&password,
 
4933
 
          .current_time=¤t_time,
 
4934
 
          .cancelled_filenames=&cancelled_filenames,
 
4935
 
          .mandos_client_exited=&mandos_client_exited,
 
4936
 
          .password_is_read=&password_is_read,
 
4938
 
    g_assert_true(queue->next_run != 0);
 
4942
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4943
 
  } else if(current_time >= notafter) {
 
4944
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4946
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
4947
 
          .func=cancel_old_question,
 
4948
 
          .question_filename=tempfilename,
 
4949
 
          .filename=tempfilename,
 
4951
 
          .cancelled_filenames=&cancelled_filenames,
 
4952
 
          .current_time=¤t_time,
 
4955
 
  g_assert_true(queue->next_run == 1);
 
4957
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4961
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
4962
 
                                        test_fixture *fixture,
 
4963
 
                                        __attribute__((unused))
 
4964
 
                                        gconstpointer user_data){
 
4965
 
  /* current_time, notafter, next_queue_run */
 
4966
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
4970
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
4971
 
                                        test_fixture *fixture,
 
4972
 
                                        __attribute__((unused))
 
4973
 
                                        gconstpointer user_data){
 
4974
 
  /* current_time, notafter, next_queue_run */
 
4975
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
4979
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
4980
 
                                          test_fixture *fixture,
 
4981
 
                                          __attribute__((unused))
 
4982
 
                                          gconstpointer user_data){
 
4983
 
  /* current_time, notafter, next_queue_run */
 
4984
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
4988
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
4989
 
                                          test_fixture *fixture,
 
4990
 
                                          __attribute__((unused))
 
4991
 
                                          gconstpointer user_data){
 
4992
 
  /* current_time, notafter, next_queue_run */
 
4993
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
4997
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
4998
 
                                            test_fixture *fixture,
 
4999
 
                                            __attribute__((unused))
 
5000
 
                                            gconstpointer user_data){
 
5001
 
  /* current_time, notafter, next_queue_run */
 
5002
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
5006
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
5007
 
                                           test_fixture *fixture,
 
5008
 
                                           __attribute__((unused))
 
5009
 
                                           gconstpointer user_data){
 
5010
 
  /* current_time, notafter, next_queue_run */
 
5011
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
5014
 
static void assert_cancel_old_question_param(const mono_microsecs
 
5016
 
                                             const mono_microsecs
 
5018
 
                                             const mono_microsecs
 
5020
 
                                             const mono_microsecs
 
5022
 
  __attribute__((cleanup(string_set_clear)))
 
5023
 
    string_set cancelled_filenames = {};
 
5024
 
  __attribute__((cleanup(cleanup_queue)))
 
5025
 
    task_queue *queue = create_queue();
 
5026
 
  g_assert_nonnull(queue);
 
5027
 
  queue->next_run = next_queue_run;
 
5029
 
  char *const question_filename = strdup("/nonexistent");
 
5030
 
  g_assert_nonnull(question_filename);
 
5031
 
  task_context task = {
 
5032
 
    .func=cancel_old_question,
 
5033
 
    .question_filename=question_filename,
 
5034
 
    .filename=question_filename,
 
5036
 
    .cancelled_filenames=&cancelled_filenames,
 
5037
 
    .current_time=¤t_time,
 
5039
 
  task.func(task, queue);
 
5041
 
  if(current_time >= notafter){
 
5042
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5043
 
    g_assert_true(string_set_contains(cancelled_filenames,
 
5046
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5047
 
          .func=cancel_old_question,
 
5048
 
          .question_filename=question_filename,
 
5049
 
          .filename=question_filename,
 
5051
 
          .cancelled_filenames=&cancelled_filenames,
 
5052
 
          .current_time=¤t_time,
 
5055
 
    g_assert_false(string_set_contains(cancelled_filenames,
 
5056
 
                                       question_filename));
 
5058
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
5059
 
                   (unsigned int)next_set_to);
 
5062
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
5063
 
                                           test_fixture *fixture,
 
5064
 
                                           __attribute__((unused))
 
5065
 
                                           gconstpointer user_data){
 
5066
 
  /* next_queue_run unset,
 
5067
 
     cancellation should happen because time has come,
 
5068
 
     next_queue_run should be unchanged */
 
5069
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5070
 
  assert_cancel_old_question_param(0, 1, 2, 0);
 
5073
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
5074
 
                                           test_fixture *fixture,
 
5075
 
                                           __attribute__((unused))
 
5076
 
                                           gconstpointer user_data){
 
5077
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
5078
 
     and current_time is not yet notafter or greater,
 
5079
 
     update value of next_queue_run to value of notafter */
 
5080
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5081
 
  assert_cancel_old_question_param(0, 2, 1, 2);
 
5084
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
5085
 
                                           test_fixture *fixture,
 
5086
 
                                           __attribute__((unused))
 
5087
 
                                           gconstpointer user_data){
 
5088
 
  /* next_queue_run 1,
 
5089
 
     cancellation should happen because time has come,
 
5090
 
     next_queue_run should be unchanged */
 
5091
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5092
 
  assert_cancel_old_question_param(1, 2, 3, 1);
 
5095
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
5096
 
                                           test_fixture *fixture,
 
5097
 
                                           __attribute__((unused))
 
5098
 
                                           gconstpointer user_data){
 
5099
 
  /* If next_queue_run is set,
 
5100
 
     and current_time is not yet notafter or greater,
 
5101
 
     and notafter is larger than next_queue_run
 
5102
 
     next_queue_run should be unchanged */
 
5103
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5104
 
  assert_cancel_old_question_param(1, 3, 2, 1);
 
5107
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
5108
 
                                           test_fixture *fixture,
 
5109
 
                                           __attribute__((unused))
 
5110
 
                                           gconstpointer user_data){
 
5111
 
  /* next_queue_run 2,
 
5112
 
     cancellation should happen because time has come,
 
5113
 
     next_queue_run should be unchanged */
 
5114
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5115
 
  assert_cancel_old_question_param(2, 1, 3, 2);
 
5118
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
5119
 
                                           test_fixture *fixture,
 
5120
 
                                           __attribute__((unused))
 
5121
 
                                           gconstpointer user_data){
 
5122
 
  /* If next_queue_run is set,
 
5123
 
     and current_time is not yet notafter or greater,
 
5124
 
     and notafter is larger than next_queue_run
 
5125
 
     next_queue_run should be unchanged */
 
5126
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5127
 
  assert_cancel_old_question_param(2, 3, 1, 2);
 
5130
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
5131
 
                                           test_fixture *fixture,
 
5132
 
                                           __attribute__((unused))
 
5133
 
                                           gconstpointer user_data){
 
5134
 
  /* next_queue_run 3,
 
5135
 
     cancellation should happen because time has come,
 
5136
 
     next_queue_run should be unchanged */
 
5137
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5138
 
  assert_cancel_old_question_param(3, 1, 2, 3);
 
5141
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
5142
 
                                           test_fixture *fixture,
 
5143
 
                                           __attribute__((unused))
 
5144
 
                                           gconstpointer user_data){
 
5145
 
  /* If next_queue_run is set,
 
5146
 
     and current_time is not yet notafter or greater,
 
5147
 
     and notafter is smaller than next_queue_run
 
5148
 
     update value of next_queue_run to value of notafter */
 
5149
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5150
 
  assert_cancel_old_question_param(3, 2, 1, 2);
 
5154
 
test_connect_question_socket_name_too_long(__attribute__((unused))
 
5155
 
                                           test_fixture *fixture,
 
5156
 
                                           __attribute__((unused))
 
5157
 
                                           gconstpointer user_data){
 
5158
 
  __attribute__((cleanup(cleanup_close)))
 
5159
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5160
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5161
 
  const char question_filename[] = "/nonexistent/question";
 
5162
 
  __attribute__((cleanup(string_set_clear)))
 
5163
 
    string_set cancelled_filenames = {};
 
5164
 
  __attribute__((cleanup(cleanup_queue)))
 
5165
 
    task_queue *queue = create_queue();
 
5166
 
  g_assert_nonnull(queue);
 
5167
 
  __attribute__((cleanup(cleanup_string)))
 
5168
 
    char *tempdir = make_temporary_directory();
 
5169
 
  g_assert_nonnull(tempdir);
 
5170
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
5171
 
  char socket_name[sizeof(unix_socket.sun_path)];
 
5172
 
  memset(socket_name, 'x', sizeof(socket_name));
 
5173
 
  socket_name[sizeof(socket_name)-1] = '\0';
 
5174
 
  char *filename = NULL;
 
5175
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5177
 
  g_assert_nonnull(filename);
 
5179
 
  task_context task = {
 
5180
 
    .func=connect_question_socket,
 
5181
 
    .question_filename=strdup(question_filename),
 
5183
 
    .password=(buffer[]){{}},
 
5185
 
    .cancelled_filenames=&cancelled_filenames,
 
5186
 
    .mandos_client_exited=(bool[]){false},
 
5187
 
    .password_is_read=(bool[]){false},
 
5188
 
    .current_time=(mono_microsecs[]){0},
 
5190
 
  g_assert_nonnull(task.question_filename);
 
5191
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5193
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5194
 
                                    question_filename));
 
5195
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5196
 
  g_assert_true(queue->next_run == 0);
 
5198
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5202
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
5203
 
                                               test_fixture *fixture,
 
5204
 
                                               __attribute__((unused))
 
5207
 
  __attribute__((cleanup(cleanup_close)))
 
5208
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5209
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5210
 
  const char question_filename[] = "/nonexistent/question";
 
5211
 
  __attribute__((cleanup(string_set_clear)))
 
5212
 
    string_set cancelled_filenames = {};
 
5213
 
  const mono_microsecs current_time = 3;
 
5214
 
  __attribute__((cleanup(cleanup_queue)))
 
5215
 
    task_queue *queue = create_queue();
 
5216
 
  g_assert_nonnull(queue);
 
5217
 
  __attribute__((cleanup(cleanup_string)))
 
5218
 
    char *tempdir = make_temporary_directory();
 
5219
 
  g_assert_nonnull(tempdir);
 
5220
 
  char socket_name[] = "nonexistent";
 
5221
 
  char *filename = NULL;
 
5222
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5224
 
  g_assert_nonnull(filename);
 
5226
 
  task_context task = {
 
5227
 
    .func=connect_question_socket,
 
5228
 
    .question_filename=strdup(question_filename),
 
5230
 
    .password=(buffer[]){{}},
 
5232
 
    .cancelled_filenames=&cancelled_filenames,
 
5233
 
    .mandos_client_exited=(bool[]){false},
 
5234
 
    .password_is_read=(bool[]){false},
 
5235
 
    .current_time=¤t_time,
 
5237
 
  g_assert_nonnull(task.question_filename);
 
5238
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5240
 
  g_assert_nonnull(find_matching_task(queue, task));
 
5242
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
5243
 
                                     question_filename));
 
5244
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5245
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5247
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5251
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
5252
 
                                            test_fixture *fixture,
 
5253
 
                                            __attribute__((unused))
 
5254
 
                                            gconstpointer user_data){
 
5255
 
  __attribute__((cleanup(cleanup_close)))
 
5256
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5257
 
  __attribute__((cleanup(cleanup_string)))
 
5258
 
    char *const question_filename = strdup("/nonexistent/question");
 
5259
 
  g_assert_nonnull(question_filename);
 
5260
 
  __attribute__((cleanup(string_set_clear)))
 
5261
 
    string_set cancelled_filenames = {};
 
5262
 
  const mono_microsecs current_time = 5;
 
5263
 
  __attribute__((cleanup(cleanup_queue)))
 
5264
 
    task_queue *queue = create_queue();
 
5265
 
  g_assert_nonnull(queue);
 
5266
 
  __attribute__((cleanup(cleanup_string)))
 
5267
 
    char *tempdir = make_temporary_directory();
 
5268
 
  g_assert_nonnull(tempdir);
 
5269
 
  __attribute__((cleanup(cleanup_close)))
 
5270
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5271
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5272
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5273
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5274
 
  const char socket_name[] = "socket_name";
 
5275
 
  __attribute__((cleanup(cleanup_string)))
 
5276
 
    char *filename = NULL;
 
5277
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5279
 
  g_assert_nonnull(filename);
 
5280
 
  g_assert_cmpint((int)strlen(filename), <,
 
5281
 
                  (int)sizeof(sock_name.sun_path));
 
5282
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5283
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5284
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5285
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5286
 
  task_context task = {
 
5287
 
    .func=connect_question_socket,
 
5288
 
    .question_filename=strdup(question_filename),
 
5290
 
    .password=(buffer[]){{}},
 
5291
 
    .filename=strdup(filename),
 
5292
 
    .cancelled_filenames=&cancelled_filenames,
 
5293
 
    .mandos_client_exited=(bool[]){false},
 
5294
 
    .password_is_read=(bool[]){false},
 
5295
 
    .current_time=¤t_time,
 
5297
 
  g_assert_nonnull(task.question_filename);
 
5298
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5300
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5301
 
  const task_context *const added_task
 
5302
 
    = find_matching_task(queue, task);
 
5303
 
  g_assert_nonnull(added_task);
 
5304
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5306
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5307
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5311
 
void test_connect_question_socket_usable(__attribute__((unused))
 
5312
 
                                         test_fixture *fixture,
 
5313
 
                                         __attribute__((unused))
 
5314
 
                                         gconstpointer user_data){
 
5315
 
  __attribute__((cleanup(cleanup_close)))
 
5316
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5317
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5318
 
  __attribute__((cleanup(cleanup_string)))
 
5319
 
    char *const question_filename = strdup("/nonexistent/question");
 
5320
 
  g_assert_nonnull(question_filename);
 
5321
 
  __attribute__((cleanup(string_set_clear)))
 
5322
 
    string_set cancelled_filenames = {};
 
5323
 
  buffer password = {};
 
5324
 
  bool mandos_client_exited = false;
 
5325
 
  bool password_is_read = false;
 
5326
 
  const mono_microsecs current_time = 0;
 
5327
 
  __attribute__((cleanup(cleanup_queue)))
 
5328
 
    task_queue *queue = create_queue();
 
5329
 
  g_assert_nonnull(queue);
 
5330
 
  __attribute__((cleanup(cleanup_string)))
 
5331
 
    char *tempdir = make_temporary_directory();
 
5332
 
  g_assert_nonnull(tempdir);
 
5333
 
  __attribute__((cleanup(cleanup_close)))
 
5334
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5335
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5336
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5337
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5338
 
  const char socket_name[] = "socket_name";
 
5339
 
  __attribute__((cleanup(cleanup_string)))
 
5340
 
    char *filename = NULL;
 
5341
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5343
 
  g_assert_nonnull(filename);
 
5344
 
  g_assert_cmpint((int)strlen(filename), <,
 
5345
 
                  (int)sizeof(sock_name.sun_path));
 
5346
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5347
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5348
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5349
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5350
 
  task_context task = {
 
5351
 
    .func=connect_question_socket,
 
5352
 
    .question_filename=strdup(question_filename),
 
5354
 
    .password=&password,
 
5355
 
    .filename=strdup(filename),
 
5356
 
    .cancelled_filenames=&cancelled_filenames,
 
5357
 
    .mandos_client_exited=&mandos_client_exited,
 
5358
 
    .password_is_read=&password_is_read,
 
5359
 
    .current_time=¤t_time,
 
5361
 
  g_assert_nonnull(task.question_filename);
 
5362
 
  task.func(task, queue);
 
5364
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5365
 
  const task_context *const added_task
 
5366
 
    = find_matching_task(queue, (task_context){
 
5367
 
        .func=send_password_to_socket,
 
5368
 
        .question_filename=question_filename,
 
5371
 
        .password=&password,
 
5372
 
        .cancelled_filenames=&cancelled_filenames,
 
5373
 
        .mandos_client_exited=&mandos_client_exited,
 
5374
 
        .password_is_read=&password_is_read,
 
5375
 
        .current_time=¤t_time,
 
5377
 
  g_assert_nonnull(added_task);
 
5378
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5380
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5383
 
  const int fd = added_task->fd;
 
5384
 
  g_assert_cmpint(fd, >, 0);
 
5385
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
5388
 
  char write_data[PIPE_BUF];
 
5390
 
    /* Construct test password buffer */
 
5391
 
    /* Start with + since that is what the real procotol uses */
 
5392
 
    write_data[0] = '+';
 
5393
 
    /* Set a special character at string end just to mark the end */
 
5394
 
    write_data[sizeof(write_data)-2] = 'y';
 
5395
 
    /* Set NUL at buffer end, as suggested by the protocol */
 
5396
 
    write_data[sizeof(write_data)-1] = '\0';
 
5397
 
    /* Fill rest of password with 'x' */
 
5398
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
5399
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
5400
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
5403
 
  /* read from sock_fd */
 
5404
 
  char read_data[sizeof(write_data)];
 
5405
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
5406
 
                  ==, sizeof(read_data));
 
5408
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
5411
 
  /* writing to sock_fd should fail */
 
5412
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
5413
 
                       MSG_NOSIGNAL), <, 0);
 
5415
 
  /* reading from fd should fail */
 
5416
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
5417
 
                            MSG_NOSIGNAL), <, 0);
 
5419
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5420
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5424
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
5425
 
                                               test_fixture *fixture,
 
5426
 
                                               __attribute__((unused))
 
5429
 
  __attribute__((cleanup(cleanup_close)))
 
5430
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5431
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5432
 
  __attribute__((cleanup(cleanup_string)))
 
5433
 
    char *const question_filename = strdup("/nonexistent/question");
 
5434
 
  g_assert_nonnull(question_filename);
 
5435
 
  __attribute__((cleanup(cleanup_string)))
 
5436
 
    char *const filename = strdup("/nonexistent/socket");
 
5437
 
  g_assert_nonnull(filename);
 
5438
 
  __attribute__((cleanup(string_set_clear)))
 
5439
 
    string_set cancelled_filenames = {};
 
5440
 
  buffer password = {};
 
5441
 
  bool password_is_read = true;
 
5442
 
  __attribute__((cleanup(cleanup_queue)))
 
5443
 
    task_queue *queue = create_queue();
 
5444
 
  g_assert_nonnull(queue);
 
5446
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5447
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5449
 
  __attribute__((cleanup(cleanup_close)))
 
5450
 
    const int read_socket = socketfds[0];
 
5451
 
  const int write_socket = socketfds[1];
 
5452
 
  task_context task = {
 
5453
 
    .func=send_password_to_socket,
 
5454
 
    .question_filename=strdup(question_filename),
 
5455
 
    .filename=strdup(filename),
 
5458
 
    .password=&password,
 
5459
 
    .cancelled_filenames=&cancelled_filenames,
 
5460
 
    .mandos_client_exited=(bool[]){false},
 
5461
 
    .password_is_read=&password_is_read,
 
5462
 
    .current_time=(mono_microsecs[]){0},
 
5464
 
  g_assert_nonnull(task.question_filename);
 
5466
 
  task.func(task, queue);
 
5468
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5470
 
  const task_context *const added_task
 
5471
 
    = find_matching_task(queue, task);
 
5472
 
  g_assert_nonnull(added_task);
 
5473
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5474
 
  g_assert_true(password_is_read);
 
5476
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5477
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5482
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
5483
 
                                               test_fixture *fixture,
 
5484
 
                                               __attribute__((unused))
 
5487
 
  __attribute__((cleanup(cleanup_close)))
 
5488
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5489
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5490
 
  __attribute__((cleanup(cleanup_string)))
 
5491
 
    char *const question_filename = strdup("/nonexistent/question");
 
5492
 
  g_assert_nonnull(question_filename);
 
5493
 
  __attribute__((cleanup(cleanup_string)))
 
5494
 
    char *const filename = strdup("/nonexistent/socket");
 
5495
 
  __attribute__((cleanup(string_set_clear)))
 
5496
 
    string_set cancelled_filenames = {};
 
5497
 
  buffer password = {};
 
5498
 
  __attribute__((cleanup(cleanup_queue)))
 
5499
 
    task_queue *queue = create_queue();
 
5500
 
  g_assert_nonnull(queue);
 
5502
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5503
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5505
 
  __attribute__((cleanup(cleanup_close)))
 
5506
 
    const int read_socket = socketfds[0];
 
5507
 
  const int write_socket = socketfds[1];
 
5508
 
  task_context task = {
 
5509
 
    .func=send_password_to_socket,
 
5510
 
    .question_filename=strdup(question_filename),
 
5511
 
    .filename=strdup(filename),
 
5514
 
    .password=&password,
 
5515
 
    .cancelled_filenames=&cancelled_filenames,
 
5516
 
    .mandos_client_exited=(bool[]){false},
 
5517
 
    .password_is_read=(bool[]){false},
 
5518
 
    .current_time=(mono_microsecs[]){0},
 
5520
 
  g_assert_nonnull(task.question_filename);
 
5522
 
  task.func(task, queue);
 
5524
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5526
 
  const task_context *const added_task = find_matching_task(queue,
 
5528
 
  g_assert_nonnull(added_task);
 
5529
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5530
 
  g_assert_true(queue->next_run == 0);
 
5532
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5533
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5538
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
5539
 
                                           test_fixture *fixture,
 
5540
 
                                           __attribute__((unused))
 
5541
 
                                           gconstpointer user_data){
 
5542
 
  __attribute__((cleanup(cleanup_close)))
 
5543
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5544
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5545
 
  const char question_filename[] = "/nonexistent/question";
 
5546
 
  char *const filename = strdup("/nonexistent/socket");
 
5547
 
  __attribute__((cleanup(string_set_clear)))
 
5548
 
    string_set cancelled_filenames = {};
 
5549
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5550
 
  __attribute__((cleanup(cleanup_buffer)))
 
5552
 
    .data=malloc(oversized),
 
5554
 
    .allocated=oversized,
 
5556
 
  g_assert_nonnull(password.data);
 
5557
 
  if(mlock(password.data, password.allocated) != 0){
 
5558
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5560
 
  /* Construct test password buffer */
 
5561
 
  /* Start with + since that is what the real procotol uses */
 
5562
 
  password.data[0] = '+';
 
5563
 
  /* Set a special character at string end just to mark the end */
 
5564
 
  password.data[oversized-3] = 'y';
 
5565
 
  /* Set NUL at buffer end, as suggested by the protocol */
 
5566
 
  password.data[oversized-2] = '\0';
 
5567
 
  /* Fill rest of password with 'x' */
 
5568
 
  memset(password.data+1, 'x', oversized-3);
 
5570
 
  __attribute__((cleanup(cleanup_queue)))
 
5571
 
    task_queue *queue = create_queue();
 
5572
 
  g_assert_nonnull(queue);
 
5574
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5575
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5577
 
  __attribute__((cleanup(cleanup_close)))
 
5578
 
    const int read_socket = socketfds[0];
 
5579
 
  __attribute__((cleanup(cleanup_close)))
 
5580
 
    const int write_socket = socketfds[1];
 
5581
 
  task_context task = {
 
5582
 
    .func=send_password_to_socket,
 
5583
 
    .question_filename=strdup(question_filename),
 
5587
 
    .password=&password,
 
5588
 
    .cancelled_filenames=&cancelled_filenames,
 
5589
 
    .mandos_client_exited=(bool[]){true},
 
5590
 
    .password_is_read=(bool[]){true},
 
5591
 
    .current_time=(mono_microsecs[]){0},
 
5593
 
  g_assert_nonnull(task.question_filename);
 
5595
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5597
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5598
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5599
 
                                    question_filename));
 
5602
 
static void test_send_password_to_socket_retry(__attribute__((unused))
 
5603
 
                                               test_fixture *fixture,
 
5604
 
                                               __attribute__((unused))
 
5607
 
  __attribute__((cleanup(cleanup_close)))
 
5608
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5609
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5610
 
  __attribute__((cleanup(cleanup_string)))
 
5611
 
    char *const question_filename = strdup("/nonexistent/question");
 
5612
 
  g_assert_nonnull(question_filename);
 
5613
 
  __attribute__((cleanup(cleanup_string)))
 
5614
 
    char *const filename = strdup("/nonexistent/socket");
 
5615
 
  g_assert_nonnull(filename);
 
5616
 
  __attribute__((cleanup(string_set_clear)))
 
5617
 
    string_set cancelled_filenames = {};
 
5618
 
  __attribute__((cleanup(cleanup_buffer)))
 
5619
 
    buffer password = {};
 
5621
 
  __attribute__((cleanup(cleanup_queue)))
 
5622
 
    task_queue *queue = create_queue();
 
5623
 
  g_assert_nonnull(queue);
 
5625
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5626
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5628
 
  __attribute__((cleanup(cleanup_close)))
 
5629
 
    const int read_socket = socketfds[0];
 
5630
 
  const int write_socket = socketfds[1];
 
5631
 
  /* Close the server side socket to force ECONNRESET on client */
 
5632
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
5633
 
  task_context task = {
 
5634
 
    .func=send_password_to_socket,
 
5635
 
    .question_filename=strdup(question_filename),
 
5636
 
    .filename=strdup(filename),
 
5639
 
    .password=&password,
 
5640
 
    .cancelled_filenames=&cancelled_filenames,
 
5641
 
    .mandos_client_exited=(bool[]){true},
 
5642
 
    .password_is_read=(bool[]){true},
 
5643
 
    .current_time=(mono_microsecs[]){0},
 
5645
 
  g_assert_nonnull(task.question_filename);
 
5647
 
  task.func(task, queue);
 
5649
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5651
 
  const task_context *const added_task = find_matching_task(queue,
 
5653
 
  g_assert_nonnull(added_task);
 
5654
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5656
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5661
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
5662
 
                                            test_fixture *fixture,
 
5663
 
                                            __attribute__((unused))
 
5664
 
                                            gconstpointer user_data){
 
5665
 
  __attribute__((cleanup(cleanup_close)))
 
5666
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5667
 
  __attribute__((cleanup(cleanup_string)))
 
5668
 
    char *const question_filename = strdup("/nonexistent/question");
 
5669
 
  g_assert_nonnull(question_filename);
 
5670
 
  __attribute__((cleanup(cleanup_string)))
 
5671
 
    char *const filename = strdup("/nonexistent/socket");
 
5672
 
  g_assert_nonnull(filename);
 
5673
 
  __attribute__((cleanup(string_set_clear)))
 
5674
 
    string_set cancelled_filenames = {};
 
5675
 
  __attribute__((cleanup(cleanup_buffer)))
 
5676
 
    buffer password = {};
 
5678
 
  const mono_microsecs current_time = 11;
 
5679
 
  __attribute__((cleanup(cleanup_queue)))
 
5680
 
    task_queue *queue = create_queue();
 
5681
 
  g_assert_nonnull(queue);
 
5683
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5684
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5686
 
  __attribute__((cleanup(cleanup_close)))
 
5687
 
    const int read_socket = socketfds[0];
 
5688
 
  const int write_socket = socketfds[1];
 
5689
 
  /* Close the server side socket to force ECONNRESET on client */
 
5690
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
5691
 
  task_context task = {
 
5692
 
    .func=send_password_to_socket,
 
5693
 
    .question_filename=strdup(question_filename),
 
5694
 
    .filename=strdup(filename),
 
5697
 
    .password=&password,
 
5698
 
    .cancelled_filenames=&cancelled_filenames,
 
5699
 
    .mandos_client_exited=(bool[]){true},
 
5700
 
    .password_is_read=(bool[]){true},
 
5701
 
    .current_time=¤t_time,
 
5703
 
  g_assert_nonnull(task.question_filename);
 
5705
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5707
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5709
 
  const task_context *const added_task = find_matching_task(queue,
 
5711
 
  g_assert_nonnull(added_task);
 
5712
 
  g_assert_true(queue->next_run == current_time + 1000000);
 
5713
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5716
 
static void assert_send_password_to_socket_password(buffer password){
 
5717
 
  __attribute__((cleanup(cleanup_close)))
 
5718
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5719
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5720
 
  char *const question_filename = strdup("/nonexistent/question");
 
5721
 
  g_assert_nonnull(question_filename);
 
5722
 
  char *const filename = strdup("/nonexistent/socket");
 
5723
 
  g_assert_nonnull(filename);
 
5724
 
  __attribute__((cleanup(string_set_clear)))
 
5725
 
    string_set cancelled_filenames = {};
 
5727
 
  __attribute__((cleanup(cleanup_queue)))
 
5728
 
    task_queue *queue = create_queue();
 
5729
 
  g_assert_nonnull(queue);
 
5731
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5732
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5734
 
  __attribute__((cleanup(cleanup_close)))
 
5735
 
    const int read_socket = socketfds[0];
 
5736
 
  const int write_socket = socketfds[1];
 
5737
 
  task_context task = {
 
5738
 
    .func=send_password_to_socket,
 
5739
 
    .question_filename=question_filename,
 
5743
 
    .password=&password,
 
5744
 
    .cancelled_filenames=&cancelled_filenames,
 
5745
 
    .mandos_client_exited=(bool[]){true},
 
5746
 
    .password_is_read=(bool[]){true},
 
5747
 
    .current_time=(mono_microsecs[]){0},
 
5750
 
  char *expected_written_data = malloc(password.length + 2);
 
5751
 
  g_assert_nonnull(expected_written_data);
 
5752
 
  expected_written_data[0] = '+';
 
5753
 
  expected_written_data[password.length + 1] = '\0';
 
5754
 
  if(password.length > 0){
 
5755
 
    g_assert_nonnull(password.data);
 
5756
 
    memcpy(expected_written_data + 1, password.data, password.length);
 
5759
 
  task.func(task, queue);
 
5762
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
5763
 
                  (int)(password.length + 2));
 
5764
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5766
 
  g_assert_true(memcmp(expected_written_data, buf,
 
5767
 
                       password.length + 2) == 0);
 
5769
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
5771
 
  free(expected_written_data);
 
5775
 
test_send_password_to_socket_null_password(__attribute__((unused))
 
5776
 
                                           test_fixture *fixture,
 
5777
 
                                           __attribute__((unused))
 
5778
 
                                           gconstpointer user_data){
 
5779
 
  __attribute__((cleanup(cleanup_buffer)))
 
5780
 
    buffer password = {};
 
5781
 
  assert_send_password_to_socket_password(password);
 
5785
 
test_send_password_to_socket_empty_password(__attribute__((unused))
 
5786
 
                                            test_fixture *fixture,
 
5787
 
                                            __attribute__((unused))
 
5788
 
                                            gconstpointer user_data){
 
5789
 
  __attribute__((cleanup(cleanup_buffer)))
 
5791
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
5793
 
    .allocated=0,               /* deliberate lie */
 
5795
 
  g_assert_nonnull(password.data);
 
5796
 
  assert_send_password_to_socket_password(password);
 
5800
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
5801
 
                                            test_fixture *fixture,
 
5802
 
                                            __attribute__((unused))
 
5803
 
                                            gconstpointer user_data){
 
5804
 
  __attribute__((cleanup(cleanup_buffer)))
 
5810
 
  if(mlock(password.data, password.allocated) != 0){
 
5811
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5813
 
  assert_send_password_to_socket_password(password);
 
5817
 
test_send_password_to_socket_text_password(__attribute__((unused))
 
5818
 
                                           test_fixture *fixture,
 
5819
 
                                           __attribute__((unused))
 
5820
 
                                           gconstpointer user_data){
 
5821
 
  const char dummy_test_password[] = "dummy test password";
 
5822
 
  __attribute__((cleanup(cleanup_buffer)))
 
5824
 
    .data = strdup(dummy_test_password),
 
5825
 
    .length = strlen(dummy_test_password),
 
5826
 
    .allocated = sizeof(dummy_test_password),
 
5828
 
  if(mlock(password.data, password.allocated) != 0){
 
5829
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5831
 
  assert_send_password_to_socket_password(password);
 
5835
 
test_send_password_to_socket_binary_password(__attribute__((unused))
 
5836
 
                                             test_fixture *fixture,
 
5837
 
                                             __attribute__((unused))
 
5838
 
                                             gconstpointer user_data){
 
5839
 
  __attribute__((cleanup(cleanup_buffer)))
 
5845
 
  g_assert_nonnull(password.data);
 
5846
 
  if(mlock(password.data, password.allocated) != 0){
 
5847
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5849
 
  char c = 1;                   /* Start at 1, avoiding NUL */
 
5850
 
  for(int i=0; i < 255; i++){
 
5851
 
    password.data[i] = c++;
 
5853
 
  assert_send_password_to_socket_password(password);
 
5857
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
5858
 
                                              test_fixture *fixture,
 
5859
 
                                              __attribute__((unused))
 
5862
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
5863
 
  __attribute__((cleanup(cleanup_buffer)))
 
5865
 
    .data=malloc(sizeof(test_password)),
 
5866
 
    .length=sizeof(test_password),
 
5867
 
    .allocated=sizeof(test_password),
 
5869
 
  g_assert_nonnull(password.data);
 
5870
 
  if(mlock(password.data, password.allocated) !=0){
 
5871
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5873
 
  memcpy(password.data, test_password, password.allocated);
 
5874
 
  assert_send_password_to_socket_password(password);
 
5877
 
static bool assert_add_existing_questions_to_devnull(task_queue
 
5890
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
5891
 
                                               test_fixture *fixture,
 
5892
 
                                               __attribute__((unused))
 
5895
 
  __attribute__((cleanup(cleanup_queue)))
 
5896
 
    task_queue *queue = create_queue();
 
5897
 
  g_assert_nonnull(queue);
 
5898
 
  __attribute__((cleanup(cleanup_close)))
 
5899
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5900
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5901
 
  __attribute__((cleanup(string_set_clear)))
 
5902
 
    string_set cancelled_filenames = {};
 
5904
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
5907
 
                  (buffer[]){{}}, /* password */
 
5908
 
                  &cancelled_filenames,
 
5909
 
                  (mono_microsecs[]){0}, /* current_time */
 
5910
 
                  (bool[]){false},       /* mandos_client_exited */
 
5911
 
                  (bool[]){false},       /* password_is_read */
 
5912
 
                  "/nonexistent"));      /* dirname */
 
5914
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5918
 
bool assert_add_existing_questions_to_devnull(task_queue
 
5925
 
                                              *cancelled_filenames,
 
5926
 
                                              const mono_microsecs
 
5927
 
                                              *const current_time,
 
5929
 
                                              mandos_client_exited,
 
5934
 
  __attribute__((cleanup(cleanup_close)))
 
5935
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5936
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
5937
 
  __attribute__((cleanup(cleanup_close)))
 
5938
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
5939
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
5940
 
  dup2(devnull_fd, STDERR_FILENO);
 
5941
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
5942
 
                                          cancelled_filenames,
 
5944
 
                                          mandos_client_exited,
 
5945
 
                                          password_is_read, dirname);
 
5946
 
  dup2(real_stderr_fd, STDERR_FILENO);
 
5951
 
void test_add_existing_questions_no_questions(__attribute__((unused))
 
5952
 
                                              test_fixture *fixture,
 
5953
 
                                              __attribute__((unused))
 
5956
 
  __attribute__((cleanup(cleanup_queue)))
 
5957
 
    task_queue *queue = create_queue();
 
5958
 
  g_assert_nonnull(queue);
 
5959
 
  __attribute__((cleanup(cleanup_close)))
 
5960
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5961
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5962
 
  __attribute__((cleanup(string_set_clear)))
 
5963
 
    string_set cancelled_filenames = {};
 
5964
 
  __attribute__((cleanup(cleanup_string)))
 
5965
 
    char *tempdir = make_temporary_directory();
 
5966
 
  g_assert_nonnull(tempdir);
 
5968
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
5971
 
                  (buffer[]){{}}, /* password */
 
5972
 
                  &cancelled_filenames,
 
5973
 
                  (mono_microsecs[]){0}, /* current_time */
 
5974
 
                  (bool[]){false},       /* mandos_client_exited */
 
5975
 
                  (bool[]){false},       /* password_is_read */
 
5978
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5980
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5983
 
static char *make_question_file_in_directory(const char *const);
 
5986
 
void test_add_existing_questions_one_question(__attribute__((unused))
 
5987
 
                                              test_fixture *fixture,
 
5988
 
                                              __attribute__((unused))
 
5991
 
  __attribute__((cleanup(cleanup_queue)))
 
5992
 
    task_queue *queue = create_queue();
 
5993
 
  g_assert_nonnull(queue);
 
5994
 
  __attribute__((cleanup(cleanup_close)))
 
5995
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5996
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5997
 
  __attribute__((cleanup(cleanup_buffer)))
 
5998
 
    buffer password = {};
 
5999
 
  __attribute__((cleanup(string_set_clear)))
 
6000
 
    string_set cancelled_filenames = {};
 
6001
 
  const mono_microsecs current_time = 0;
 
6002
 
  bool mandos_client_exited = false;
 
6003
 
  bool password_is_read = false;
 
6004
 
  __attribute__((cleanup(cleanup_string)))
 
6005
 
    char *tempdir = make_temporary_directory();
 
6006
 
  g_assert_nonnull(tempdir);
 
6007
 
  __attribute__((cleanup(cleanup_string)))
 
6008
 
    char *question_filename
 
6009
 
    = make_question_file_in_directory(tempdir);
 
6010
 
  g_assert_nonnull(question_filename);
 
6012
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6016
 
                 &cancelled_filenames,
 
6018
 
                 &mandos_client_exited,
 
6022
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6024
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6025
 
        .func=open_and_parse_question,
 
6027
 
        .filename=question_filename,
 
6028
 
        .question_filename=question_filename,
 
6029
 
        .password=&password,
 
6030
 
        .cancelled_filenames=&cancelled_filenames,
 
6031
 
        .current_time=¤t_time,
 
6032
 
        .mandos_client_exited=&mandos_client_exited,
 
6033
 
        .password_is_read=&password_is_read,
 
6036
 
  g_assert_true(queue->next_run == 1);
 
6038
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6039
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6042
 
static char *make_question_file_in_directory(const char
 
6044
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
6048
 
void test_add_existing_questions_two_questions(__attribute__((unused))
 
6049
 
                                               test_fixture *fixture,
 
6050
 
                                               __attribute__((unused))
 
6053
 
  __attribute__((cleanup(cleanup_queue)))
 
6054
 
    task_queue *queue = create_queue();
 
6055
 
  g_assert_nonnull(queue);
 
6056
 
  __attribute__((cleanup(cleanup_close)))
 
6057
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6058
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6059
 
  __attribute__((cleanup(cleanup_buffer)))
 
6060
 
    buffer password = {};
 
6061
 
  __attribute__((cleanup(string_set_clear)))
 
6062
 
    string_set cancelled_filenames = {};
 
6063
 
  const mono_microsecs current_time = 0;
 
6064
 
  bool mandos_client_exited = false;
 
6065
 
  bool password_is_read = false;
 
6066
 
  __attribute__((cleanup(cleanup_string)))
 
6067
 
    char *tempdir = make_temporary_directory();
 
6068
 
  g_assert_nonnull(tempdir);
 
6069
 
  __attribute__((cleanup(cleanup_string)))
 
6070
 
    char *question_filename1
 
6071
 
    = make_question_file_in_directory(tempdir);
 
6072
 
  g_assert_nonnull(question_filename1);
 
6073
 
  __attribute__((cleanup(cleanup_string)))
 
6074
 
    char *question_filename2
 
6075
 
    = make_question_file_in_directory(tempdir);
 
6076
 
  g_assert_nonnull(question_filename2);
 
6078
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6082
 
                 &cancelled_filenames,
 
6084
 
                 &mandos_client_exited,
 
6088
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
6090
 
  g_assert_true(queue->next_run == 1);
 
6092
 
  __attribute__((cleanup(string_set_clear)))
 
6093
 
    string_set seen_questions = {};
 
6095
 
  bool queue_contains_question_opener(char *const question_filename){
 
6096
 
    return(find_matching_task(queue, (task_context){
 
6097
 
          .func=open_and_parse_question,
 
6099
 
          .question_filename=question_filename,
 
6100
 
          .password=&password,
 
6101
 
          .cancelled_filenames=&cancelled_filenames,
 
6102
 
          .current_time=¤t_time,
 
6103
 
          .mandos_client_exited=&mandos_client_exited,
 
6104
 
          .password_is_read=&password_is_read,
 
6108
 
  g_assert_true(queue_contains_question_opener(question_filename1));
 
6109
 
  g_assert_true(queue_contains_question_opener(question_filename2));
 
6111
 
  g_assert_true(queue->next_run == 1);
 
6113
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6114
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6115
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6119
 
test_add_existing_questions_non_questions(__attribute__((unused))
 
6120
 
                                          test_fixture *fixture,
 
6121
 
                                          __attribute__((unused))
 
6122
 
                                          gconstpointer user_data){
 
6123
 
  __attribute__((cleanup(cleanup_queue)))
 
6124
 
    task_queue *queue = create_queue();
 
6125
 
  g_assert_nonnull(queue);
 
6126
 
  __attribute__((cleanup(cleanup_close)))
 
6127
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6128
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6129
 
  __attribute__((cleanup(string_set_clear)))
 
6130
 
    string_set cancelled_filenames = {};
 
6131
 
  __attribute__((cleanup(cleanup_string)))
 
6132
 
    char *tempdir = make_temporary_directory();
 
6133
 
  g_assert_nonnull(tempdir);
 
6134
 
  __attribute__((cleanup(cleanup_string)))
 
6135
 
    char *question_filename1
 
6136
 
    = make_temporary_file_in_directory(tempdir);
 
6137
 
  g_assert_nonnull(question_filename1);
 
6138
 
  __attribute__((cleanup(cleanup_string)))
 
6139
 
    char *question_filename2
 
6140
 
    = make_temporary_file_in_directory(tempdir);
 
6141
 
  g_assert_nonnull(question_filename2);
 
6143
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6146
 
                  (buffer[]){{}}, /* password */
 
6147
 
                  &cancelled_filenames,
 
6148
 
                  (mono_microsecs[]){0}, /* current_time */
 
6149
 
                  (bool[]){false},       /* mandos_client_exited */
 
6150
 
                  (bool[]){false},       /* password_is_read */
 
6153
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6155
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6156
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6157
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6161
 
test_add_existing_questions_both_types(__attribute__((unused))
 
6162
 
                                       test_fixture *fixture,
 
6163
 
                                       __attribute__((unused))
 
6164
 
                                       gconstpointer user_data){
 
6165
 
  __attribute__((cleanup(cleanup_queue)))
 
6166
 
    task_queue *queue = create_queue();
 
6167
 
  g_assert_nonnull(queue);
 
6168
 
  __attribute__((cleanup(cleanup_close)))
 
6169
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6170
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6171
 
  __attribute__((cleanup(cleanup_buffer)))
 
6172
 
    buffer password = {};
 
6173
 
  __attribute__((cleanup(string_set_clear)))
 
6174
 
    string_set cancelled_filenames = {};
 
6175
 
  const mono_microsecs current_time = 0;
 
6176
 
  bool mandos_client_exited = false;
 
6177
 
  bool password_is_read = false;
 
6178
 
  __attribute__((cleanup(cleanup_string)))
 
6179
 
    char *tempdir = make_temporary_directory();
 
6180
 
  g_assert_nonnull(tempdir);
 
6181
 
  __attribute__((cleanup(cleanup_string)))
 
6182
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
6183
 
  g_assert_nonnull(tempfilename1);
 
6184
 
  __attribute__((cleanup(cleanup_string)))
 
6185
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
6186
 
  g_assert_nonnull(tempfilename2);
 
6187
 
  __attribute__((cleanup(cleanup_string)))
 
6188
 
    char *question_filename
 
6189
 
    = make_question_file_in_directory(tempdir);
 
6190
 
  g_assert_nonnull(question_filename);
 
6192
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6196
 
                 &cancelled_filenames,
 
6198
 
                 &mandos_client_exited,
 
6202
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6204
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6205
 
        .func=open_and_parse_question,
 
6207
 
        .filename=question_filename,
 
6208
 
        .question_filename=question_filename,
 
6209
 
        .password=&password,
 
6210
 
        .cancelled_filenames=&cancelled_filenames,
 
6211
 
        .current_time=¤t_time,
 
6212
 
        .mandos_client_exited=&mandos_client_exited,
 
6213
 
        .password_is_read=&password_is_read,
 
6216
 
  g_assert_true(queue->next_run == 1);
 
6218
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
6219
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
6220
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6221
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6224
 
static void test_wait_for_event_timeout(__attribute__((unused))
 
6225
 
                                        test_fixture *fixture,
 
6226
 
                                        __attribute__((unused))
 
6227
 
                                        gconstpointer user_data){
 
6228
 
  __attribute__((cleanup(cleanup_close)))
 
6229
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6230
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6232
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
6235
 
static void test_wait_for_event_event(__attribute__((unused))
 
6236
 
                                      test_fixture *fixture,
 
6237
 
                                      __attribute__((unused))
 
6238
 
                                      gconstpointer user_data){
 
6239
 
  __attribute__((cleanup(cleanup_close)))
 
6240
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6241
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6243
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6244
 
  __attribute__((cleanup(cleanup_close)))
 
6245
 
    const int read_pipe = pipefds[0];
 
6246
 
  __attribute__((cleanup(cleanup_close)))
 
6247
 
    const int write_pipe = pipefds[1];
 
6248
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
6249
 
                            &(struct epoll_event)
 
6250
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
6251
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
6253
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6256
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
6257
 
                                        __attribute__((unused))
 
6258
 
                                        gconstpointer user_data){
 
6259
 
  const pid_t pid = fork();
 
6260
 
  if(pid == 0){         /* Child */
 
6261
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
6262
 
      _exit(EXIT_FAILURE);
 
6264
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
6265
 
      _exit(EXIT_FAILURE);
 
6269
 
  g_assert_true(pid != -1);
 
6270
 
  __attribute__((cleanup(cleanup_close)))
 
6271
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6272
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6274
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6277
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
6278
 
  g_assert_true(WIFEXITED(status));
 
6279
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
6282
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
6283
 
                                           test_fixture *fixture,
 
6284
 
                                           __attribute__((unused))
 
6285
 
                                           gconstpointer user_data){
 
6286
 
  __attribute__((cleanup(cleanup_queue)))
 
6287
 
    task_queue *queue = create_queue();
 
6288
 
  g_assert_nonnull(queue);
 
6289
 
  queue->next_run = 1;
 
6290
 
  __attribute__((cleanup(cleanup_close)))
 
6291
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6292
 
  __attribute__((cleanup(string_set_clear)))
 
6293
 
    string_set cancelled_filenames = {};
 
6294
 
  bool quit_now = false;
 
6296
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6297
 
  g_assert_false(quit_now);
 
6298
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
6302
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
6303
 
                                               test_fixture *fixture,
 
6304
 
                                               __attribute__((unused))
 
6307
 
  __attribute__((cleanup(cleanup_queue)))
 
6308
 
    task_queue *queue = create_queue();
 
6309
 
  g_assert_nonnull(queue);
 
6310
 
  __attribute__((cleanup(string_set_clear)))
 
6311
 
    string_set cancelled_filenames = {};
 
6312
 
  bool quit_now = false;
 
6313
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6314
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6315
 
                               question_filename));
 
6317
 
  g_assert_true(add_to_queue(queue,
 
6318
 
                             (task_context){ .func=dummy_func }));
 
6320
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6321
 
  g_assert_false(quit_now);
 
6322
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6323
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
6324
 
                                     question_filename));
 
6328
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
6329
 
                                              test_fixture *fixture,
 
6330
 
                                              __attribute__((unused))
 
6333
 
  __attribute__((cleanup(cleanup_queue)))
 
6334
 
    task_queue *queue = create_queue();
 
6335
 
  g_assert_nonnull(queue);
 
6336
 
  __attribute__((cleanup(string_set_clear)))
 
6337
 
    string_set cancelled_filenames = {};
 
6338
 
  bool quit_now = false;
 
6340
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6341
 
  __attribute__((cleanup(cleanup_close)))
 
6342
 
    const int read_pipe = pipefds[0];
 
6343
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
6344
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6345
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6346
 
                               question_filename));
 
6347
 
  __attribute__((nonnull))
 
6348
 
    void quit_func(const task_context task,
 
6349
 
                   __attribute__((unused)) task_queue *const q){
 
6350
 
    g_assert_nonnull(task.quit_now);
 
6351
 
    *task.quit_now = true;
 
6353
 
  task_context task = {
 
6355
 
    .question_filename=strdup(question_filename),
 
6356
 
    .quit_now=&quit_now,
 
6359
 
  g_assert_nonnull(task.question_filename);
 
6361
 
  g_assert_true(add_to_queue(queue, task));
 
6363
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6364
 
  g_assert_false(quit_now);
 
6366
 
  /* read_pipe should be closed already */
 
6368
 
  bool read_pipe_closed = (close(read_pipe) == -1);
 
6369
 
  read_pipe_closed &= (errno == EBADF);
 
6370
 
  g_assert_true(read_pipe_closed);
 
6373
 
static void test_run_queue_one_task(__attribute__((unused))
 
6374
 
                                    test_fixture *fixture,
 
6375
 
                                    __attribute__((unused))
 
6376
 
                                    gconstpointer user_data){
 
6377
 
  __attribute__((cleanup(cleanup_queue)))
 
6378
 
    task_queue *queue = create_queue();
 
6379
 
  g_assert_nonnull(queue);
 
6380
 
  __attribute__((cleanup(string_set_clear)))
 
6381
 
    string_set cancelled_filenames = {};
 
6382
 
  bool quit_now = false;
 
6384
 
  __attribute__((nonnull))
 
6385
 
    void next_run_func(__attribute__((unused))
 
6386
 
                       const task_context task,
 
6387
 
                       task_queue *const q){
 
6391
 
  task_context task = {
 
6392
 
    .func=next_run_func,
 
6394
 
  g_assert_true(add_to_queue(queue, task));
 
6396
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6397
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6398
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6401
 
static void test_run_queue_two_tasks(__attribute__((unused))
 
6402
 
                                     test_fixture *fixture,
 
6403
 
                                     __attribute__((unused))
 
6404
 
                                     gconstpointer user_data){
 
6405
 
  __attribute__((cleanup(cleanup_queue)))
 
6406
 
    task_queue *queue = create_queue();
 
6407
 
  g_assert_nonnull(queue);
 
6408
 
  queue->next_run = 1;
 
6409
 
  __attribute__((cleanup(string_set_clear)))
 
6410
 
    string_set cancelled_filenames = {};
 
6411
 
  bool quit_now = false;
 
6412
 
  bool mandos_client_exited = false;
 
6414
 
  __attribute__((nonnull))
 
6415
 
    void next_run_func(__attribute__((unused))
 
6416
 
                       const task_context task,
 
6417
 
                       task_queue *const q){
 
6421
 
  __attribute__((nonnull))
 
6422
 
    void exited_func(const task_context task,
 
6423
 
                     __attribute__((unused)) task_queue *const q){
 
6424
 
    *task.mandos_client_exited = true;
 
6427
 
  task_context task1 = {
 
6428
 
    .func=next_run_func,
 
6430
 
  g_assert_true(add_to_queue(queue, task1));
 
6432
 
  task_context task2 = {
 
6434
 
    .mandos_client_exited=&mandos_client_exited,
 
6436
 
  g_assert_true(add_to_queue(queue, task2));
 
6438
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6439
 
  g_assert_false(quit_now);
 
6440
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6441
 
  g_assert_true(mandos_client_exited);
 
6442
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6445
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
6446
 
                                          test_fixture *fixture,
 
6447
 
                                          __attribute__((unused))
 
6448
 
                                          gconstpointer user_data){
 
6449
 
  __attribute__((cleanup(cleanup_queue)))
 
6450
 
    task_queue *queue = create_queue();
 
6451
 
  g_assert_nonnull(queue);
 
6452
 
  __attribute__((cleanup(string_set_clear)))
 
6453
 
    string_set cancelled_filenames = {};
 
6454
 
  bool quit_now = false;
 
6455
 
  bool mandos_client_exited = false;
 
6456
 
  bool password_is_read = false;
 
6458
 
  __attribute__((nonnull))
 
6459
 
    void set_exited_func(const task_context task,
 
6460
 
                         __attribute__((unused)) task_queue *const q){
 
6461
 
    *task.mandos_client_exited = true;
 
6462
 
    *task.quit_now = true;
 
6464
 
  task_context task1 = {
 
6465
 
    .func=set_exited_func,
 
6466
 
    .quit_now=&quit_now,
 
6467
 
    .mandos_client_exited=&mandos_client_exited,
 
6469
 
  g_assert_true(add_to_queue(queue, task1));
 
6471
 
  __attribute__((nonnull))
 
6472
 
    void set_read_func(const task_context task,
 
6473
 
                       __attribute__((unused)) task_queue *const q){
 
6474
 
    *task.quit_now = true;
 
6475
 
    *task.password_is_read = true;
 
6477
 
  task_context task2 = {
 
6478
 
    .func=set_read_func,
 
6479
 
    .quit_now=&quit_now,
 
6480
 
    .password_is_read=&password_is_read,
 
6482
 
  g_assert_true(add_to_queue(queue, task2));
 
6484
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6485
 
  g_assert_true(quit_now);
 
6486
 
  g_assert_true(mandos_client_exited xor password_is_read);
 
6487
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6490
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
6491
 
                                             test_fixture *fixture,
 
6492
 
                                             __attribute__((unused))
 
6493
 
                                             gconstpointer user_data){
 
6494
 
  __attribute__((cleanup(cleanup_queue)))
 
6495
 
    task_queue *queue = create_queue();
 
6496
 
  g_assert_nonnull(queue);
 
6497
 
  __attribute__((cleanup(string_set_clear)))
 
6498
 
    string_set cancelled_filenames = {};
 
6500
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6501
 
  __attribute__((cleanup(cleanup_close)))
 
6502
 
    const int read_pipe = pipefds[0];
 
6503
 
  __attribute__((cleanup(cleanup_close)))
 
6504
 
    const int write_pipe = pipefds[1];
 
6505
 
  bool quit_now = false;
 
6507
 
  __attribute__((nonnull))
 
6508
 
    void read_func(const task_context task,
 
6509
 
                   __attribute__((unused)) task_queue *const q){
 
6510
 
    *task.quit_now = true;
 
6512
 
  task_context task1 = {
 
6514
 
    .quit_now=&quit_now,
 
6517
 
  g_assert_true(add_to_queue(queue, task1));
 
6519
 
  __attribute__((nonnull))
 
6520
 
    void write_func(const task_context task,
 
6521
 
                    __attribute__((unused)) task_queue *const q){
 
6522
 
    *task.quit_now = true;
 
6524
 
  task_context task2 = {
 
6526
 
    .quit_now=&quit_now,
 
6529
 
  g_assert_true(add_to_queue(queue, task2));
 
6531
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6532
 
  g_assert_true(quit_now);
 
6534
 
  /* Either read_pipe or write_pipe should be closed already */
 
6536
 
  bool close_read_pipe = (close(read_pipe) == -1);
 
6537
 
  close_read_pipe &= (errno == EBADF);
 
6539
 
  bool close_write_pipe = (close(write_pipe) == -1);
 
6540
 
  close_write_pipe &= (errno == EBADF);
 
6541
 
  g_assert_true(close_read_pipe xor close_write_pipe);
 
6542
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6545
 
static void test_setup_signal_handler(__attribute__((unused))
 
6546
 
                                      test_fixture *fixture,
 
6547
 
                                      __attribute__((unused))
 
6548
 
                                      gconstpointer user_data){
 
6549
 
  /* Save current SIGCHLD action, whatever it is */
 
6550
 
  struct sigaction expected_sigchld_action;
 
6551
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
6554
 
  /* Act; i.e. run the setup_signal_handler() function */
 
6555
 
  struct sigaction actual_old_sigchld_action;
 
6556
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
6558
 
  /* Check that the function correctly set "actual_old_sigchld_action"
 
6559
 
     to the same values as the previously saved
 
6560
 
     "expected_sigchld_action" */
 
6561
 
  /* Check member sa_handler */
 
6562
 
  g_assert_true(actual_old_sigchld_action.sa_handler
 
6563
 
                == expected_sigchld_action.sa_handler);
 
6564
 
  /* Check member sa_mask */
 
6565
 
  for(int signum = 1; signum < NSIG; signum++){
 
6566
 
    const int expected_old_block_state
 
6567
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
6568
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6569
 
    const int actual_old_block_state
 
6570
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
6571
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
6572
 
    g_assert_cmpint(actual_old_block_state,
 
6573
 
                    ==, expected_old_block_state);
 
6575
 
  /* Check member sa_flags */
 
6576
 
  g_assert_true((actual_old_sigchld_action.sa_flags
 
6577
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6578
 
                == (expected_sigchld_action.sa_flags
 
6579
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
6581
 
  /* Retrieve the current signal handler for SIGCHLD as set by
 
6582
 
     setup_signal_handler() */
 
6583
 
  struct sigaction actual_new_sigchld_action;
 
6584
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
6585
 
                            &actual_new_sigchld_action), ==, 0);
 
6586
 
  /* Check that the signal handler (member sa_handler) is correctly
 
6587
 
     set to the "handle_sigchld" function */
 
6588
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
6589
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
6590
 
  g_assert_true(actual_new_sigchld_action.sa_handler
 
6592
 
  /* Check (in member sa_mask) that at least a handful of signals are
 
6593
 
     actually blocked during the signal handler */
 
6594
 
  for(int signum = 1; signum < NSIG; signum++){
 
6595
 
    int actual_new_block_state;
 
6601
 
      actual_new_block_state
 
6602
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
6603
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
6605
 
    case SIGKILL:               /* non-blockable */
 
6606
 
    case SIGSTOP:               /* non-blockable */
 
6607
 
    case SIGCHLD:               /* always blocked */
 
6612
 
  /* Check member sa_flags */
 
6613
 
  g_assert_true((actual_new_sigchld_action.sa_flags
 
6614
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6615
 
                == (SA_NOCLDSTOP | SA_RESTART));
 
6617
 
  /* Restore signal handler */
 
6618
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
6622
 
static void test_restore_signal_handler(__attribute__((unused))
 
6623
 
                                        test_fixture *fixture,
 
6624
 
                                        __attribute__((unused))
 
6625
 
                                        gconstpointer user_data){
 
6626
 
  /* Save current SIGCHLD action, whatever it is */
 
6627
 
  struct sigaction expected_sigchld_action;
 
6628
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
6630
 
  /* Since we haven't established a signal handler yet, there should
 
6631
 
     not be one established.  But another test may have relied on
 
6632
 
     restore_signal_handler() to restore the signal handler, and if
 
6633
 
     restore_signal_handler() is buggy (which we should be prepared
 
6634
 
     for in this test) the signal handler may not have been restored
 
6635
 
     properly; check for this: */
 
6636
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
6638
 
  /* Establish a signal handler */
 
6639
 
  struct sigaction sigchld_action = {
 
6640
 
    .sa_handler=handle_sigchld,
 
6641
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
6643
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
6644
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
6646
 
  /* Act; i.e. run the restore_signal_handler() function */
 
6647
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
6649
 
  /* Retrieve the restored signal handler data */
 
6650
 
  struct sigaction actual_restored_sigchld_action;
 
6651
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
6652
 
                            &actual_restored_sigchld_action), ==, 0);
 
6654
 
  /* Check that the function correctly restored the signal action, as
 
6655
 
     saved in "actual_restored_sigchld_action", to the same values as
 
6656
 
     the previously saved "expected_sigchld_action" */
 
6657
 
  /* Check member sa_handler */
 
6658
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
6659
 
                == expected_sigchld_action.sa_handler);
 
6660
 
  /* Check member sa_mask */
 
6661
 
  for(int signum = 1; signum < NSIG; signum++){
 
6662
 
    const int expected_old_block_state
 
6663
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
6664
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6665
 
    const int actual_restored_block_state
 
6666
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
6667
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
6668
 
    g_assert_cmpint(actual_restored_block_state,
 
6669
 
                    ==, expected_old_block_state);
 
6671
 
  /* Check member sa_flags */
 
6672
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
6673
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6674
 
                == (expected_sigchld_action.sa_flags
 
6675
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
6678
 
static void test_block_sigchld(__attribute__((unused))
 
6679
 
                               test_fixture *fixture,
 
6680
 
                               __attribute__((unused))
 
6681
 
                               gconstpointer user_data){
 
6682
 
  /* Save original signal mask */
 
6683
 
  sigset_t expected_sigmask;
 
6684
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
6687
 
  /* Make sure SIGCHLD is unblocked for this test */
 
6688
 
  sigset_t sigchld_sigmask;
 
6689
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
6690
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
6691
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
6694
 
  /* Act; i.e. run the block_sigchld() function */
 
6695
 
  sigset_t actual_old_sigmask;
 
6696
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
6698
 
  /* Check the actual_old_sigmask; it should be the same as the
 
6699
 
     previously saved signal mask "expected_sigmask". */
 
6700
 
  for(int signum = 1; signum < NSIG; signum++){
 
6701
 
    const int expected_old_block_state
 
6702
 
      = sigismember(&expected_sigmask, signum);
 
6703
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6704
 
    const int actual_old_block_state
 
6705
 
      = sigismember(&actual_old_sigmask, signum);
 
6706
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
6707
 
    g_assert_cmpint(actual_old_block_state,
 
6708
 
                    ==, expected_old_block_state);
 
6711
 
  /* Retrieve the newly set signal mask */
 
6712
 
  sigset_t actual_sigmask;
 
6713
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
6715
 
  /* SIGCHLD should be blocked */
 
6716
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
6718
 
  /* Restore signal mask */
 
6719
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
6723
 
static void test_restore_sigmask(__attribute__((unused))
 
6724
 
                                 test_fixture *fixture,
 
6725
 
                                 __attribute__((unused))
 
6726
 
                                 gconstpointer user_data){
 
6727
 
  /* Save original signal mask */
 
6728
 
  sigset_t orig_sigmask;
 
6729
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
6731
 
  /* Make sure SIGCHLD is blocked for this test */
 
6732
 
  sigset_t sigchld_sigmask;
 
6733
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
6734
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
6735
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
6738
 
  /* Act; i.e. run the restore_sigmask() function */
 
6739
 
  g_assert_true(restore_sigmask(&orig_sigmask));
 
6741
 
  /* Retrieve the newly restored signal mask */
 
6742
 
  sigset_t restored_sigmask;
 
6743
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
6746
 
  /* Check the restored_sigmask; it should be the same as the
 
6747
 
     previously saved signal mask "orig_sigmask". */
 
6748
 
  for(int signum = 1; signum < NSIG; signum++){
 
6749
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
6750
 
    g_assert_cmpint(orig_block_state, >=, 0);
 
6751
 
    const int restored_block_state = sigismember(&restored_sigmask,
 
6753
 
    g_assert_cmpint(restored_block_state, >=, 0);
 
6754
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
6757
 
  /* Restore signal mask */
 
6758
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
6762
 
static void test_parse_arguments_noargs(__attribute__((unused))
 
6763
 
                                        test_fixture *fixture,
 
6764
 
                                        __attribute__((unused))
 
6765
 
                                        gconstpointer user_data){
 
6769
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6771
 
  char *agent_directory = NULL;
 
6772
 
  char *helper_directory = NULL;
 
6775
 
  char *mandos_argz = NULL;
 
6776
 
  size_t mandos_argz_length = 0;
 
6778
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6779
 
                                &helper_directory, &user, &group,
 
6780
 
                                &mandos_argz, &mandos_argz_length));
 
6781
 
  g_assert_null(agent_directory);
 
6782
 
  g_assert_null(helper_directory);
 
6783
 
  g_assert_true(user == 0);
 
6784
 
  g_assert_true(group == 0);
 
6785
 
  g_assert_null(mandos_argz);
 
6786
 
  g_assert_true(mandos_argz_length == 0);
 
6788
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6793
 
__attribute__((nonnull))
 
6794
 
static bool parse_arguments_devnull(int argc, char *argv[],
 
6795
 
                                    const bool exit_failure,
 
6796
 
                                    char **agent_directory,
 
6797
 
                                    char **helper_directory,
 
6801
 
                                    size_t *mandos_argz_length){
 
6803
 
  FILE *real_stderr = stderr;
 
6804
 
  FILE *devnull = fopen("/dev/null", "we");
 
6805
 
  g_assert_nonnull(devnull);
 
6808
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
6810
 
                                   helper_directory, user, group,
 
6811
 
                                   mandos_argz, mandos_argz_length);
 
6812
 
  const error_t saved_errno = errno;
 
6814
 
  stderr = real_stderr;
 
6815
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
6817
 
  errno = saved_errno;
 
6822
 
static void test_parse_arguments_invalid(__attribute__((unused))
 
6823
 
                                         test_fixture *fixture,
 
6824
 
                                         __attribute__((unused))
 
6825
 
                                         gconstpointer user_data){
 
6828
 
    strdup("--invalid"),
 
6830
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6832
 
  char *agent_directory = NULL;
 
6833
 
  char *helper_directory = NULL;
 
6836
 
  char *mandos_argz = NULL;
 
6837
 
  size_t mandos_argz_length = 0;
 
6839
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
6841
 
                                         &helper_directory, &user,
 
6842
 
                                         &group, &mandos_argz,
 
6843
 
                                         &mandos_argz_length));
 
6845
 
  g_assert_true(errno == EINVAL);
 
6846
 
  g_assert_null(agent_directory);
 
6847
 
  g_assert_null(helper_directory);
 
6848
 
  g_assert_null(mandos_argz);
 
6849
 
  g_assert_true(mandos_argz_length == 0);
 
6851
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6856
 
static void test_parse_arguments_long_dir(__attribute__((unused))
 
6857
 
                                          test_fixture *fixture,
 
6858
 
                                          __attribute__((unused))
 
6859
 
                                          gconstpointer user_data){
 
6862
 
    strdup("--agent-directory"),
 
6865
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6867
 
  __attribute__((cleanup(cleanup_string)))
 
6868
 
    char *agent_directory = NULL;
 
6869
 
  char *helper_directory = NULL;
 
6872
 
  __attribute__((cleanup(cleanup_string)))
 
6873
 
    char *mandos_argz = NULL;
 
6874
 
  size_t mandos_argz_length = 0;
 
6876
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6877
 
                                &helper_directory, &user, &group,
 
6878
 
                                &mandos_argz, &mandos_argz_length));
 
6880
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
6881
 
  g_assert_null(helper_directory);
 
6882
 
  g_assert_true(user == 0);
 
6883
 
  g_assert_true(group == 0);
 
6884
 
  g_assert_null(mandos_argz);
 
6885
 
  g_assert_true(mandos_argz_length == 0);
 
6887
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6892
 
static void test_parse_arguments_short_dir(__attribute__((unused))
 
6893
 
                                           test_fixture *fixture,
 
6894
 
                                           __attribute__((unused))
 
6895
 
                                           gconstpointer user_data){
 
6901
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6903
 
  __attribute__((cleanup(cleanup_string)))
 
6904
 
    char *agent_directory = NULL;
 
6905
 
  char *helper_directory = NULL;
 
6908
 
  __attribute__((cleanup(cleanup_string)))
 
6909
 
    char *mandos_argz = NULL;
 
6910
 
  size_t mandos_argz_length = 0;
 
6912
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6913
 
                                &helper_directory, &user, &group,
 
6914
 
                                &mandos_argz, &mandos_argz_length));
 
6916
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
6917
 
  g_assert_null(helper_directory);
 
6918
 
  g_assert_true(user == 0);
 
6919
 
  g_assert_true(group == 0);
 
6920
 
  g_assert_null(mandos_argz);
 
6921
 
  g_assert_true(mandos_argz_length == 0);
 
6923
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6929
 
void test_parse_arguments_helper_directory(__attribute__((unused))
 
6930
 
                                           test_fixture *fixture,
 
6931
 
                                           __attribute__((unused))
 
6932
 
                                           gconstpointer user_data){
 
6935
 
    strdup("--helper-directory"),
 
6938
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6940
 
  char *agent_directory = NULL;
 
6941
 
  __attribute__((cleanup(cleanup_string)))
 
6942
 
    char *helper_directory = NULL;
 
6945
 
  __attribute__((cleanup(cleanup_string)))
 
6946
 
    char *mandos_argz = NULL;
 
6947
 
  size_t mandos_argz_length = 0;
 
6949
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6950
 
                                &helper_directory, &user, &group,
 
6951
 
                                &mandos_argz, &mandos_argz_length));
 
6953
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
6954
 
  g_assert_null(agent_directory);
 
6955
 
  g_assert_true(user == 0);
 
6956
 
  g_assert_true(group == 0);
 
6957
 
  g_assert_null(mandos_argz);
 
6958
 
  g_assert_true(mandos_argz_length == 0);
 
6960
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6966
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
6967
 
                                            test_fixture *fixture,
 
6968
 
                                            __attribute__((unused))
 
6969
 
                                            gconstpointer user_data){
 
6972
 
    strdup("--plugin-helper-dir"),
 
6975
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6977
 
  char *agent_directory = NULL;
 
6978
 
  __attribute__((cleanup(cleanup_string)))
 
6979
 
    char *helper_directory = NULL;
 
6982
 
  __attribute__((cleanup(cleanup_string)))
 
6983
 
    char *mandos_argz = NULL;
 
6984
 
  size_t mandos_argz_length = 0;
 
6986
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6987
 
                                &helper_directory, &user, &group,
 
6988
 
                                &mandos_argz, &mandos_argz_length));
 
6990
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
6991
 
  g_assert_null(agent_directory);
 
6992
 
  g_assert_true(user == 0);
 
6993
 
  g_assert_true(group == 0);
 
6994
 
  g_assert_null(mandos_argz);
 
6995
 
  g_assert_true(mandos_argz_length == 0);
 
6997
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7002
 
static void test_parse_arguments_user(__attribute__((unused))
 
7003
 
                                      test_fixture *fixture,
 
7004
 
                                      __attribute__((unused))
 
7005
 
                                      gconstpointer user_data){
 
7011
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7013
 
  char *agent_directory = NULL;
 
7014
 
  __attribute__((cleanup(cleanup_string)))
 
7015
 
    char *helper_directory = NULL;
 
7018
 
  __attribute__((cleanup(cleanup_string)))
 
7019
 
    char *mandos_argz = NULL;
 
7020
 
  size_t mandos_argz_length = 0;
 
7022
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7023
 
                                &helper_directory, &user, &group,
 
7024
 
                                &mandos_argz, &mandos_argz_length));
 
7026
 
  g_assert_null(helper_directory);
 
7027
 
  g_assert_null(agent_directory);
 
7028
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
7029
 
  g_assert_true(group == 0);
 
7030
 
  g_assert_null(mandos_argz);
 
7031
 
  g_assert_true(mandos_argz_length == 0);
 
7033
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7038
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
7039
 
                                              test_fixture *fixture,
 
7040
 
                                              __attribute__((unused))
 
7048
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7050
 
  char *agent_directory = NULL;
 
7051
 
  __attribute__((cleanup(cleanup_string)))
 
7052
 
    char *helper_directory = NULL;
 
7055
 
  __attribute__((cleanup(cleanup_string)))
 
7056
 
    char *mandos_argz = NULL;
 
7057
 
  size_t mandos_argz_length = 0;
 
7059
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7061
 
                                         &helper_directory, &user,
 
7062
 
                                         &group, &mandos_argz,
 
7063
 
                                         &mandos_argz_length));
 
7065
 
  g_assert_null(helper_directory);
 
7066
 
  g_assert_null(agent_directory);
 
7067
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7068
 
  g_assert_true(group == 0);
 
7069
 
  g_assert_null(mandos_argz);
 
7070
 
  g_assert_true(mandos_argz_length == 0);
 
7072
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7078
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
7079
 
                                            test_fixture *fixture,
 
7080
 
                                            __attribute__((unused))
 
7081
 
                                            gconstpointer user_data){
 
7087
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7089
 
  char *agent_directory = NULL;
 
7090
 
  __attribute__((cleanup(cleanup_string)))
 
7091
 
    char *helper_directory = NULL;
 
7094
 
  __attribute__((cleanup(cleanup_string)))
 
7095
 
    char *mandos_argz = NULL;
 
7096
 
  size_t mandos_argz_length = 0;
 
7098
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7100
 
                                         &helper_directory, &user,
 
7101
 
                                         &group, &mandos_argz,
 
7102
 
                                         &mandos_argz_length));
 
7104
 
  g_assert_null(helper_directory);
 
7105
 
  g_assert_null(agent_directory);
 
7106
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7107
 
  g_assert_true(group == 0);
 
7108
 
  g_assert_null(mandos_argz);
 
7109
 
  g_assert_true(mandos_argz_length == 0);
 
7111
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7116
 
static void test_parse_arguments_group(__attribute__((unused))
 
7117
 
                                       test_fixture *fixture,
 
7118
 
                                       __attribute__((unused))
 
7119
 
                                       gconstpointer user_data){
 
7125
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7127
 
  char *agent_directory = NULL;
 
7128
 
  __attribute__((cleanup(cleanup_string)))
 
7129
 
    char *helper_directory = NULL;
 
7132
 
  __attribute__((cleanup(cleanup_string)))
 
7133
 
    char *mandos_argz = NULL;
 
7134
 
  size_t mandos_argz_length = 0;
 
7136
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7137
 
                                &helper_directory, &user, &group,
 
7138
 
                                &mandos_argz, &mandos_argz_length));
 
7140
 
  g_assert_null(helper_directory);
 
7141
 
  g_assert_null(agent_directory);
 
7142
 
  g_assert_true(user == 0);
 
7143
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
7144
 
  g_assert_null(mandos_argz);
 
7145
 
  g_assert_true(mandos_argz_length == 0);
 
7147
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7152
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
7153
 
                                               test_fixture *fixture,
 
7154
 
                                               __attribute__((unused))
 
7162
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7164
 
  char *agent_directory = NULL;
 
7165
 
  __attribute__((cleanup(cleanup_string)))
 
7166
 
    char *helper_directory = NULL;
 
7169
 
  __attribute__((cleanup(cleanup_string)))
 
7170
 
    char *mandos_argz = NULL;
 
7171
 
  size_t mandos_argz_length = 0;
 
7173
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7175
 
                                         &helper_directory, &user,
 
7176
 
                                         &group, &mandos_argz,
 
7177
 
                                         &mandos_argz_length));
 
7179
 
  g_assert_null(helper_directory);
 
7180
 
  g_assert_null(agent_directory);
 
7181
 
  g_assert_true(user == 0);
 
7182
 
  g_assert_true(group == 0);
 
7183
 
  g_assert_null(mandos_argz);
 
7184
 
  g_assert_true(mandos_argz_length == 0);
 
7186
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7192
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
7193
 
                                             test_fixture *fixture,
 
7194
 
                                             __attribute__((unused))
 
7195
 
                                             gconstpointer user_data){
 
7201
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7203
 
  char *agent_directory = NULL;
 
7204
 
  __attribute__((cleanup(cleanup_string)))
 
7205
 
    char *helper_directory = NULL;
 
7208
 
  __attribute__((cleanup(cleanup_string)))
 
7209
 
    char *mandos_argz = NULL;
 
7210
 
  size_t mandos_argz_length = 0;
 
7212
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7214
 
                                         &helper_directory, &user,
 
7215
 
                                         &group, &mandos_argz,
 
7216
 
                                         &mandos_argz_length));
 
7218
 
  g_assert_null(helper_directory);
 
7219
 
  g_assert_null(agent_directory);
 
7220
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
7221
 
  g_assert_true(group == 0);
 
7222
 
  g_assert_null(mandos_argz);
 
7223
 
  g_assert_true(mandos_argz_length == 0);
 
7225
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7230
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
7231
 
                                               test_fixture *fixture,
 
7232
 
                                               __attribute__((unused))
 
7237
 
    strdup("mandos-client"),
 
7239
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7241
 
  __attribute__((cleanup(cleanup_string)))
 
7242
 
    char *agent_directory = NULL;
 
7243
 
  __attribute__((cleanup(cleanup_string)))
 
7244
 
    char *helper_directory = NULL;
 
7247
 
  __attribute__((cleanup(cleanup_string)))
 
7248
 
    char *mandos_argz = NULL;
 
7249
 
  size_t mandos_argz_length = 0;
 
7251
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7252
 
                                &helper_directory, &user, &group,
 
7253
 
                                &mandos_argz, &mandos_argz_length));
 
7255
 
  g_assert_null(agent_directory);
 
7256
 
  g_assert_null(helper_directory);
 
7257
 
  g_assert_true(user == 0);
 
7258
 
  g_assert_true(group == 0);
 
7259
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
7260
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7261
 
                                            mandos_argz_length),
 
7264
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7269
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
7270
 
                                             test_fixture *fixture,
 
7271
 
                                             __attribute__((unused))
 
7272
 
                                             gconstpointer user_data){
 
7275
 
    strdup("mandos-client"),
 
7280
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7282
 
  __attribute__((cleanup(cleanup_string)))
 
7283
 
    char *agent_directory = NULL;
 
7284
 
  __attribute__((cleanup(cleanup_string)))
 
7285
 
    char *helper_directory = NULL;
 
7288
 
  __attribute__((cleanup(cleanup_string)))
 
7289
 
    char *mandos_argz = NULL;
 
7290
 
  size_t mandos_argz_length = 0;
 
7292
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7293
 
                                &helper_directory, &user, &group,
 
7294
 
                                &mandos_argz, &mandos_argz_length));
 
7296
 
  g_assert_null(agent_directory);
 
7297
 
  g_assert_null(helper_directory);
 
7298
 
  g_assert_true(user == 0);
 
7299
 
  g_assert_true(group == 0);
 
7300
 
  char *marg = mandos_argz;
 
7301
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7302
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7303
 
  g_assert_cmpstr(marg, ==, "one");
 
7304
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7305
 
  g_assert_cmpstr(marg, ==, "two");
 
7306
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7307
 
  g_assert_cmpstr(marg, ==, "three");
 
7308
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7309
 
                                            mandos_argz_length),
 
7312
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7317
 
static void test_parse_arguments_all_args(__attribute__((unused))
 
7318
 
                                          test_fixture *fixture,
 
7319
 
                                          __attribute__((unused))
 
7320
 
                                          gconstpointer user_data){
 
7323
 
    strdup("--agent-directory"),
 
7325
 
    strdup("--helper-directory"),
 
7331
 
    strdup("mandos-client"),
 
7336
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7338
 
  __attribute__((cleanup(cleanup_string)))
 
7339
 
    char *agent_directory = NULL;
 
7340
 
  __attribute__((cleanup(cleanup_string)))
 
7341
 
    char *helper_directory = NULL;
 
7344
 
  __attribute__((cleanup(cleanup_string)))
 
7345
 
    char *mandos_argz = NULL;
 
7346
 
  size_t mandos_argz_length = 0;
 
7348
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7349
 
                                &helper_directory, &user, &group,
 
7350
 
                                &mandos_argz, &mandos_argz_length));
 
7352
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7353
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7354
 
  g_assert_true(user == 1);
 
7355
 
  g_assert_true(group == 2);
 
7356
 
  char *marg = mandos_argz;
 
7357
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7358
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7359
 
  g_assert_cmpstr(marg, ==, "one");
 
7360
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7361
 
  g_assert_cmpstr(marg, ==, "two");
 
7362
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7363
 
  g_assert_cmpstr(marg, ==, "three");
 
7364
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7365
 
                                            mandos_argz_length),
 
7368
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7373
 
static void test_parse_arguments_mixed(__attribute__((unused))
 
7374
 
                                       test_fixture *fixture,
 
7375
 
                                       __attribute__((unused))
 
7376
 
                                       gconstpointer user_data){
 
7379
 
    strdup("mandos-client"),
 
7383
 
    strdup("--agent-directory"),
 
7387
 
    strdup("--helper-directory=/var/tmp"),
 
7389
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7391
 
  __attribute__((cleanup(cleanup_string)))
 
7392
 
    char *agent_directory = NULL;
 
7393
 
  __attribute__((cleanup(cleanup_string)))
 
7394
 
    char *helper_directory = NULL;
 
7397
 
  __attribute__((cleanup(cleanup_string)))
 
7398
 
    char *mandos_argz = NULL;
 
7399
 
  size_t mandos_argz_length = 0;
 
7401
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7402
 
                                &helper_directory, &user, &group,
 
7403
 
                                &mandos_argz, &mandos_argz_length));
 
7405
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7406
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7407
 
  g_assert_true(user == 1);
 
7408
 
  g_assert_true(group == 0);
 
7409
 
  char *marg = mandos_argz;
 
7410
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7411
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7412
 
  g_assert_cmpstr(marg, ==, "one");
 
7413
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7414
 
  g_assert_cmpstr(marg, ==, "two");
 
7415
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7416
 
  g_assert_cmpstr(marg, ==, "three");
 
7417
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7418
 
                                            mandos_argz_length),
 
7421
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7426
 
/* End of tests section */
 
7428
 
/* Test boilerplate section; New tests should be added to the test
 
7429
 
   suite definition here, in the "run_tests" function.
 
7431
 
   Finally, this section also contains the should_only_run_tests()
 
7432
 
   function used by main() for deciding if tests should be run or to
 
7435
 
__attribute__((cold))
 
7436
 
static bool run_tests(int argc, char *argv[]){
 
7437
 
  g_test_init(&argc, &argv, NULL);
 
7439
 
  /* A macro to add a test with no setup or teardown functions */
 
7440
 
#define test_add(testpath, testfunc)                    \
 
7442
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
7443
 
               (testfunc), NULL);                       \
 
7446
 
  /* Test the signal-related functions first, since some other tests
 
7447
 
     depend on these functions in their setups and teardowns */
 
7448
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
7449
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
7450
 
  test_add("/signal-handling/block", test_block_sigchld);
 
7451
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
7453
 
  /* Regular non-signal-related tests; these use no setups or
 
7455
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
7456
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
7457
 
  test_add("/parse_arguments/long-dir",
 
7458
 
           test_parse_arguments_long_dir);
 
7459
 
  test_add("/parse_arguments/short-dir",
 
7460
 
           test_parse_arguments_short_dir);
 
7461
 
  test_add("/parse_arguments/helper-directory",
 
7462
 
           test_parse_arguments_helper_directory);
 
7463
 
  test_add("/parse_arguments/plugin-helper-dir",
 
7464
 
           test_parse_arguments_plugin_helper_dir);
 
7465
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
7466
 
  test_add("/parse_arguments/user-invalid",
 
7467
 
           test_parse_arguments_user_invalid);
 
7468
 
  test_add("/parse_arguments/user-zero-invalid",
 
7469
 
           test_parse_arguments_user_zero_invalid);
 
7470
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
7471
 
  test_add("/parse_arguments/group-invalid",
 
7472
 
           test_parse_arguments_group_invalid);
 
7473
 
  test_add("/parse_arguments/group-zero-invalid",
 
7474
 
           test_parse_arguments_group_zero_invalid);
 
7475
 
  test_add("/parse_arguments/mandos-noargs",
 
7476
 
           test_parse_arguments_mandos_noargs);
 
7477
 
  test_add("/parse_arguments/mandos-args",
 
7478
 
           test_parse_arguments_mandos_args);
 
7479
 
  test_add("/parse_arguments/all-args",
 
7480
 
           test_parse_arguments_all_args);
 
7481
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
7482
 
  test_add("/queue/create", test_create_queue);
 
7483
 
  test_add("/queue/add", test_add_to_queue);
 
7484
 
  test_add("/queue/has_question/empty",
 
7485
 
           test_queue_has_question_empty);
 
7486
 
  test_add("/queue/has_question/false",
 
7487
 
           test_queue_has_question_false);
 
7488
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
7489
 
  test_add("/queue/has_question/false2",
 
7490
 
           test_queue_has_question_false2);
 
7491
 
  test_add("/queue/has_question/true2",
 
7492
 
           test_queue_has_question_true2);
 
7493
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
7494
 
  test_add("/string_set/net-set-contains-nothing",
 
7495
 
           test_string_set_new_set_contains_nothing);
 
7496
 
  test_add("/string_set/with-added-string-contains-it",
 
7497
 
           test_string_set_with_added_string_contains_it);
 
7498
 
  test_add("/string_set/cleared-does-not-contain-string",
 
7499
 
           test_string_set_cleared_does_not_contain_str);
 
7500
 
  test_add("/string_set/swap/one-with-empty",
 
7501
 
           test_string_set_swap_one_with_empty);
 
7502
 
  test_add("/string_set/swap/empty-with-one",
 
7503
 
           test_string_set_swap_empty_with_one);
 
7504
 
  test_add("/string_set/swap/one-with-one",
 
7505
 
           test_string_set_swap_one_with_one);
 
7507
 
  /* A macro to add a test using the setup and teardown functions */
 
7508
 
#define test_add_st(path, func)                                 \
 
7510
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
7514
 
  /* Signal-related tests; these use setups and teardowns which
 
7515
 
     establish, during each test run, a signal handler for, and a
 
7516
 
     signal mask blocking, the SIGCHLD signal, just like main() */
 
7517
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
7518
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
7519
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
7520
 
  test_add_st("/run_queue/zeroes-next-run",
 
7521
 
              test_run_queue_zeroes_next_run);
 
7522
 
  test_add_st("/run_queue/clears-cancelled_filenames",
 
7523
 
              test_run_queue_clears_cancelled_filenames);
 
7524
 
  test_add_st("/run_queue/skips-cancelled-filenames",
 
7525
 
              test_run_queue_skips_cancelled_filenames);
 
7526
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
7527
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
7528
 
  test_add_st("/run_queue/two-tasks/quit",
 
7529
 
              test_run_queue_two_tasks_quit);
 
7530
 
  test_add_st("/run_queue/two-tasks-cleanup",
 
7531
 
              test_run_queue_two_tasks_cleanup);
 
7532
 
  test_add_st("/task-creators/start_mandos_client",
 
7533
 
              test_start_mandos_client);
 
7534
 
  test_add_st("/task-creators/start_mandos_client/execv",
 
7535
 
              test_start_mandos_client_execv);
 
7536
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
7537
 
              test_start_mandos_client_suid_euid);
 
7538
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
7539
 
              test_start_mandos_client_suid_egid);
 
7540
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
7541
 
              test_start_mandos_client_suid_ruid);
 
7542
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
7543
 
              test_start_mandos_client_suid_rgid);
 
7544
 
  test_add_st("/task-creators/start_mandos_client/read",
 
7545
 
              test_start_mandos_client_read);
 
7546
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
7547
 
              test_start_mandos_client_helper_directory);
 
7548
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
7549
 
              test_start_mandos_client_sigmask);
 
7550
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
7551
 
              test_wait_for_mandos_client_exit_badpid);
 
7552
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
7553
 
              test_wait_for_mandos_client_exit_noexit);
 
7554
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
7555
 
              test_wait_for_mandos_client_exit_success);
 
7556
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
7557
 
              test_wait_for_mandos_client_exit_failure);
 
7558
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
7559
 
              test_wait_for_mandos_client_exit_killed);
 
7560
 
  test_add_st("/task/read_mandos_client_output/readerror",
 
7561
 
              test_read_mandos_client_output_readerror);
 
7562
 
  test_add_st("/task/read_mandos_client_output/nodata",
 
7563
 
              test_read_mandos_client_output_nodata);
 
7564
 
  test_add_st("/task/read_mandos_client_output/eof",
 
7565
 
              test_read_mandos_client_output_eof);
 
7566
 
  test_add_st("/task/read_mandos_client_output/once",
 
7567
 
              test_read_mandos_client_output_once);
 
7568
 
  test_add_st("/task/read_mandos_client_output/malloc",
 
7569
 
              test_read_mandos_client_output_malloc);
 
7570
 
  test_add_st("/task/read_mandos_client_output/append",
 
7571
 
              test_read_mandos_client_output_append);
 
7572
 
  test_add_st("/task-creators/add_inotify_dir_watch",
 
7573
 
              test_add_inotify_dir_watch);
 
7574
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
7575
 
              test_add_inotify_dir_watch_fail);
 
7576
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
7577
 
              test_add_inotify_dir_watch_EAGAIN);
 
7578
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
7579
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
7580
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
7581
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
7582
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
7583
 
              test_add_inotify_dir_watch_IN_DELETE);
 
7584
 
  test_add_st("/task/read_inotify_event/readerror",
 
7585
 
              test_read_inotify_event_readerror);
 
7586
 
  test_add_st("/task/read_inotify_event/bad-epoll",
 
7587
 
              test_read_inotify_event_bad_epoll);
 
7588
 
  test_add_st("/task/read_inotify_event/nodata",
 
7589
 
              test_read_inotify_event_nodata);
 
7590
 
  test_add_st("/task/read_inotify_event/eof",
 
7591
 
              test_read_inotify_event_eof);
 
7592
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
7593
 
              test_read_inotify_event_IN_CLOSE_WRITE);
 
7594
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
7595
 
              test_read_inotify_event_IN_MOVED_TO);
 
7596
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
7597
 
              test_read_inotify_event_IN_DELETE);
 
7598
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
7599
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
7600
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
7601
 
              test_read_inotify_event_IN_MOVED_TO_badname);
 
7602
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
7603
 
              test_read_inotify_event_IN_DELETE_badname);
 
7604
 
  test_add_st("/task/open_and_parse_question/ENOENT",
 
7605
 
              test_open_and_parse_question_ENOENT);
 
7606
 
  test_add_st("/task/open_and_parse_question/EIO",
 
7607
 
              test_open_and_parse_question_EIO);
 
7608
 
  test_add_st("/task/open_and_parse_question/parse-error",
 
7609
 
              test_open_and_parse_question_parse_error);
 
7610
 
  test_add_st("/task/open_and_parse_question/nosocket",
 
7611
 
              test_open_and_parse_question_nosocket);
 
7612
 
  test_add_st("/task/open_and_parse_question/badsocket",
 
7613
 
              test_open_and_parse_question_badsocket);
 
7614
 
  test_add_st("/task/open_and_parse_question/nopid",
 
7615
 
              test_open_and_parse_question_nopid);
 
7616
 
  test_add_st("/task/open_and_parse_question/badpid",
 
7617
 
              test_open_and_parse_question_badpid);
 
7618
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
7619
 
              test_open_and_parse_question_noexist_pid);
 
7620
 
  test_add_st("/task/open_and_parse_question/no-notafter",
 
7621
 
              test_open_and_parse_question_no_notafter);
 
7622
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
7623
 
              test_open_and_parse_question_bad_notafter);
 
7624
 
  test_add_st("/task/open_and_parse_question/notafter-0",
 
7625
 
              test_open_and_parse_question_notafter_0);
 
7626
 
  test_add_st("/task/open_and_parse_question/notafter-1",
 
7627
 
              test_open_and_parse_question_notafter_1);
 
7628
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
7629
 
              test_open_and_parse_question_notafter_1_1);
 
7630
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
7631
 
              test_open_and_parse_question_notafter_1_2);
 
7632
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
7633
 
              test_open_and_parse_question_equal_notafter);
 
7634
 
  test_add_st("/task/open_and_parse_question/late-notafter",
 
7635
 
              test_open_and_parse_question_late_notafter);
 
7636
 
  test_add_st("/task/cancel_old_question/0-1-2",
 
7637
 
              test_cancel_old_question_0_1_2);
 
7638
 
  test_add_st("/task/cancel_old_question/0-2-1",
 
7639
 
              test_cancel_old_question_0_2_1);
 
7640
 
  test_add_st("/task/cancel_old_question/1-2-3",
 
7641
 
              test_cancel_old_question_1_2_3);
 
7642
 
  test_add_st("/task/cancel_old_question/1-3-2",
 
7643
 
              test_cancel_old_question_1_3_2);
 
7644
 
  test_add_st("/task/cancel_old_question/2-1-3",
 
7645
 
              test_cancel_old_question_2_1_3);
 
7646
 
  test_add_st("/task/cancel_old_question/2-3-1",
 
7647
 
              test_cancel_old_question_2_3_1);
 
7648
 
  test_add_st("/task/cancel_old_question/3-1-2",
 
7649
 
              test_cancel_old_question_3_1_2);
 
7650
 
  test_add_st("/task/cancel_old_question/3-2-1",
 
7651
 
              test_cancel_old_question_3_2_1);
 
7652
 
  test_add_st("/task/connect_question_socket/name-too-long",
 
7653
 
              test_connect_question_socket_name_too_long);
 
7654
 
  test_add_st("/task/connect_question_socket/connect-fail",
 
7655
 
              test_connect_question_socket_connect_fail);
 
7656
 
  test_add_st("/task/connect_question_socket/bad-epoll",
 
7657
 
              test_connect_question_socket_bad_epoll);
 
7658
 
  test_add_st("/task/connect_question_socket/usable",
 
7659
 
              test_connect_question_socket_usable);
 
7660
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
7661
 
              test_send_password_to_socket_client_not_exited);
 
7662
 
  test_add_st("/task/send_password_to_socket/password-not-read",
 
7663
 
              test_send_password_to_socket_password_not_read);
 
7664
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
7665
 
              test_send_password_to_socket_EMSGSIZE);
 
7666
 
  test_add_st("/task/send_password_to_socket/retry",
 
7667
 
              test_send_password_to_socket_retry);
 
7668
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
7669
 
              test_send_password_to_socket_bad_epoll);
 
7670
 
  test_add_st("/task/send_password_to_socket/null-password",
 
7671
 
              test_send_password_to_socket_null_password);
 
7672
 
  test_add_st("/task/send_password_to_socket/empty-password",
 
7673
 
              test_send_password_to_socket_empty_password);
 
7674
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
7675
 
              test_send_password_to_socket_empty_str_pass);
 
7676
 
  test_add_st("/task/send_password_to_socket/text-password",
 
7677
 
              test_send_password_to_socket_text_password);
 
7678
 
  test_add_st("/task/send_password_to_socket/binary-password",
 
7679
 
              test_send_password_to_socket_binary_password);
 
7680
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
7681
 
              test_send_password_to_socket_nuls_in_password);
 
7682
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
7683
 
              test_add_existing_questions_ENOENT);
 
7684
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
7685
 
              test_add_existing_questions_no_questions);
 
7686
 
  test_add_st("/task-creators/add_existing_questions/one-question",
 
7687
 
              test_add_existing_questions_one_question);
 
7688
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
7689
 
              test_add_existing_questions_two_questions);
 
7690
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
7691
 
              test_add_existing_questions_non_questions);
 
7692
 
  test_add_st("/task-creators/add_existing_questions/both-types",
 
7693
 
              test_add_existing_questions_both_types);
 
7695
 
  return g_test_run() == 0;
 
7698
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
7699
 
  GOptionContext *context = g_option_context_new("");
 
7701
 
  g_option_context_set_help_enabled(context, FALSE);
 
7702
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
7704
 
  gboolean run_tests = FALSE;
 
7705
 
  GOptionEntry entries[] = {
 
7706
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
7707
 
      &run_tests, "Run tests", NULL },
 
7710
 
  g_option_context_add_main_entries(context, entries, NULL);
 
7712
 
  GError *error = NULL;
 
7714
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
7715
 
    g_option_context_free(context);
 
7716
 
    g_error("Failed to parse options: %s", error->message);
 
7719
 
  g_option_context_free(context);
 
7720
 
  return run_tests != FALSE;