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