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
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
 
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 <stdint.h>             /* SIZE_MAX */
 
 
77
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
 
78
                                   fork(), _exit(), dup2(),
 
 
79
                                   STDOUT_FILENO, setresgid(),
 
 
80
                                   setresuid(), execv(), ssize_t,
 
 
81
                                   read(), dup3(), getuid(), dup(),
 
 
82
                                   STDERR_FILENO, pause(), write(),
 
 
83
                                   rmdir(), unlink(), getpid() */
 
 
84
#include <sys/mman.h>           /* munlock(), mlock() */
 
 
85
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
 
86
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
 
87
                                   open(), O_WRONLY, O_NOCTTY,
 
 
88
                                   O_RDONLY, O_NOFOLLOW */
 
 
89
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
 
91
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
 
92
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
 
93
                                   IN_CLOEXEC, inotify_add_watch(),
 
 
94
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
 
95
                                   IN_MOVED_FROM, IN_DELETE,
 
 
96
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
 
97
                                   struct inotify_event */
 
 
98
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
 
99
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
 
100
                                   fclose(), getline(), sscanf(),
 
 
101
                                   feof(), ferror(), rename(),
 
 
102
                                   fdopen(), fprintf(), fscanf() */
 
 
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
/* "task_func" - 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
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
 
656
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
 
657
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
 
661
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
 
662
  if(needed_size > (queue->allocated)){
 
 
663
    task_context *const new_tasks = realloc(queue->tasks,
 
 
665
    if(new_tasks == NULL){
 
 
666
      error(0, errno, "Failed to allocate %" PRIuMAX
 
 
667
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
 
670
    queue->tasks = new_tasks;
 
 
671
    queue->allocated = needed_size;
 
 
673
  /* Using memcpy here is necessary because doing */
 
 
674
  /* queue->tasks[queue->length++] = task; */
 
 
675
  /* would violate const-ness of task members */
 
 
676
  memcpy(&(queue->tasks[queue->length++]), &task,
 
 
677
         sizeof(task_context));
 
 
681
__attribute__((nonnull))
 
 
682
void cleanup_task(const task_context *const task){
 
 
683
  const error_t saved_errno = errno;
 
 
684
  /* free and close all task data */
 
 
685
  free(task->question_filename);
 
 
686
  if(task->filename != task->question_filename){
 
 
687
    free(task->filename);
 
 
690
    kill(task->pid, SIGTERM);
 
 
698
__attribute__((nonnull))
 
 
699
void free_queue(task_queue *const queue){
 
 
704
__attribute__((nonnull))
 
 
705
void cleanup_queue(task_queue *const *const queue){
 
 
709
  for(size_t i = 0; i < (*queue)->length; i++){
 
 
710
    const task_context *const task = ((*queue)->tasks)+i;
 
 
716
__attribute__((pure, nonnull, warn_unused_result))
 
 
717
bool queue_has_question(const task_queue *const queue){
 
 
718
  for(size_t i=0; i < queue->length; i++){
 
 
719
    if(queue->tasks[i].question_filename != NULL){
 
 
726
__attribute__((nonnull))
 
 
727
void cleanup_close(const int *const fd){
 
 
728
  const error_t saved_errno = errno;
 
 
733
__attribute__((nonnull))
 
 
734
void cleanup_string(char *const *const ptr){
 
 
738
__attribute__((nonnull))
 
 
739
void cleanup_buffer(buffer *buf){
 
 
740
  if(buf->allocated > 0){
 
 
741
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
742
    explicit_bzero(buf->data, buf->allocated);
 
 
744
    memset(buf->data, '\0', buf->allocated);
 
 
747
  if(buf->data != NULL){
 
 
748
    if(munlock(buf->data, buf->allocated) != 0){
 
 
749
      error(0, errno, "Failed to unlock memory of old buffer");
 
 
758
__attribute__((pure, nonnull, warn_unused_result))
 
 
759
bool string_set_contains(const string_set set, const char *const str){
 
 
760
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
 
761
      s = argz_next(set.argz, set.argz_len, s)){
 
 
762
    if(strcmp(s, str) == 0){
 
 
769
__attribute__((nonnull, warn_unused_result))
 
 
770
bool string_set_add(string_set *const set, const char *const str){
 
 
771
  if(string_set_contains(*set, str)){
 
 
774
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
 
782
__attribute__((nonnull))
 
 
783
void string_set_clear(string_set *set){
 
 
789
__attribute__((nonnull))
 
 
790
void string_set_swap(string_set *const set1, string_set *const set2){
 
 
791
  /* Swap contents of two string sets */
 
 
793
    char *const tmp_argz = set1->argz;
 
 
794
    set1->argz = set2->argz;
 
 
795
    set2->argz = tmp_argz;
 
 
798
    const size_t tmp_argz_len = set1->argz_len;
 
 
799
    set1->argz_len = set2->argz_len;
 
 
800
    set2->argz_len = tmp_argz_len;
 
 
804
__attribute__((nonnull, warn_unused_result))
 
 
805
bool start_mandos_client(task_queue *const queue,
 
 
807
                         bool *const mandos_client_exited,
 
 
808
                         bool *const quit_now, buffer *const password,
 
 
809
                         bool *const password_is_read,
 
 
810
                         const struct sigaction *const
 
 
811
                         old_sigchld_action, const sigset_t sigmask,
 
 
812
                         const char *const helper_directory,
 
 
813
                         const uid_t user, const gid_t group,
 
 
814
                         const char *const *const argv){
 
 
816
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
 
817
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
 
821
  const pid_t pid = fork();
 
 
823
    if(not restore_signal_handler(old_sigchld_action)){
 
 
826
    if(not restore_sigmask(&sigmask)){
 
 
829
    if(close(pipefds[0]) != 0){
 
 
830
      error(0, errno, "Failed to close() parent pipe fd");
 
 
833
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
 
834
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
 
837
    if(close(pipefds[1]) != 0){
 
 
838
      error(0, errno, "Failed to close() old child pipe fd");
 
 
841
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
 
842
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
 
843
            " \"%s\", 1)", helper_directory);
 
 
846
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
 
847
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
 
848
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
851
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
 
852
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
 
853
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
857
#pragma GCC diagnostic push
 
 
858
    /* For historical reasons, the "argv" argument to execv() is not
 
 
859
       const, but it is safe to override this. */
 
 
860
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
862
    execv(argv[0], (char **)argv);
 
 
864
#pragma GCC diagnostic pop
 
 
866
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
 
872
    error(0, errno, "Failed to fork()");
 
 
877
  if(not add_to_queue(queue, (task_context){
 
 
878
        .func=wait_for_mandos_client_exit,
 
 
880
        .mandos_client_exited=mandos_client_exited,
 
 
883
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
 
888
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
 
889
                            &(struct epoll_event)
 
 
890
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
891
  if(ret != 0 and errno != EEXIST){
 
 
892
    error(0, errno, "Failed to add file descriptor to epoll set");
 
 
897
  return add_to_queue(queue, (task_context){
 
 
898
      .func=read_mandos_client_output,
 
 
903
      .password_is_read=password_is_read,
 
 
907
__attribute__((nonnull))
 
 
908
void wait_for_mandos_client_exit(const task_context task,
 
 
909
                                 task_queue *const queue){
 
 
910
  const pid_t pid = task.pid;
 
 
911
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
912
  bool *const quit_now = task.quit_now;
 
 
915
  switch(waitpid(pid, &status, WNOHANG)){
 
 
916
  case 0:                       /* Not exited yet */
 
 
917
    if(not add_to_queue(queue, task)){
 
 
918
      error(0, errno, "Failed to add myself to queue");
 
 
923
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
 
929
  default:                      /* Has exited */
 
 
930
    *mandos_client_exited = true;
 
 
931
    if((not WIFEXITED(status))
 
 
932
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
 
933
      error(0, 0, "Mandos client failed or was killed");
 
 
939
__attribute__((nonnull))
 
 
940
void read_mandos_client_output(const task_context task,
 
 
941
                               task_queue *const queue){
 
 
942
  buffer *const password = task.password;
 
 
943
  bool *const quit_now = task.quit_now;
 
 
944
  bool *const password_is_read = task.password_is_read;
 
 
945
  const int fd = task.fd;
 
 
946
  const int epoll_fd = task.epoll_fd;
 
 
948
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
 
949
  if(password->allocated < new_potential_size){
 
 
950
    char *const new_buffer = calloc(new_potential_size, 1);
 
 
951
    if(new_buffer == NULL){
 
 
952
      error(0, errno, "Failed to allocate %" PRIuMAX
 
 
953
            " bytes for password", (uintmax_t)new_potential_size);
 
 
958
    if(mlock(new_buffer, new_potential_size) != 0){
 
 
959
      /* Warn but do not treat as fatal error */
 
 
960
      if(errno != EPERM and errno != ENOMEM){
 
 
961
        error(0, errno, "Failed to lock memory for password");
 
 
964
    if(password->length > 0){
 
 
965
      memcpy(new_buffer, password->data, password->length);
 
 
966
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
967
      explicit_bzero(password->data, password->allocated);
 
 
969
      memset(password->data, '\0', password->allocated);
 
 
972
    if(password->data != NULL){
 
 
973
      if(munlock(password->data, password->allocated) != 0){
 
 
974
        error(0, errno, "Failed to unlock memory of old buffer");
 
 
976
      free(password->data);
 
 
978
    password->data = new_buffer;
 
 
979
    password->allocated = new_potential_size;
 
 
982
  const ssize_t read_length = read(fd, password->data
 
 
983
                                   + password->length, PIPE_BUF);
 
 
985
  if(read_length == 0){ /* EOF */
 
 
986
    *password_is_read = true;
 
 
990
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
 
991
    error(0, errno, "Failed to read password from Mandos client");
 
 
996
  if(read_length > 0){          /* Data has been read */
 
 
997
    password->length += (size_t)read_length;
 
 
1000
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
 
1003
  /* Re-add the fd to the epoll set */
 
 
1004
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1005
                            &(struct epoll_event)
 
 
1006
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1007
  if(ret != 0 and errno != EEXIST){
 
 
1008
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
 
1014
  /* Re-add myself to the queue */
 
 
1015
  if(not add_to_queue(queue, task)){
 
 
1016
    error(0, errno, "Failed to add myself to queue");
 
 
1022
__attribute__((nonnull, warn_unused_result))
 
 
1023
bool add_inotify_dir_watch(task_queue *const queue,
 
 
1024
                           const int epoll_fd, bool *const quit_now,
 
 
1025
                           buffer *const password,
 
 
1026
                           const char *const dir,
 
 
1027
                           string_set *cancelled_filenames,
 
 
1028
                           const mono_microsecs *const current_time,
 
 
1029
                           bool *const mandos_client_exited,
 
 
1030
                           bool *const password_is_read){
 
 
1031
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
 
1033
    error(0, errno, "Failed to create inotify instance");
 
 
1037
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
 
1038
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
 
 
1041
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
 
1045
  /* Add the inotify fd to the epoll set */
 
 
1046
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1047
                            &(struct epoll_event)
 
 
1048
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1049
  if(ret != 0 and errno != EEXIST){
 
 
1050
    error(0, errno, "Failed to add file descriptor to epoll set");
 
 
1055
  const task_context read_inotify_event_task = {
 
 
1056
    .func=read_inotify_event,
 
 
1061
    .filename=strdup(dir),
 
 
1062
    .cancelled_filenames=cancelled_filenames,
 
 
1063
    .current_time=current_time,
 
 
1064
    .mandos_client_exited=mandos_client_exited,
 
 
1065
    .password_is_read=password_is_read,
 
 
1067
  if(read_inotify_event_task.filename == NULL){
 
 
1068
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
 
1073
  return add_to_queue(queue, read_inotify_event_task);
 
 
1076
__attribute__((nonnull))
 
 
1077
void read_inotify_event(const task_context task,
 
 
1078
                        task_queue *const queue){
 
 
1079
  const int fd = task.fd;
 
 
1080
  const int epoll_fd = task.epoll_fd;
 
 
1081
  char *const filename = task.filename;
 
 
1082
  bool *quit_now = task.quit_now;
 
 
1083
  buffer *const password = task.password;
 
 
1084
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1085
  const mono_microsecs *const current_time = task.current_time;
 
 
1086
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1087
  bool *const password_is_read = task.password_is_read;
 
 
1089
  /* "sufficient to read at least one event." - inotify(7) */
 
 
1090
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
1093
    struct inotify_event event;
 
 
1094
    char name_buffer[NAME_MAX + 1];
 
 
1096
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
1098
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
 
1099
  if(read_length == 0){ /* EOF */
 
 
1100
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
 
1102
    cleanup_task(&task);
 
 
1105
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
 
1106
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
 
1109
    cleanup_task(&task);
 
 
1112
  if(read_length > 0            /* Data has been read */
 
 
1113
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
 
1114
    char *question_filename = NULL;
 
 
1115
    const ssize_t question_filename_length
 
 
1116
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
 
1117
    if(question_filename_length < 0){
 
 
1118
      error(0, errno, "Failed to create file name from directory name"
 
 
1119
            " %s and file name %s", filename, ievent->name);
 
 
1121
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
 
1122
        if(not add_to_queue(queue, (task_context){
 
 
1123
              .func=open_and_parse_question,
 
 
1125
              .question_filename=question_filename,
 
 
1126
              .filename=question_filename,
 
 
1128
              .cancelled_filenames=cancelled_filenames,
 
 
1129
              .current_time=current_time,
 
 
1130
              .mandos_client_exited=mandos_client_exited,
 
 
1131
              .password_is_read=password_is_read,
 
 
1133
          error(0, errno, "Failed to add open_and_parse_question task"
 
 
1134
                " for file name %s to queue", filename);
 
 
1136
          /* Force the added task (open_and_parse_question) to run
 
 
1138
          queue->next_run = 1;
 
 
1140
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
 
1141
        if(not string_set_add(cancelled_filenames,
 
 
1142
                              question_filename)){
 
 
1143
          error(0, errno, "Could not add question %s to"
 
 
1144
                " cancelled_questions", question_filename);
 
 
1146
          free(question_filename);
 
 
1147
          cleanup_task(&task);
 
 
1150
        free(question_filename);
 
 
1155
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
 
1158
  /* Re-add myself to the queue */
 
 
1159
  if(not add_to_queue(queue, task)){
 
 
1160
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
 
1161
          " queue", filename);
 
 
1163
    cleanup_task(&task);
 
 
1167
  /* Re-add the fd to the epoll set */
 
 
1168
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1169
                            &(struct epoll_event)
 
 
1170
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1171
  if(ret != 0 and errno != EEXIST){
 
 
1172
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
 
1173
          " directory %s to epoll set", fd, filename);
 
 
1174
    /* Force the added task (read_inotify_event) to run again, at most
 
 
1175
       one second from now */
 
 
1176
    if((queue->next_run == 0)
 
 
1177
       or (queue->next_run > (*current_time + 1000000))){
 
 
1178
      queue->next_run = *current_time + 1000000;
 
 
1183
__attribute__((nonnull))
 
 
1184
void open_and_parse_question(const task_context task,
 
 
1185
                             task_queue *const queue){
 
 
1186
  __attribute__((cleanup(cleanup_string)))
 
 
1187
    char *question_filename = task.question_filename;
 
 
1188
  const int epoll_fd = task.epoll_fd;
 
 
1189
  buffer *const password = task.password;
 
 
1190
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1191
  const mono_microsecs *const current_time = task.current_time;
 
 
1192
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1193
  bool *const password_is_read = task.password_is_read;
 
 
1195
  /* We use the GLib "Key-value file parser" functions to parse the
 
 
1196
     question file.  See <https://www.freedesktop.org/wiki/Software
 
 
1197
     /systemd/PasswordAgents/> for specification of contents */
 
 
1198
  __attribute__((nonnull))
 
 
1199
    void cleanup_g_key_file(GKeyFile **key_file){
 
 
1200
    if(*key_file != NULL){
 
 
1201
      g_key_file_free(*key_file);
 
 
1205
  __attribute__((cleanup(cleanup_g_key_file)))
 
 
1206
    GKeyFile *key_file = g_key_file_new();
 
 
1207
  if(key_file == NULL){
 
 
1208
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
 
1212
  GError *glib_error = NULL;
 
 
1213
  if(g_key_file_load_from_file(key_file, question_filename,
 
 
1214
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
 
1215
    /* If a file was removed, we should ignore it, so */
 
 
1216
    /* only show error message if file actually existed */
 
 
1217
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
 
1218
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
 
1219
            question_filename, glib_error->message);
 
 
1224
  __attribute__((cleanup(cleanup_string)))
 
 
1225
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
 
1228
  if(socket_name == NULL){
 
 
1229
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
 
1230
          question_filename, glib_error->message);
 
 
1234
  if(strlen(socket_name) == 0){
 
 
1235
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
 
1240
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
 
1242
  if(glib_error != NULL){
 
 
1243
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
 
1244
          question_filename, glib_error->message);
 
 
1248
  if((pid != (guint64)((pid_t)pid))
 
 
1249
     or (kill((pid_t)pid, 0) != 0)){
 
 
1250
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
 
1251
          " does not exist", (uintmax_t)pid, question_filename);
 
 
1255
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
 
1256
                                           "NotAfter", &glib_error);
 
 
1257
  if(glib_error != NULL){
 
 
1258
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
 
1259
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
 
1260
            " %s", question_filename, glib_error->message);
 
 
1265
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
 
1266
      queue->next_run = notafter;
 
 
1268
    if(*current_time >= notafter){
 
 
1273
  const task_context connect_question_socket_task = {
 
 
1274
    .func=connect_question_socket,
 
 
1275
    .question_filename=strdup(question_filename),
 
 
1278
    .filename=strdup(socket_name),
 
 
1279
    .cancelled_filenames=task.cancelled_filenames,
 
 
1280
    .mandos_client_exited=mandos_client_exited,
 
 
1281
    .password_is_read=password_is_read,
 
 
1282
    .current_time=current_time,
 
 
1284
  if(connect_question_socket_task.question_filename == NULL
 
 
1285
     or connect_question_socket_task.filename == NULL
 
 
1286
     or not add_to_queue(queue, connect_question_socket_task)){
 
 
1287
    error(0, errno, "Failed to add connect_question_socket for socket"
 
 
1288
          " %s (from \"%s\") to queue", socket_name,
 
 
1290
    cleanup_task(&connect_question_socket_task);
 
 
1293
  /* Force the added task (connect_question_socket) to run
 
 
1295
  queue->next_run = 1;
 
 
1298
    char *const dup_filename = strdup(question_filename);
 
 
1299
    const task_context cancel_old_question_task = {
 
 
1300
      .func=cancel_old_question,
 
 
1301
      .question_filename=dup_filename,
 
 
1303
      .filename=dup_filename,
 
 
1304
      .cancelled_filenames=cancelled_filenames,
 
 
1305
      .current_time=current_time,
 
 
1307
    if(cancel_old_question_task.question_filename == NULL
 
 
1308
       or not add_to_queue(queue, cancel_old_question_task)){
 
 
1309
      error(0, errno, "Failed to add cancel_old_question for file "
 
 
1310
            "\"%s\" to queue", question_filename);
 
 
1311
      cleanup_task(&cancel_old_question_task);
 
 
1317
__attribute__((nonnull))
 
 
1318
void cancel_old_question(const task_context task,
 
 
1319
                         task_queue *const queue){
 
 
1320
  char *const question_filename = task.question_filename;
 
 
1321
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1322
  const mono_microsecs notafter = task.notafter;
 
 
1323
  const mono_microsecs *const current_time = task.current_time;
 
 
1325
  if(*current_time >= notafter){
 
 
1326
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1327
      error(0, errno, "Failed to cancel question for file %s",
 
 
1330
    cleanup_task(&task);
 
 
1334
  if(not add_to_queue(queue, task)){
 
 
1335
    error(0, errno, "Failed to add cancel_old_question for file "
 
 
1336
          "%s to queue", question_filename);
 
 
1337
    cleanup_task(&task);
 
 
1341
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
 
1342
    queue->next_run = notafter;
 
 
1346
__attribute__((nonnull))
 
 
1347
void connect_question_socket(const task_context task,
 
 
1348
                             task_queue *const queue){
 
 
1349
  char *const question_filename = task.question_filename;
 
 
1350
  char *const filename = task.filename;
 
 
1351
  const int epoll_fd = task.epoll_fd;
 
 
1352
  buffer *const password = task.password;
 
 
1353
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1354
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1355
  bool *const password_is_read = task.password_is_read;
 
 
1356
  const mono_microsecs *const current_time = task.current_time;
 
 
1358
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
1360
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
 
1361
    error(0, 0, "Socket filename is larger than"
 
 
1362
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
 
1363
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
 
1364
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1365
      error(0, errno, "Failed to cancel question for file %s",
 
 
1368
    cleanup_task(&task);
 
 
1372
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
1373
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
1376
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
 
1377
    if(not add_to_queue(queue, task)){
 
 
1378
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1379
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1381
      cleanup_task(&task);
 
 
1383
      /* Force the added task (connect_question_socket) to run
 
 
1385
      queue->next_run = 1;
 
 
1390
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
1391
  if(connect(fd, (struct sockaddr *)&sock_name,
 
 
1392
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
 
1393
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
 
1394
    if(not add_to_queue(queue, task)){
 
 
1395
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1396
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1398
      cleanup_task(&task);
 
 
1400
      /* Force the added task (connect_question_socket) to run again,
 
 
1401
         at most one second from now */
 
 
1402
      if((queue->next_run == 0)
 
 
1403
         or (queue->next_run > (*current_time + 1000000))){
 
 
1404
        queue->next_run = *current_time + 1000000;
 
 
1410
  /* Not necessary, but we can try, and merely warn on failure */
 
 
1411
  if(shutdown(fd, SHUT_RD) != 0){
 
 
1412
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
 
1416
  /* Add the fd to the epoll set */
 
 
1417
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1418
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1420
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
 
1421
          " socket %s to epoll set", fd, filename);
 
 
1422
    if(not add_to_queue(queue, task)){
 
 
1423
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1424
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1426
      cleanup_task(&task);
 
 
1428
      /* Force the added task (connect_question_socket) to run again,
 
 
1429
         at most one second from now */
 
 
1430
      if((queue->next_run == 0)
 
 
1431
         or (queue->next_run > (*current_time + 1000000))){
 
 
1432
        queue->next_run = *current_time + 1000000;
 
 
1438
  /* add task send_password_to_socket to queue */
 
 
1439
  const task_context send_password_to_socket_task = {
 
 
1440
    .func=send_password_to_socket,
 
 
1441
    .question_filename=question_filename,
 
 
1446
    .cancelled_filenames=cancelled_filenames,
 
 
1447
    .mandos_client_exited=mandos_client_exited,
 
 
1448
    .password_is_read=password_is_read,
 
 
1449
    .current_time=current_time,
 
 
1452
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
 
1453
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1454
          " file \"%s\" and socket \"%s\" to queue",
 
 
1455
          question_filename, filename);
 
 
1456
    cleanup_task(&send_password_to_socket_task);
 
 
1460
__attribute__((nonnull))
 
 
1461
void send_password_to_socket(const task_context task,
 
 
1462
                             task_queue *const queue){
 
 
1463
  char *const question_filename=task.question_filename;
 
 
1464
  char *const filename=task.filename;
 
 
1465
  const int epoll_fd=task.epoll_fd;
 
 
1466
  const int fd=task.fd;
 
 
1467
  buffer *const password=task.password;
 
 
1468
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
 
1469
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1470
  bool *const password_is_read = task.password_is_read;
 
 
1471
  const mono_microsecs *const current_time = task.current_time;
 
 
1473
  if(*mandos_client_exited and *password_is_read){
 
 
1475
    const size_t send_buffer_length = password->length + 2;
 
 
1476
    char *send_buffer = malloc(send_buffer_length);
 
 
1477
    if(send_buffer == NULL){
 
 
1478
      error(0, errno, "Failed to allocate send_buffer");
 
 
1480
      if(mlock(send_buffer, send_buffer_length) != 0){
 
 
1481
        /* Warn but do not treat as fatal error */
 
 
1482
        if(errno != EPERM and errno != ENOMEM){
 
 
1483
          error(0, errno, "Failed to lock memory for password"
 
 
1487
      /* “[…] send a single datagram to the socket consisting of the
 
 
1488
         password string either prefixed with "+" or with "-"
 
 
1489
         depending on whether the password entry was successful or
 
 
1490
         not. You may but don't have to include a final NUL byte in
 
 
1493
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
 
1494
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
 
1496
      send_buffer[0] = '+';     /* Prefix with "+" */
 
 
1497
      /* Always add an extra NUL */
 
 
1498
      send_buffer[password->length + 1] = '\0';
 
 
1499
      if(password->length > 0){
 
 
1500
        memcpy(send_buffer + 1, password->data, password->length);
 
 
1503
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
 
1505
      const error_t saved_errno = errno;
 
 
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
1507
      explicit_bzero(send_buffer, send_buffer_length);
 
 
1509
      memset(send_buffer, '\0', send_buffer_length);
 
 
1511
      if(munlock(send_buffer, send_buffer_length) != 0){
 
 
1512
        error(0, errno, "Failed to unlock memory of send buffer");
 
 
1515
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
 
1516
        switch(saved_errno){
 
 
1529
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
 
1530
                (uintmax_t)password->length);
 
 
1534
          __attribute__((fallthrough));
 
 
1537
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
 
1538
            error(0, 0, "Password only partially sent to socket");
 
 
1543
          __attribute__((fallthrough));
 
 
1546
          error(0, saved_errno, "Failed to send() to socket %s",
 
 
1548
          if(not string_set_add(cancelled_filenames,
 
 
1549
                                question_filename)){
 
 
1550
            error(0, errno, "Failed to cancel question for file %s",
 
 
1553
          cleanup_task(&task);
 
 
1558
        cleanup_task(&task);
 
 
1564
  /* We failed or are not ready yet; retry later */
 
 
1566
  if(not add_to_queue(queue, task)){
 
 
1567
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1568
          " file %s and socket %s to queue", question_filename,
 
 
1570
    cleanup_task(&task);
 
 
1573
  /* Add the fd to the epoll set */
 
 
1574
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1575
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1577
    error(0, errno, "Failed to add socket file descriptor %d for"
 
 
1578
          " socket %s to epoll set", fd, filename);
 
 
1579
    /* Force the added task (send_password_to_socket) to run again, at
 
 
1580
       most one second from now */
 
 
1581
    if((queue->next_run == 0)
 
 
1582
       or (queue->next_run > (*current_time + 1000000))){
 
 
1583
      queue->next_run = *current_time + 1000000;
 
 
1588
__attribute__((warn_unused_result))
 
 
1589
bool add_existing_questions(task_queue *const queue,
 
 
1591
                            buffer *const password,
 
 
1592
                            string_set *cancelled_filenames,
 
 
1593
                            const mono_microsecs *const current_time,
 
 
1594
                            bool *const mandos_client_exited,
 
 
1595
                            bool *const password_is_read,
 
 
1596
                            const char *const dirname){
 
 
1597
  __attribute__((cleanup(cleanup_string)))
 
 
1598
    char *dir_pattern = NULL;
 
 
1599
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
 
1600
  if(ret < 0 or dir_pattern == NULL){
 
 
1601
    error(0, errno, "Could not create glob pattern for directory %s",
 
 
1605
  __attribute__((cleanup(globfree)))
 
 
1606
    glob_t question_filenames = {};
 
 
1607
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
 
1608
              NULL, &question_filenames)){
 
 
1611
    error(0, errno, "Failed to open directory %s", dirname);
 
 
1614
    error(0, errno, "There are no question files in %s", dirname);
 
 
1617
    error(0, errno, "Could not allocate memory for question file"
 
 
1618
          " names in %s", dirname);
 
 
1622
    __attribute__((fallthrough));
 
 
1625
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
 
1626
      char *const question_filename = strdup(question_filenames
 
 
1628
      const task_context task = {
 
 
1629
        .func=open_and_parse_question,
 
 
1631
        .question_filename=question_filename,
 
 
1632
        .filename=question_filename,
 
 
1634
        .cancelled_filenames=cancelled_filenames,
 
 
1635
        .current_time=current_time,
 
 
1636
        .mandos_client_exited=mandos_client_exited,
 
 
1637
        .password_is_read=password_is_read,
 
 
1640
      if(question_filename == NULL
 
 
1641
         or not add_to_queue(queue, task)){
 
 
1642
        error(0, errno, "Failed to add open_and_parse_question for"
 
 
1643
              " file %s to queue",
 
 
1644
              question_filenames.gl_pathv[i]);
 
 
1645
        free(question_filename);
 
 
1647
        queue->next_run = 1;
 
 
1654
__attribute__((nonnull, warn_unused_result))
 
 
1655
bool wait_for_event(const int epoll_fd,
 
 
1656
                    const mono_microsecs queue_next_run,
 
 
1657
                    const mono_microsecs current_time){
 
 
1658
  __attribute__((const))
 
 
1659
    int milliseconds_to_wait(const mono_microsecs currtime,
 
 
1660
                             const mono_microsecs nextrun){
 
 
1661
    if(currtime >= nextrun){
 
 
1664
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
 
1665
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
 
1668
    return (int)wait_time_ms;
 
 
1671
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
 
1674
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
 
1675
  sigset_t temporary_unblocked_sigmask;
 
 
1676
  /* Get current signal mask */
 
 
1677
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
 
1680
  /* Remove SIGCHLD from the signal mask */
 
 
1681
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
 
1684
  struct epoll_event events[8]; /* Ignored */
 
 
1685
  int ret = epoll_pwait(epoll_fd, events,
 
 
1686
                        sizeof(events) / sizeof(struct epoll_event),
 
 
1687
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
 
1688
                        &temporary_unblocked_sigmask);
 
 
1689
  if(ret < 0 and errno != EINTR){
 
 
1690
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
 
1692
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
 
1695
  return clear_all_fds_from_epoll_set(epoll_fd);
 
 
1698
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
 
1699
  /* Create a new empty epoll set */
 
 
1700
  __attribute__((cleanup(cleanup_close)))
 
 
1701
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
1702
  if(new_epoll_fd < 0){
 
 
1705
  /* dup3() the new epoll set fd over the old one, replacing it */
 
 
1706
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
 
1712
__attribute__((nonnull, warn_unused_result))
 
 
1713
bool run_queue(task_queue **const queue,
 
 
1714
               string_set *const cancelled_filenames,
 
 
1715
               bool *const quit_now){
 
 
1717
  task_queue *new_queue = create_queue();
 
 
1718
  if(new_queue == NULL){
 
 
1722
  __attribute__((cleanup(string_set_clear)))
 
 
1723
    string_set old_cancelled_filenames = {};
 
 
1724
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
 
1726
  /* Declare i outside the for loop, since we might need i after the
 
 
1727
     loop in case we aborted in the middle */
 
 
1729
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
 
1730
    task_context *const task = &((*queue)->tasks[i]);
 
 
1731
    const char *const question_filename = task->question_filename;
 
 
1732
    /* Skip any task referencing a cancelled question filename */
 
 
1733
    if(question_filename != NULL
 
 
1734
       and string_set_contains(old_cancelled_filenames,
 
 
1735
                               question_filename)){
 
 
1739
    task->func(*task, new_queue);
 
 
1743
    /* we might be in the middle of the queue, so clean up any
 
 
1744
       remaining tasks in the current queue */
 
 
1745
    for(; i < (*queue)->length; i++){
 
 
1746
      cleanup_task(&((*queue)->tasks[i]));
 
 
1760
/* End of regular code section */
 
 
1762
/* Start of tests section; here are the tests for the above code */
 
 
1764
/* This "fixture" data structure is used by the test setup and
 
 
1765
   teardown functions */
 
 
1767
  struct sigaction orig_sigaction;
 
 
1768
  sigset_t orig_sigmask;
 
 
1771
static void test_setup(test_fixture *fixture,
 
 
1772
                       __attribute__((unused))
 
 
1773
                       gconstpointer user_data){
 
 
1774
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
 
1775
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
 
1778
static void test_teardown(test_fixture *fixture,
 
 
1779
                          __attribute__((unused))
 
 
1780
                          gconstpointer user_data){
 
 
1781
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
 
1782
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
 
1785
/* Utility function used by tests to search queue for matching task */
 
 
1786
__attribute__((pure, nonnull, warn_unused_result))
 
 
1787
static task_context *find_matching_task(const task_queue *const queue,
 
 
1788
                                        const task_context task){
 
 
1789
  /* The argument "task" structure is a pattern to match; 0 in any
 
 
1790
     member means any value matches, otherwise the value must match.
 
 
1791
     The filename strings are compared by strcmp(), not by pointer. */
 
 
1792
  for(size_t i = 0; i < queue->length; i++){
 
 
1793
    task_context *const current_task = queue->tasks+i;
 
 
1794
    /* Check all members of task_context, if set to a non-zero value.
 
 
1795
       If a member does not match, continue to next task in queue */
 
 
1797
    /* task_func *const func */
 
 
1798
    if(task.func != NULL and current_task->func != task.func){
 
 
1801
    /* char *const question_filename; */
 
 
1802
    if(task.question_filename != NULL
 
 
1803
       and (current_task->question_filename == NULL
 
 
1804
            or strcmp(current_task->question_filename,
 
 
1805
                      task.question_filename) != 0)){
 
 
1808
    /* const pid_t pid; */
 
 
1809
    if(task.pid != 0 and current_task->pid != task.pid){
 
 
1812
    /* const int epoll_fd; */
 
 
1813
    if(task.epoll_fd != 0
 
 
1814
       and current_task->epoll_fd != task.epoll_fd){
 
 
1817
    /* bool *const quit_now; */
 
 
1818
    if(task.quit_now != NULL
 
 
1819
       and current_task->quit_now != task.quit_now){
 
 
1823
    if(task.fd != 0 and current_task->fd != task.fd){
 
 
1826
    /* bool *const mandos_client_exited; */
 
 
1827
    if(task.mandos_client_exited != NULL
 
 
1828
       and current_task->mandos_client_exited
 
 
1829
       != task.mandos_client_exited){
 
 
1832
    /* buffer *const password; */
 
 
1833
    if(task.password != NULL
 
 
1834
       and current_task->password != task.password){
 
 
1837
    /* bool *const password_is_read; */
 
 
1838
    if(task.password_is_read != NULL
 
 
1839
       and current_task->password_is_read != task.password_is_read){
 
 
1842
    /* char *filename; */
 
 
1843
    if(task.filename != NULL
 
 
1844
       and (current_task->filename == NULL
 
 
1845
            or strcmp(current_task->filename, task.filename) != 0)){
 
 
1848
    /* string_set *const cancelled_filenames; */
 
 
1849
    if(task.cancelled_filenames != NULL
 
 
1850
       and current_task->cancelled_filenames
 
 
1851
       != task.cancelled_filenames){
 
 
1854
    /* const mono_microsecs notafter; */
 
 
1855
    if(task.notafter != 0
 
 
1856
       and current_task->notafter != task.notafter){
 
 
1859
    /* const mono_microsecs *const current_time; */
 
 
1860
    if(task.current_time != NULL
 
 
1861
       and current_task->current_time != task.current_time){
 
 
1864
    /* Current task matches all members; return it */
 
 
1865
    return current_task;
 
 
1867
  /* No task in queue matches passed pattern task */
 
 
1871
static void test_create_queue(__attribute__((unused))
 
 
1872
                              test_fixture *fixture,
 
 
1873
                              __attribute__((unused))
 
 
1874
                              gconstpointer user_data){
 
 
1875
  __attribute__((cleanup(cleanup_queue)))
 
 
1876
    task_queue *const queue = create_queue();
 
 
1877
  g_assert_nonnull(queue);
 
 
1878
  g_assert_null(queue->tasks);
 
 
1879
  g_assert_true(queue->length == 0);
 
 
1880
  g_assert_true(queue->next_run == 0);
 
 
1883
static task_func dummy_func;
 
 
1885
static void test_add_to_queue(__attribute__((unused))
 
 
1886
                              test_fixture *fixture,
 
 
1887
                              __attribute__((unused))
 
 
1888
                              gconstpointer user_data){
 
 
1889
  __attribute__((cleanup(cleanup_queue)))
 
 
1890
    task_queue *queue = create_queue();
 
 
1891
  g_assert_nonnull(queue);
 
 
1893
  g_assert_true(add_to_queue(queue,
 
 
1894
                             (task_context){ .func=dummy_func }));
 
 
1895
  g_assert_true(queue->length == 1);
 
 
1896
  g_assert_nonnull(queue->tasks);
 
 
1897
  g_assert_true(queue->tasks[0].func == dummy_func);
 
 
1900
static void test_add_to_queue_overflow(__attribute__((unused))
 
 
1901
                                       test_fixture *fixture,
 
 
1902
                                       __attribute__((unused))
 
 
1903
                                       gconstpointer user_data){
 
 
1904
  __attribute__((cleanup(cleanup_queue)))
 
 
1905
    task_queue *queue = create_queue();
 
 
1906
  g_assert_nonnull(queue);
 
 
1907
  g_assert_true(queue->length == 0);
 
 
1908
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
 
1910
  FILE *real_stderr = stderr;
 
 
1911
  FILE *devnull = fopen("/dev/null", "we");
 
 
1912
  g_assert_nonnull(devnull);
 
 
1914
  const bool ret = add_to_queue(queue,
 
 
1915
                                (task_context){ .func=dummy_func });
 
 
1916
  g_assert_true(errno == ENOMEM);
 
 
1917
  g_assert_false(ret);
 
 
1918
  stderr = real_stderr;
 
 
1919
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
1920
  queue->length = 0;            /* Restore real size */
 
 
1923
static void dummy_func(__attribute__((unused))
 
 
1924
                       const task_context task,
 
 
1925
                       __attribute__((unused))
 
 
1926
                       task_queue *const queue){
 
 
1929
static void test_queue_has_question_empty(__attribute__((unused))
 
 
1930
                                          test_fixture *fixture,
 
 
1931
                                          __attribute__((unused))
 
 
1932
                                          gconstpointer user_data){
 
 
1933
  __attribute__((cleanup(cleanup_queue)))
 
 
1934
    task_queue *queue = create_queue();
 
 
1935
  g_assert_nonnull(queue);
 
 
1936
  g_assert_false(queue_has_question(queue));
 
 
1939
static void test_queue_has_question_false(__attribute__((unused))
 
 
1940
                                          test_fixture *fixture,
 
 
1941
                                          __attribute__((unused))
 
 
1942
                                          gconstpointer user_data){
 
 
1943
  __attribute__((cleanup(cleanup_queue)))
 
 
1944
    task_queue *queue = create_queue();
 
 
1945
  g_assert_nonnull(queue);
 
 
1946
  g_assert_true(add_to_queue(queue,
 
 
1947
                             (task_context){ .func=dummy_func }));
 
 
1948
  g_assert_false(queue_has_question(queue));
 
 
1951
static void test_queue_has_question_true(__attribute__((unused))
 
 
1952
                                         test_fixture *fixture,
 
 
1953
                                         __attribute__((unused))
 
 
1954
                                         gconstpointer user_data){
 
 
1955
  __attribute__((cleanup(cleanup_queue)))
 
 
1956
    task_queue *queue = create_queue();
 
 
1957
  g_assert_nonnull(queue);
 
 
1958
  char *const question_filename
 
 
1959
    = strdup("/nonexistent/question_filename");
 
 
1960
  g_assert_nonnull(question_filename);
 
 
1961
  task_context task = {
 
 
1963
    .question_filename=question_filename,
 
 
1965
  g_assert_true(add_to_queue(queue, task));
 
 
1966
  g_assert_true(queue_has_question(queue));
 
 
1969
static void test_queue_has_question_false2(__attribute__((unused))
 
 
1970
                                           test_fixture *fixture,
 
 
1971
                                           __attribute__((unused))
 
 
1972
                                           gconstpointer user_data){
 
 
1973
  __attribute__((cleanup(cleanup_queue)))
 
 
1974
    task_queue *queue = create_queue();
 
 
1975
  g_assert_nonnull(queue);
 
 
1976
  task_context task = { .func=dummy_func };
 
 
1977
  g_assert_true(add_to_queue(queue, task));
 
 
1978
  g_assert_true(add_to_queue(queue, task));
 
 
1979
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
1980
  g_assert_false(queue_has_question(queue));
 
 
1983
static void test_queue_has_question_true2(__attribute__((unused))
 
 
1984
                                          test_fixture *fixture,
 
 
1985
                                          __attribute__((unused))
 
 
1986
                                          gconstpointer user_data){
 
 
1987
  __attribute__((cleanup(cleanup_queue)))
 
 
1988
    task_queue *queue = create_queue();
 
 
1989
  g_assert_nonnull(queue);
 
 
1990
  task_context task1 = { .func=dummy_func };
 
 
1991
  g_assert_true(add_to_queue(queue, task1));
 
 
1992
  char *const question_filename
 
 
1993
    = strdup("/nonexistent/question_filename");
 
 
1994
  g_assert_nonnull(question_filename);
 
 
1995
  task_context task2 = {
 
 
1997
    .question_filename=question_filename,
 
 
1999
  g_assert_true(add_to_queue(queue, task2));
 
 
2000
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
2001
  g_assert_true(queue_has_question(queue));
 
 
2004
static void test_cleanup_buffer(__attribute__((unused))
 
 
2005
                                test_fixture *fixture,
 
 
2006
                                __attribute__((unused))
 
 
2007
                                gconstpointer user_data){
 
 
2010
  const size_t buffersize = 10;
 
 
2012
  buf.data = malloc(buffersize);
 
 
2013
  g_assert_nonnull(buf.data);
 
 
2014
  if(mlock(buf.data, buffersize) != 0){
 
 
2015
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
2018
  cleanup_buffer(&buf);
 
 
2019
  g_assert_null(buf.data);
 
 
2023
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
 
2024
                                              test_fixture *fixture,
 
 
2025
                                              __attribute__((unused))
 
 
2028
  __attribute__((cleanup(string_set_clear)))
 
 
2029
    string_set set = {};
 
 
2030
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
 
2031
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2035
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
 
2036
                                              test_fixture *fixture,
 
 
2037
                                              __attribute__((unused))
 
 
2040
  __attribute__((cleanup(string_set_clear)))
 
 
2041
    string_set set = {};
 
 
2042
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2043
  g_assert_true(string_set_contains(set, "test_string"));
 
 
2047
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
 
2048
                                             test_fixture *fixture,
 
 
2049
                                             __attribute__((unused))
 
 
2050
                                             gconstpointer user_data){
 
 
2051
  __attribute__((cleanup(string_set_clear)))
 
 
2052
    string_set set = {};
 
 
2053
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2054
  string_set_clear(&set);
 
 
2055
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2059
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
 
2060
                                         test_fixture *fixture,
 
 
2061
                                         __attribute__((unused))
 
 
2062
                                         gconstpointer user_data){
 
 
2063
  __attribute__((cleanup(string_set_clear)))
 
 
2064
    string_set set1 = {};
 
 
2065
  __attribute__((cleanup(string_set_clear)))
 
 
2066
    string_set set2 = {};
 
 
2067
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2068
  string_set_swap(&set1, &set2);
 
 
2069
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2070
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2074
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
 
2075
                                         test_fixture *fixture,
 
 
2076
                                         __attribute__((unused))
 
 
2077
                                         gconstpointer user_data){
 
 
2078
  __attribute__((cleanup(string_set_clear)))
 
 
2079
    string_set set1 = {};
 
 
2080
  __attribute__((cleanup(string_set_clear)))
 
 
2081
    string_set set2 = {};
 
 
2082
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2083
  string_set_swap(&set1, &set2);
 
 
2084
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2085
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2088
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
 
2089
                                              test_fixture *fixture,
 
 
2090
                                              __attribute__((unused))
 
 
2093
  __attribute__((cleanup(string_set_clear)))
 
 
2094
    string_set set1 = {};
 
 
2095
  __attribute__((cleanup(string_set_clear)))
 
 
2096
    string_set set2 = {};
 
 
2097
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2098
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2099
  string_set_swap(&set1, &set2);
 
 
2100
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2101
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2102
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2103
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2106
static bool fd_has_cloexec_and_nonblock(const int);
 
 
2108
static bool epoll_set_contains(int, int, uint32_t);
 
 
2110
static void test_start_mandos_client(test_fixture *fixture,
 
 
2111
                                     __attribute__((unused))
 
 
2112
                                     gconstpointer user_data){
 
 
2114
  bool mandos_client_exited = false;
 
 
2115
  bool quit_now = false;
 
 
2116
  __attribute__((cleanup(cleanup_close)))
 
 
2117
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2118
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2119
  __attribute__((cleanup(cleanup_queue)))
 
 
2120
    task_queue *queue = create_queue();
 
 
2121
  g_assert_nonnull(queue);
 
 
2122
  buffer password = {};
 
 
2123
  bool password_is_read = false;
 
 
2124
  const char helper_directory[] = "/nonexistent";
 
 
2125
  const char *const argv[] = { "/bin/true", NULL };
 
 
2127
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2128
                                    &mandos_client_exited, &quit_now,
 
 
2129
                                    &password, &password_is_read,
 
 
2130
                                    &fixture->orig_sigaction,
 
 
2131
                                    fixture->orig_sigmask,
 
 
2132
                                    helper_directory, 0, 0, argv));
 
 
2134
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
2136
  const task_context *const added_wait_task
 
 
2137
    = find_matching_task(queue, (task_context){
 
 
2138
        .func=wait_for_mandos_client_exit,
 
 
2139
        .mandos_client_exited=&mandos_client_exited,
 
 
2140
        .quit_now=&quit_now,
 
 
2142
  g_assert_nonnull(added_wait_task);
 
 
2143
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
 
2144
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
 
2145
  waitpid(added_wait_task->pid, NULL, 0);
 
 
2147
  const task_context *const added_read_task
 
 
2148
    = find_matching_task(queue, (task_context){
 
 
2149
        .func=read_mandos_client_output,
 
 
2151
        .password=&password,
 
 
2152
        .password_is_read=&password_is_read,
 
 
2153
        .quit_now=&quit_now,
 
 
2155
  g_assert_nonnull(added_read_task);
 
 
2156
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
2157
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
2158
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
 
2159
                                   EPOLLIN | EPOLLRDHUP));
 
 
2162
static bool fd_has_cloexec_and_nonblock(const int fd){
 
 
2163
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
 
2164
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
 
2165
  return ((socket_fd_flags >= 0)
 
 
2166
          and (socket_fd_flags & FD_CLOEXEC)
 
 
2167
          and (socket_file_flags >= 0)
 
 
2168
          and (socket_file_flags & O_NONBLOCK));
 
 
2171
__attribute__((const))
 
 
2172
bool is_privileged(void){
 
 
2173
  uid_t user = getuid() + 1;
 
 
2174
  if(user == 0){                /* Overflow check */
 
 
2177
  gid_t group = getuid() + 1;
 
 
2178
  if(group == 0){               /* Overflow check */
 
 
2181
  const pid_t pid = fork();
 
 
2182
  if(pid == 0){                 /* Child */
 
 
2183
    if(setresgid((uid_t)-1, group, group) == -1){
 
 
2185
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
 
2186
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
2190
    if(setresuid((uid_t)-1, user, user) == -1){
 
 
2192
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
 
2193
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
2200
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
 
2204
  waitpid(pid, &status, 0);
 
 
2205
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
 
2211
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
 
2212
  /* Only scan for events in this eventmask */
 
 
2213
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
 
2214
  __attribute__((cleanup(cleanup_string)))
 
 
2215
    char *fdinfo_name = NULL;
 
 
2216
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
 
2217
  g_assert_cmpint(ret, >, 0);
 
 
2218
  g_assert_nonnull(fdinfo_name);
 
 
2220
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
 
2221
  g_assert_nonnull(fdinfo);
 
 
2222
  uint32_t reported_events;
 
 
2227
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
 
2230
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
 
2231
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
 
2232
              &found_fd, &reported_events) == 2){
 
 
2237
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
 
2238
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
 
2245
    /* Don't check events if none are given */
 
 
2248
  return (reported_events & eventmask) == (events & eventmask);
 
 
2251
static void test_start_mandos_client_execv(test_fixture *fixture,
 
 
2252
                                           __attribute__((unused))
 
 
2253
                                           gconstpointer user_data){
 
 
2254
  bool mandos_client_exited = false;
 
 
2255
  bool quit_now = false;
 
 
2256
  __attribute__((cleanup(cleanup_close)))
 
 
2257
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2258
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2259
  __attribute__((cleanup(cleanup_queue)))
 
 
2260
    task_queue *queue = create_queue();
 
 
2261
  g_assert_nonnull(queue);
 
 
2262
  __attribute__((cleanup(cleanup_buffer)))
 
 
2263
    buffer password = {};
 
 
2264
  const char helper_directory[] = "/nonexistent";
 
 
2265
  /* Can't execv("/", ...), so this should fail */
 
 
2266
  const char *const argv[] = { "/", NULL };
 
 
2269
    __attribute__((cleanup(cleanup_close)))
 
 
2270
      const int devnull_fd = open("/dev/null",
 
 
2271
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2272
    g_assert_cmpint(devnull_fd, >=, 0);
 
 
2273
    __attribute__((cleanup(cleanup_close)))
 
 
2274
      const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2275
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2276
    dup2(devnull_fd, STDERR_FILENO);
 
 
2278
    const bool success = start_mandos_client(queue, epoll_fd,
 
 
2279
                                             &mandos_client_exited,
 
 
2283
                                             &fixture->orig_sigaction,
 
 
2284
                                             fixture->orig_sigmask,
 
 
2285
                                             helper_directory, 0, 0,
 
 
2287
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2288
    g_assert_true(success);
 
 
2290
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
2292
  struct timespec starttime, currtime;
 
 
2293
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2295
    queue->next_run = 0;
 
 
2296
    string_set cancelled_filenames = {};
 
 
2299
      __attribute__((cleanup(cleanup_close)))
 
 
2300
        const int devnull_fd = open("/dev/null",
 
 
2301
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2302
      g_assert_cmpint(devnull_fd, >=, 0);
 
 
2303
      __attribute__((cleanup(cleanup_close)))
 
 
2304
        const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2305
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2306
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2307
      dup2(devnull_fd, STDERR_FILENO);
 
 
2308
      const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2310
      dup2(real_stderr_fd, STDERR_FILENO);
 
 
2315
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2316
  } while(((queue->length) > 0)
 
 
2318
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2320
  g_assert_true(quit_now);
 
 
2321
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2322
  g_assert_true(mandos_client_exited);
 
 
2325
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
 
2326
                                               __attribute__((unused))
 
 
2329
  if(not is_privileged()){
 
 
2330
    g_test_skip("Not privileged");
 
 
2334
  bool mandos_client_exited = false;
 
 
2335
  bool quit_now = false;
 
 
2336
  __attribute__((cleanup(cleanup_close)))
 
 
2337
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2338
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2339
  __attribute__((cleanup(cleanup_queue)))
 
 
2340
    task_queue *queue = create_queue();
 
 
2341
  g_assert_nonnull(queue);
 
 
2342
  __attribute__((cleanup(cleanup_buffer)))
 
 
2343
    buffer password = {};
 
 
2344
  bool password_is_read = false;
 
 
2345
  const char helper_directory[] = "/nonexistent";
 
 
2346
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
 
2350
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2351
                                           &mandos_client_exited,
 
 
2352
                                           &quit_now, &password,
 
 
2354
                                           &fixture->orig_sigaction,
 
 
2355
                                           fixture->orig_sigmask,
 
 
2356
                                           helper_directory, user,
 
 
2358
  g_assert_true(success);
 
 
2359
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2361
  struct timespec starttime, currtime;
 
 
2362
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2364
    queue->next_run = 0;
 
 
2365
    string_set cancelled_filenames = {};
 
 
2366
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2367
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2368
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2369
  } while(((queue->length) > 0)
 
 
2371
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2373
  g_assert_false(quit_now);
 
 
2374
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2375
  g_assert_true(mandos_client_exited);
 
 
2377
  g_assert_true(password_is_read);
 
 
2378
  g_assert_nonnull(password.data);
 
 
2381
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2383
  g_assert_true((uid_t)id == id);
 
 
2385
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2388
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
 
2389
                                               __attribute__((unused))
 
 
2392
  if(not is_privileged()){
 
 
2393
    g_test_skip("Not privileged");
 
 
2397
  bool mandos_client_exited = false;
 
 
2398
  bool quit_now = false;
 
 
2399
  __attribute__((cleanup(cleanup_close)))
 
 
2400
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2401
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2402
  __attribute__((cleanup(cleanup_queue)))
 
 
2403
    task_queue *queue = create_queue();
 
 
2404
  g_assert_nonnull(queue);
 
 
2405
  __attribute__((cleanup(cleanup_buffer)))
 
 
2406
    buffer password = {};
 
 
2407
  bool password_is_read = false;
 
 
2408
  const char helper_directory[] = "/nonexistent";
 
 
2409
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
 
2413
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2414
                                           &mandos_client_exited,
 
 
2415
                                           &quit_now, &password,
 
 
2417
                                           &fixture->orig_sigaction,
 
 
2418
                                           fixture->orig_sigmask,
 
 
2419
                                           helper_directory, user,
 
 
2421
  g_assert_true(success);
 
 
2422
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2424
  struct timespec starttime, currtime;
 
 
2425
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2427
    queue->next_run = 0;
 
 
2428
    string_set cancelled_filenames = {};
 
 
2429
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2430
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2431
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2432
  } while(((queue->length) > 0)
 
 
2434
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2436
  g_assert_false(quit_now);
 
 
2437
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2438
  g_assert_true(mandos_client_exited);
 
 
2440
  g_assert_true(password_is_read);
 
 
2441
  g_assert_nonnull(password.data);
 
 
2444
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2446
  g_assert_true((gid_t)id == id);
 
 
2448
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2451
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
 
2452
                                               __attribute__((unused))
 
 
2455
  if(not is_privileged()){
 
 
2456
    g_test_skip("Not privileged");
 
 
2460
  bool mandos_client_exited = false;
 
 
2461
  bool quit_now = false;
 
 
2462
  __attribute__((cleanup(cleanup_close)))
 
 
2463
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2464
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2465
  __attribute__((cleanup(cleanup_queue)))
 
 
2466
    task_queue *queue = create_queue();
 
 
2467
  g_assert_nonnull(queue);
 
 
2468
  __attribute__((cleanup(cleanup_buffer)))
 
 
2469
    buffer password = {};
 
 
2470
  bool password_is_read = false;
 
 
2471
  const char helper_directory[] = "/nonexistent";
 
 
2472
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
 
2477
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2478
                                           &mandos_client_exited,
 
 
2479
                                           &quit_now, &password,
 
 
2481
                                           &fixture->orig_sigaction,
 
 
2482
                                           fixture->orig_sigmask,
 
 
2483
                                           helper_directory, user,
 
 
2485
  g_assert_true(success);
 
 
2486
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2488
  struct timespec starttime, currtime;
 
 
2489
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2491
    queue->next_run = 0;
 
 
2492
    string_set cancelled_filenames = {};
 
 
2493
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2494
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2495
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2496
  } while(((queue->length) > 0)
 
 
2498
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2500
  g_assert_false(quit_now);
 
 
2501
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2502
  g_assert_true(mandos_client_exited);
 
 
2504
  g_assert_true(password_is_read);
 
 
2505
  g_assert_nonnull(password.data);
 
 
2508
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2510
  g_assert_true((uid_t)id == id);
 
 
2512
  g_assert_cmpuint((unsigned int)id, ==, user);
 
 
2515
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
 
2516
                                               __attribute__((unused))
 
 
2519
  if(not is_privileged()){
 
 
2520
    g_test_skip("Not privileged");
 
 
2524
  bool mandos_client_exited = false;
 
 
2525
  bool quit_now = false;
 
 
2526
  __attribute__((cleanup(cleanup_close)))
 
 
2527
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2528
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2529
  __attribute__((cleanup(cleanup_queue)))
 
 
2530
    task_queue *queue = create_queue();
 
 
2531
  g_assert_nonnull(queue);
 
 
2532
  __attribute__((cleanup(cleanup_buffer)))
 
 
2533
    buffer password = {};
 
 
2534
  bool password_is_read = false;
 
 
2535
  const char helper_directory[] = "/nonexistent";
 
 
2536
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
 
2541
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2542
                                           &mandos_client_exited,
 
 
2543
                                           &quit_now, &password,
 
 
2545
                                           &fixture->orig_sigaction,
 
 
2546
                                           fixture->orig_sigmask,
 
 
2547
                                           helper_directory, user,
 
 
2549
  g_assert_true(success);
 
 
2550
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2552
  struct timespec starttime, currtime;
 
 
2553
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2555
    queue->next_run = 0;
 
 
2556
    string_set cancelled_filenames = {};
 
 
2557
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2558
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2559
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2560
  } while(((queue->length) > 0)
 
 
2562
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2564
  g_assert_false(quit_now);
 
 
2565
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2566
  g_assert_true(mandos_client_exited);
 
 
2568
  g_assert_true(password_is_read);
 
 
2569
  g_assert_nonnull(password.data);
 
 
2572
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2574
  g_assert_true((gid_t)id == id);
 
 
2576
  g_assert_cmpuint((unsigned int)id, ==, group);
 
 
2579
static void test_start_mandos_client_read(test_fixture *fixture,
 
 
2580
                                          __attribute__((unused))
 
 
2581
                                          gconstpointer user_data){
 
 
2582
  bool mandos_client_exited = false;
 
 
2583
  bool quit_now = false;
 
 
2584
  __attribute__((cleanup(cleanup_close)))
 
 
2585
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2586
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2587
  __attribute__((cleanup(cleanup_queue)))
 
 
2588
    task_queue *queue = create_queue();
 
 
2589
  g_assert_nonnull(queue);
 
 
2590
  __attribute__((cleanup(cleanup_buffer)))
 
 
2591
    buffer password = {};
 
 
2592
  bool password_is_read = false;
 
 
2593
  const char dummy_test_password[] = "dummy test password";
 
 
2594
  const char helper_directory[] = "/nonexistent";
 
 
2595
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
 
2598
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2599
                                           &mandos_client_exited,
 
 
2600
                                           &quit_now, &password,
 
 
2602
                                           &fixture->orig_sigaction,
 
 
2603
                                           fixture->orig_sigmask,
 
 
2604
                                           helper_directory, 0, 0,
 
 
2606
  g_assert_true(success);
 
 
2607
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2609
  struct timespec starttime, currtime;
 
 
2610
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2612
    queue->next_run = 0;
 
 
2613
    string_set cancelled_filenames = {};
 
 
2614
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2615
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2616
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2617
  } while(((queue->length) > 0)
 
 
2619
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2621
  g_assert_false(quit_now);
 
 
2622
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2623
  g_assert_true(mandos_client_exited);
 
 
2625
  g_assert_true(password_is_read);
 
 
2626
  g_assert_cmpint((int)password.length, ==,
 
 
2627
                  sizeof(dummy_test_password)-1);
 
 
2628
  g_assert_nonnull(password.data);
 
 
2629
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
 
2630
                         sizeof(dummy_test_password)-1), ==, 0);
 
 
2634
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
 
2635
                                               __attribute__((unused))
 
 
2638
  bool mandos_client_exited = false;
 
 
2639
  bool quit_now = false;
 
 
2640
  __attribute__((cleanup(cleanup_close)))
 
 
2641
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2642
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2643
  __attribute__((cleanup(cleanup_queue)))
 
 
2644
    task_queue *queue = create_queue();
 
 
2645
  g_assert_nonnull(queue);
 
 
2646
  __attribute__((cleanup(cleanup_buffer)))
 
 
2647
    buffer password = {};
 
 
2648
  bool password_is_read = false;
 
 
2649
  const char helper_directory[] = "/nonexistent";
 
 
2650
  const char *const argv[] = { "/bin/sh", "-c",
 
 
2651
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
 
2653
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2654
                                           &mandos_client_exited,
 
 
2655
                                           &quit_now, &password,
 
 
2657
                                           &fixture->orig_sigaction,
 
 
2658
                                           fixture->orig_sigmask,
 
 
2659
                                           helper_directory, 0, 0,
 
 
2661
  g_assert_true(success);
 
 
2662
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2664
  struct timespec starttime, currtime;
 
 
2665
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2667
    queue->next_run = 0;
 
 
2668
    string_set cancelled_filenames = {};
 
 
2669
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2670
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2671
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2672
  } while(((queue->length) > 0)
 
 
2674
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2676
  g_assert_false(quit_now);
 
 
2677
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2678
  g_assert_true(mandos_client_exited);
 
 
2680
  g_assert_true(password_is_read);
 
 
2681
  g_assert_cmpint((int)password.length, ==,
 
 
2682
                  sizeof(helper_directory)-1);
 
 
2683
  g_assert_nonnull(password.data);
 
 
2684
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
 
2685
                         sizeof(helper_directory)-1), ==, 0);
 
 
2688
__attribute__((nonnull, warn_unused_result))
 
 
2689
static bool proc_status_sigblk_to_sigset(const char *const,
 
 
2692
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
 
2693
                                             __attribute__((unused))
 
 
2694
                                             gconstpointer user_data){
 
 
2695
  bool mandos_client_exited = false;
 
 
2696
  bool quit_now = false;
 
 
2697
  __attribute__((cleanup(cleanup_close)))
 
 
2698
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2699
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2700
  __attribute__((cleanup(cleanup_queue)))
 
 
2701
    task_queue *queue = create_queue();
 
 
2702
  g_assert_nonnull(queue);
 
 
2703
  __attribute__((cleanup(cleanup_buffer)))
 
 
2704
    buffer password = {};
 
 
2705
  bool password_is_read = false;
 
 
2706
  const char helper_directory[] = "/nonexistent";
 
 
2707
  /* see proc(5) for format of /proc/self/status */
 
 
2708
  const char *const argv[] = { "/usr/bin/awk",
 
 
2709
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
 
2711
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2712
                                    &mandos_client_exited, &quit_now,
 
 
2713
                                    &password, &password_is_read,
 
 
2714
                                    &fixture->orig_sigaction,
 
 
2715
                                    fixture->orig_sigmask,
 
 
2716
                                    helper_directory, 0, 0, argv));
 
 
2718
  struct timespec starttime, currtime;
 
 
2719
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2721
    queue->next_run = 0;
 
 
2722
    string_set cancelled_filenames = {};
 
 
2723
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2724
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2725
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2726
  } while((not (mandos_client_exited and password_is_read))
 
 
2728
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2729
  g_assert_true(mandos_client_exited);
 
 
2730
  g_assert_true(password_is_read);
 
 
2732
  sigset_t parsed_sigmask;
 
 
2733
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
 
2736
  for(int signum = 1; signum < NSIG; signum++){
 
 
2737
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
 
2738
    if(sigismember(&fixture->orig_sigmask, signum)){
 
 
2739
      g_assert_true(has_signal);
 
 
2741
      g_assert_false(has_signal);
 
 
2746
__attribute__((nonnull, warn_unused_result))
 
 
2747
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
 
2748
                                         sigset_t *const sigmask){
 
 
2749
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
 
2750
  uintmax_t scanned_sigmask;
 
 
2751
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
 
2754
  if(sigemptyset(sigmask) != 0){
 
 
2757
  for(int signum = 1; signum < NSIG; signum++){
 
 
2758
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
 
2759
      if(sigaddset(sigmask, signum) != 0){
 
 
2767
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2768
                                             task_queue *const queue);
 
 
2771
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
 
2772
                                             test_fixture *fixture,
 
 
2773
                                             __attribute__((unused))
 
 
2774
                                             gconstpointer user_data){
 
 
2776
  bool mandos_client_exited = false;
 
 
2777
  bool quit_now = false;
 
 
2779
  __attribute__((cleanup(cleanup_queue)))
 
 
2780
    task_queue *queue = create_queue();
 
 
2781
  g_assert_nonnull(queue);
 
 
2782
  const task_context task = {
 
 
2783
    .func=wait_for_mandos_client_exit,
 
 
2785
    .mandos_client_exited=&mandos_client_exited,
 
 
2786
    .quit_now=&quit_now,
 
 
2788
  run_task_with_stderr_to_dev_null(task, queue);
 
 
2790
  g_assert_false(mandos_client_exited);
 
 
2791
  g_assert_true(quit_now);
 
 
2792
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2795
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2796
                                             task_queue *const queue){
 
 
2797
  FILE *real_stderr = stderr;
 
 
2798
  FILE *devnull = fopen("/dev/null", "we");
 
 
2799
  g_assert_nonnull(devnull);
 
 
2802
  task.func(task, queue);
 
 
2803
  stderr = real_stderr;
 
 
2805
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
2809
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
 
2810
                                             __attribute__((unused))
 
 
2811
                                             gconstpointer user_data){
 
 
2812
  bool mandos_client_exited = false;
 
 
2813
  bool quit_now = false;
 
 
2815
  pid_t create_eternal_process(void){
 
 
2816
    const pid_t pid = fork();
 
 
2817
    if(pid == 0){               /* Child */
 
 
2818
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2819
        _exit(EXIT_FAILURE);
 
 
2821
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2822
        _exit(EXIT_FAILURE);
 
 
2830
  pid_t pid = create_eternal_process();
 
 
2831
  g_assert_true(pid != -1);
 
 
2833
  __attribute__((cleanup(cleanup_queue)))
 
 
2834
    task_queue *queue = create_queue();
 
 
2835
  g_assert_nonnull(queue);
 
 
2836
  const task_context task = {
 
 
2837
    .func=wait_for_mandos_client_exit,
 
 
2839
    .mandos_client_exited=&mandos_client_exited,
 
 
2840
    .quit_now=&quit_now,
 
 
2842
  task.func(task, queue);
 
 
2844
  g_assert_false(mandos_client_exited);
 
 
2845
  g_assert_false(quit_now);
 
 
2846
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
2848
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
2849
        .func=wait_for_mandos_client_exit,
 
 
2851
        .mandos_client_exited=&mandos_client_exited,
 
 
2852
        .quit_now=&quit_now,
 
 
2857
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
 
2858
                                              __attribute__((unused))
 
 
2861
  bool mandos_client_exited = false;
 
 
2862
  bool quit_now = false;
 
 
2864
  pid_t create_successful_process(void){
 
 
2865
    const pid_t pid = fork();
 
 
2866
    if(pid == 0){               /* Child */
 
 
2867
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2868
        _exit(EXIT_FAILURE);
 
 
2870
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2871
        _exit(EXIT_FAILURE);
 
 
2877
  const pid_t pid = create_successful_process();
 
 
2878
  g_assert_true(pid != -1);
 
 
2880
  __attribute__((cleanup(cleanup_queue)))
 
 
2881
    task_queue *queue = create_queue();
 
 
2882
  g_assert_nonnull(queue);
 
 
2883
  const task_context initial_task = {
 
 
2884
    .func=wait_for_mandos_client_exit,
 
 
2886
    .mandos_client_exited=&mandos_client_exited,
 
 
2887
    .quit_now=&quit_now,
 
 
2889
  g_assert_true(add_to_queue(queue, initial_task));
 
 
2891
  struct timespec starttime, currtime;
 
 
2892
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2893
  __attribute__((cleanup(cleanup_close)))
 
 
2894
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2896
    queue->next_run = 0;
 
 
2897
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2898
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
 
2899
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2900
  } while((not mandos_client_exited)
 
 
2902
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2904
  g_assert_true(mandos_client_exited);
 
 
2905
  g_assert_false(quit_now);
 
 
2906
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2910
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
 
2911
                                              __attribute__((unused))
 
 
2914
  bool mandos_client_exited = false;
 
 
2915
  bool quit_now = false;
 
 
2917
  pid_t create_failing_process(void){
 
 
2918
    const pid_t pid = fork();
 
 
2919
    if(pid == 0){               /* Child */
 
 
2920
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2921
        _exit(EXIT_FAILURE);
 
 
2923
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2924
        _exit(EXIT_FAILURE);
 
 
2930
  const pid_t pid = create_failing_process();
 
 
2931
  g_assert_true(pid != -1);
 
 
2933
  __attribute__((cleanup(string_set_clear)))
 
 
2934
    string_set cancelled_filenames = {};
 
 
2935
  __attribute__((cleanup(cleanup_close)))
 
 
2936
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2937
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2938
  __attribute__((cleanup(cleanup_queue)))
 
 
2939
    task_queue *queue = create_queue();
 
 
2940
  g_assert_nonnull(queue);
 
 
2941
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2942
        .func=wait_for_mandos_client_exit,
 
 
2944
        .mandos_client_exited=&mandos_client_exited,
 
 
2945
        .quit_now=&quit_now,
 
 
2948
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
 
2950
  __attribute__((cleanup(cleanup_close)))
 
 
2951
    const int devnull_fd = open("/dev/null",
 
 
2952
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2953
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2954
  __attribute__((cleanup(cleanup_close)))
 
 
2955
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2956
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2958
  struct timespec starttime, currtime;
 
 
2959
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2961
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2962
    dup2(devnull_fd, STDERR_FILENO);
 
 
2963
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2965
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2970
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2971
  } while((not mandos_client_exited)
 
 
2973
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2975
  g_assert_true(quit_now);
 
 
2976
  g_assert_true(mandos_client_exited);
 
 
2977
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2981
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
 
2982
                                             __attribute__((unused))
 
 
2983
                                             gconstpointer user_data){
 
 
2984
  bool mandos_client_exited = false;
 
 
2985
  bool quit_now = false;
 
 
2987
  pid_t create_killed_process(void){
 
 
2988
    const pid_t pid = fork();
 
 
2989
    if(pid == 0){               /* Child */
 
 
2990
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2991
        _exit(EXIT_FAILURE);
 
 
2993
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2994
        _exit(EXIT_FAILURE);
 
 
3003
  const pid_t pid = create_killed_process();
 
 
3004
  g_assert_true(pid != -1);
 
 
3006
  __attribute__((cleanup(string_set_clear)))
 
 
3007
    string_set cancelled_filenames = {};
 
 
3008
  __attribute__((cleanup(cleanup_close)))
 
 
3009
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3010
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3011
  __attribute__((cleanup(cleanup_queue)))
 
 
3012
    task_queue *queue = create_queue();
 
 
3013
  g_assert_nonnull(queue);
 
 
3014
  g_assert_true(add_to_queue(queue, (task_context){
 
 
3015
        .func=wait_for_mandos_client_exit,
 
 
3017
        .mandos_client_exited=&mandos_client_exited,
 
 
3018
        .quit_now=&quit_now,
 
 
3021
  __attribute__((cleanup(cleanup_close)))
 
 
3022
    const int devnull_fd = open("/dev/null",
 
 
3023
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
 
3024
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
3025
  __attribute__((cleanup(cleanup_close)))
 
 
3026
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
3027
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
3029
  struct timespec starttime, currtime;
 
 
3030
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
3032
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
3033
    dup2(devnull_fd, STDERR_FILENO);
 
 
3034
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
3036
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
3041
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
3042
  } while((not mandos_client_exited)
 
 
3044
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
3046
  g_assert_true(mandos_client_exited);
 
 
3047
  g_assert_true(quit_now);
 
 
3048
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3051
static bool epoll_set_does_not_contain(int, int);
 
 
3054
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
 
3055
                                              test_fixture *fixture,
 
 
3056
                                              __attribute__((unused))
 
 
3059
  __attribute__((cleanup(cleanup_close)))
 
 
3060
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3061
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3063
  __attribute__((cleanup(cleanup_buffer)))
 
 
3064
    buffer password = {};
 
 
3066
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
 
3067
  const int fd = open("/proc/self/mem",
 
 
3068
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3070
  bool password_is_read = false;
 
 
3071
  bool quit_now = false;
 
 
3072
  __attribute__((cleanup(cleanup_queue)))
 
 
3073
    task_queue *queue = create_queue();
 
 
3074
  g_assert_nonnull(queue);
 
 
3076
  task_context task = {
 
 
3077
    .func=read_mandos_client_output,
 
 
3080
    .password=&password,
 
 
3081
    .password_is_read=&password_is_read,
 
 
3082
    .quit_now=&quit_now,
 
 
3084
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3085
  g_assert_false(password_is_read);
 
 
3086
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3087
  g_assert_true(quit_now);
 
 
3088
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3090
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3092
  g_assert_cmpint(close(fd), ==, -1);
 
 
3095
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
 
3096
  return not epoll_set_contains(epoll_fd, fd, 0);
 
 
3100
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
 
3101
                                           test_fixture *fixture,
 
 
3102
                                           __attribute__((unused))
 
 
3103
                                           gconstpointer user_data){
 
 
3104
  __attribute__((cleanup(cleanup_close)))
 
 
3105
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3106
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3109
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3111
  __attribute__((cleanup(cleanup_buffer)))
 
 
3112
    buffer password = {};
 
 
3114
  bool password_is_read = false;
 
 
3115
  bool quit_now = false;
 
 
3116
  __attribute__((cleanup(cleanup_queue)))
 
 
3117
    task_queue *queue = create_queue();
 
 
3118
  g_assert_nonnull(queue);
 
 
3120
  task_context task = {
 
 
3121
    .func=read_mandos_client_output,
 
 
3124
    .password=&password,
 
 
3125
    .password_is_read=&password_is_read,
 
 
3126
    .quit_now=&quit_now,
 
 
3128
  task.func(task, queue);
 
 
3129
  g_assert_false(password_is_read);
 
 
3130
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3131
  g_assert_false(quit_now);
 
 
3132
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3134
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3135
        .func=read_mandos_client_output,
 
 
3138
        .password=&password,
 
 
3139
        .password_is_read=&password_is_read,
 
 
3140
        .quit_now=&quit_now,
 
 
3143
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3144
                                   EPOLLIN | EPOLLRDHUP));
 
 
3146
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3149
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
 
3150
                                               test_fixture *fixture,
 
 
3151
                                               __attribute__((unused))
 
 
3154
  __attribute__((cleanup(cleanup_close)))
 
 
3155
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3156
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3159
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3160
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3162
  __attribute__((cleanup(cleanup_buffer)))
 
 
3163
    buffer password = {};
 
 
3165
  bool password_is_read = false;
 
 
3166
  bool quit_now = false;
 
 
3167
  __attribute__((cleanup(cleanup_queue)))
 
 
3168
    task_queue *queue = create_queue();
 
 
3169
  g_assert_nonnull(queue);
 
 
3171
  task_context task = {
 
 
3172
    .func=read_mandos_client_output,
 
 
3175
    .password=&password,
 
 
3176
    .password_is_read=&password_is_read,
 
 
3177
    .quit_now=&quit_now,
 
 
3179
  task.func(task, queue);
 
 
3180
  g_assert_true(password_is_read);
 
 
3181
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3182
  g_assert_false(quit_now);
 
 
3183
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3185
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3187
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3191
void test_read_mandos_client_output_once(__attribute__((unused))
 
 
3192
                                         test_fixture *fixture,
 
 
3193
                                         __attribute__((unused))
 
 
3194
                                         gconstpointer user_data){
 
 
3195
  __attribute__((cleanup(cleanup_close)))
 
 
3196
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3197
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3200
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3202
  const char dummy_test_password[] = "dummy test password";
 
 
3203
  /* Start with a pre-allocated buffer */
 
 
3204
  __attribute__((cleanup(cleanup_buffer)))
 
 
3206
    .data=malloc(sizeof(dummy_test_password)),
 
 
3208
    .allocated=sizeof(dummy_test_password),
 
 
3210
  g_assert_nonnull(password.data);
 
 
3211
  if(mlock(password.data, password.allocated) != 0){
 
 
3212
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3215
  bool password_is_read = false;
 
 
3216
  bool quit_now = false;
 
 
3217
  __attribute__((cleanup(cleanup_queue)))
 
 
3218
    task_queue *queue = create_queue();
 
 
3219
  g_assert_nonnull(queue);
 
 
3221
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3222
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3223
                             sizeof(dummy_test_password)),
 
 
3224
                  ==, (int)sizeof(dummy_test_password));
 
 
3226
  task_context task = {
 
 
3227
    .func=read_mandos_client_output,
 
 
3230
    .password=&password,
 
 
3231
    .password_is_read=&password_is_read,
 
 
3232
    .quit_now=&quit_now,
 
 
3234
  task.func(task, queue);
 
 
3236
  g_assert_false(password_is_read);
 
 
3237
  g_assert_cmpint((int)password.length, ==,
 
 
3238
                  (int)sizeof(dummy_test_password));
 
 
3239
  g_assert_nonnull(password.data);
 
 
3240
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3241
                         sizeof(dummy_test_password)), ==, 0);
 
 
3243
  g_assert_false(quit_now);
 
 
3244
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3246
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3247
        .func=read_mandos_client_output,
 
 
3250
        .password=&password,
 
 
3251
        .password_is_read=&password_is_read,
 
 
3252
        .quit_now=&quit_now,
 
 
3255
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3256
                                   EPOLLIN | EPOLLRDHUP));
 
 
3258
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3262
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
 
3263
                                           test_fixture *fixture,
 
 
3264
                                           __attribute__((unused))
 
 
3265
                                           gconstpointer user_data){
 
 
3266
  __attribute__((cleanup(cleanup_close)))
 
 
3267
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3268
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3271
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3273
  const char dummy_test_password[] = "dummy test password";
 
 
3274
  /* Start with an empty buffer */
 
 
3275
  __attribute__((cleanup(cleanup_buffer)))
 
 
3276
    buffer password = {};
 
 
3278
  bool password_is_read = false;
 
 
3279
  bool quit_now = false;
 
 
3280
  __attribute__((cleanup(cleanup_queue)))
 
 
3281
    task_queue *queue = create_queue();
 
 
3282
  g_assert_nonnull(queue);
 
 
3284
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3285
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3286
                             sizeof(dummy_test_password)),
 
 
3287
                  ==, (int)sizeof(dummy_test_password));
 
 
3289
  task_context task = {
 
 
3290
    .func=read_mandos_client_output,
 
 
3293
    .password=&password,
 
 
3294
    .password_is_read=&password_is_read,
 
 
3295
    .quit_now=&quit_now,
 
 
3297
  task.func(task, queue);
 
 
3299
  g_assert_false(password_is_read);
 
 
3300
  g_assert_cmpint((int)password.length, ==,
 
 
3301
                  (int)sizeof(dummy_test_password));
 
 
3302
  g_assert_nonnull(password.data);
 
 
3303
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3304
                         sizeof(dummy_test_password)), ==, 0);
 
 
3306
  g_assert_false(quit_now);
 
 
3307
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3309
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3310
        .func=read_mandos_client_output,
 
 
3313
        .password=&password,
 
 
3314
        .password_is_read=&password_is_read,
 
 
3315
        .quit_now=&quit_now,
 
 
3318
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3319
                                   EPOLLIN | EPOLLRDHUP));
 
 
3321
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3325
void test_read_mandos_client_output_append(__attribute__((unused))
 
 
3326
                                           test_fixture *fixture,
 
 
3327
                                           __attribute__((unused))
 
 
3328
                                           gconstpointer user_data){
 
 
3329
  __attribute__((cleanup(cleanup_close)))
 
 
3330
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3331
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3334
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3336
  const char dummy_test_password[] = "dummy test password";
 
 
3337
  __attribute__((cleanup(cleanup_buffer)))
 
 
3339
    .data=malloc(PIPE_BUF),
 
 
3341
    .allocated=PIPE_BUF,
 
 
3343
  g_assert_nonnull(password.data);
 
 
3344
  if(mlock(password.data, password.allocated) != 0){
 
 
3345
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3348
  memset(password.data, 'x', PIPE_BUF);
 
 
3349
  char password_expected[PIPE_BUF];
 
 
3350
  memcpy(password_expected, password.data, PIPE_BUF);
 
 
3352
  bool password_is_read = false;
 
 
3353
  bool quit_now = false;
 
 
3354
  __attribute__((cleanup(cleanup_queue)))
 
 
3355
    task_queue *queue = create_queue();
 
 
3356
  g_assert_nonnull(queue);
 
 
3358
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3359
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3360
                             sizeof(dummy_test_password)),
 
 
3361
                  ==, (int)sizeof(dummy_test_password));
 
 
3363
  task_context task = {
 
 
3364
    .func=read_mandos_client_output,
 
 
3367
    .password=&password,
 
 
3368
    .password_is_read=&password_is_read,
 
 
3369
    .quit_now=&quit_now,
 
 
3371
  task.func(task, queue);
 
 
3373
  g_assert_false(password_is_read);
 
 
3374
  g_assert_cmpint((int)password.length, ==,
 
 
3375
                  PIPE_BUF + sizeof(dummy_test_password));
 
 
3376
  g_assert_nonnull(password.data);
 
 
3377
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
 
3379
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
 
3380
                         dummy_test_password,
 
 
3381
                         sizeof(dummy_test_password)), ==, 0);
 
 
3382
  g_assert_false(quit_now);
 
 
3383
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3385
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3386
        .func=read_mandos_client_output,
 
 
3389
        .password=&password,
 
 
3390
        .password_is_read=&password_is_read,
 
 
3391
        .quit_now=&quit_now,
 
 
3394
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3395
                                   EPOLLIN | EPOLLRDHUP));
 
 
3398
static char *make_temporary_directory(void);
 
 
3400
static void test_add_inotify_dir_watch(__attribute__((unused))
 
 
3401
                                       test_fixture *fixture,
 
 
3402
                                       __attribute__((unused))
 
 
3403
                                       gconstpointer user_data){
 
 
3404
  __attribute__((cleanup(cleanup_close)))
 
 
3405
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3406
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3407
  __attribute__((cleanup(cleanup_queue)))
 
 
3408
    task_queue *queue = create_queue();
 
 
3409
  g_assert_nonnull(queue);
 
 
3410
  __attribute__((cleanup(string_set_clear)))
 
 
3411
    string_set cancelled_filenames = {};
 
 
3412
  const mono_microsecs current_time = 0;
 
 
3414
  bool quit_now = false;
 
 
3415
  buffer password = {};
 
 
3416
  bool mandos_client_exited = false;
 
 
3417
  bool password_is_read = false;
 
 
3419
  __attribute__((cleanup(cleanup_string)))
 
 
3420
    char *tempdir = make_temporary_directory();
 
 
3421
  g_assert_nonnull(tempdir);
 
 
3423
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3425
                                      &cancelled_filenames,
 
 
3427
                                      &mandos_client_exited,
 
 
3428
                                      &password_is_read));
 
 
3430
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3432
  const task_context *const added_read_task
 
 
3433
    = find_matching_task(queue, (task_context){
 
 
3434
        .func=read_inotify_event,
 
 
3436
        .quit_now=&quit_now,
 
 
3437
        .password=&password,
 
 
3439
        .cancelled_filenames=&cancelled_filenames,
 
 
3440
        .current_time=¤t_time,
 
 
3441
        .mandos_client_exited=&mandos_client_exited,
 
 
3442
        .password_is_read=&password_is_read,
 
 
3444
  g_assert_nonnull(added_read_task);
 
 
3446
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3447
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3448
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
 
3449
                                   added_read_task->fd,
 
 
3450
                                   EPOLLIN | EPOLLRDHUP));
 
 
3452
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3455
static char *make_temporary_directory(void){
 
 
3456
  char *name = strdup("/tmp/mandosXXXXXX");
 
 
3457
  g_assert_nonnull(name);
 
 
3458
  char *result = mkdtemp(name);
 
 
3465
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
 
3466
                                            test_fixture *fixture,
 
 
3467
                                            __attribute__((unused))
 
 
3468
                                            gconstpointer user_data){
 
 
3469
  __attribute__((cleanup(cleanup_close)))
 
 
3470
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3471
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3472
  __attribute__((cleanup(cleanup_queue)))
 
 
3473
    task_queue *queue = create_queue();
 
 
3474
  g_assert_nonnull(queue);
 
 
3475
  __attribute__((cleanup(string_set_clear)))
 
 
3476
    string_set cancelled_filenames = {};
 
 
3477
  const mono_microsecs current_time = 0;
 
 
3479
  bool quit_now = false;
 
 
3480
  buffer password = {};
 
 
3481
  bool mandos_client_exited = false;
 
 
3482
  bool password_is_read = false;
 
 
3484
  const char nonexistent_dir[] = "/nonexistent";
 
 
3486
  FILE *real_stderr = stderr;
 
 
3487
  FILE *devnull = fopen("/dev/null", "we");
 
 
3488
  g_assert_nonnull(devnull);
 
 
3490
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3491
                                       &password, nonexistent_dir,
 
 
3492
                                       &cancelled_filenames,
 
 
3494
                                       &mandos_client_exited,
 
 
3495
                                       &password_is_read));
 
 
3496
  stderr = real_stderr;
 
 
3497
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3499
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3502
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
 
3503
                                              test_fixture *fixture,
 
 
3504
                                            __attribute__((unused))
 
 
3507
  __attribute__((cleanup(cleanup_close)))
 
 
3508
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3509
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3510
  __attribute__((cleanup(cleanup_queue)))
 
 
3511
    task_queue *queue = create_queue();
 
 
3512
  g_assert_nonnull(queue);
 
 
3513
  __attribute__((cleanup(string_set_clear)))
 
 
3514
    string_set cancelled_filenames = {};
 
 
3515
  const mono_microsecs current_time = 0;
 
 
3517
  bool quit_now = false;
 
 
3518
  buffer password = {};
 
 
3519
  bool mandos_client_exited = false;
 
 
3520
  bool password_is_read = false;
 
 
3522
  const char not_a_directory[] = "/dev/tty";
 
 
3524
  FILE *real_stderr = stderr;
 
 
3525
  FILE *devnull = fopen("/dev/null", "we");
 
 
3526
  g_assert_nonnull(devnull);
 
 
3528
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3529
                                       &password, not_a_directory,
 
 
3530
                                       &cancelled_filenames,
 
 
3532
                                       &mandos_client_exited,
 
 
3533
                                       &password_is_read));
 
 
3534
  stderr = real_stderr;
 
 
3535
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3537
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3540
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
 
3541
                                              test_fixture *fixture,
 
 
3542
                                              __attribute__((unused))
 
 
3545
  __attribute__((cleanup(cleanup_close)))
 
 
3546
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3547
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3548
  __attribute__((cleanup(cleanup_queue)))
 
 
3549
    task_queue *queue = create_queue();
 
 
3550
  g_assert_nonnull(queue);
 
 
3551
  __attribute__((cleanup(string_set_clear)))
 
 
3552
    string_set cancelled_filenames = {};
 
 
3553
  const mono_microsecs current_time = 0;
 
 
3555
  bool quit_now = false;
 
 
3556
  buffer password = {};
 
 
3557
  bool mandos_client_exited = false;
 
 
3558
  bool password_is_read = false;
 
 
3560
  __attribute__((cleanup(cleanup_string)))
 
 
3561
    char *tempdir = make_temporary_directory();
 
 
3562
  g_assert_nonnull(tempdir);
 
 
3564
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3566
                                      &cancelled_filenames,
 
 
3568
                                      &mandos_client_exited,
 
 
3569
                                      &password_is_read));
 
 
3571
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3573
  const task_context *const added_read_task
 
 
3574
    = find_matching_task(queue,
 
 
3575
                         (task_context){ .func=read_inotify_event });
 
 
3576
  g_assert_nonnull(added_read_task);
 
 
3578
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3579
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3581
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3582
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3584
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3585
  g_assert_nonnull(ievent);
 
 
3587
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
 
3589
  g_assert_cmpint(errno, ==, EAGAIN);
 
 
3593
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3596
static char *make_temporary_file_in_directory(const char
 
 
3600
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3601
                                               test_fixture *fixture,
 
 
3602
                                               __attribute__((unused))
 
 
3605
  __attribute__((cleanup(cleanup_close)))
 
 
3606
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3607
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3608
  __attribute__((cleanup(cleanup_queue)))
 
 
3609
    task_queue *queue = create_queue();
 
 
3610
  g_assert_nonnull(queue);
 
 
3611
  __attribute__((cleanup(string_set_clear)))
 
 
3612
    string_set cancelled_filenames = {};
 
 
3613
  const mono_microsecs current_time = 0;
 
 
3615
  bool quit_now = false;
 
 
3616
  buffer password = {};
 
 
3617
  bool mandos_client_exited = false;
 
 
3618
  bool password_is_read = false;
 
 
3620
  __attribute__((cleanup(cleanup_string)))
 
 
3621
    char *tempdir = make_temporary_directory();
 
 
3622
  g_assert_nonnull(tempdir);
 
 
3624
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3626
                                      &cancelled_filenames,
 
 
3628
                                      &mandos_client_exited,
 
 
3629
                                      &password_is_read));
 
 
3631
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3633
  const task_context *const added_read_task
 
 
3634
    = find_matching_task(queue,
 
 
3635
                         (task_context){ .func=read_inotify_event });
 
 
3636
  g_assert_nonnull(added_read_task);
 
 
3638
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3639
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3641
  __attribute__((cleanup(cleanup_string)))
 
 
3642
    char *filename = make_temporary_file_in_directory(tempdir);
 
 
3643
  g_assert_nonnull(filename);
 
 
3645
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3646
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3648
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3649
  g_assert_nonnull(ievent);
 
 
3651
  ssize_t read_size = 0;
 
 
3652
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3654
  g_assert_cmpint((int)read_size, >, 0);
 
 
3655
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
 
3656
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
 
3660
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
3661
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3664
static char *make_temporary_prefixed_file_in_directory(const char
 
 
3668
  char *filename = NULL;
 
 
3669
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
 
3671
  g_assert_nonnull(filename);
 
 
3672
  const int fd = mkostemp(filename, O_CLOEXEC);
 
 
3673
  g_assert_cmpint(fd, >=, 0);
 
 
3674
  g_assert_cmpint(close(fd), ==, 0);
 
 
3678
static char *make_temporary_file_in_directory(const char
 
 
3680
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
 
3684
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
 
3685
                                            test_fixture *fixture,
 
 
3686
                                            __attribute__((unused))
 
 
3687
                                            gconstpointer user_data){
 
 
3688
  __attribute__((cleanup(cleanup_close)))
 
 
3689
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3690
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3691
  __attribute__((cleanup(cleanup_queue)))
 
 
3692
    task_queue *queue = create_queue();
 
 
3693
  g_assert_nonnull(queue);
 
 
3694
  __attribute__((cleanup(string_set_clear)))
 
 
3695
    string_set cancelled_filenames = {};
 
 
3696
  const mono_microsecs current_time = 0;
 
 
3698
  bool quit_now = false;
 
 
3699
  buffer password = {};
 
 
3700
  bool mandos_client_exited = false;
 
 
3701
  bool password_is_read = false;
 
 
3703
  __attribute__((cleanup(cleanup_string)))
 
 
3704
    char *watchdir = make_temporary_directory();
 
 
3705
  g_assert_nonnull(watchdir);
 
 
3707
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3708
                                      &password, watchdir,
 
 
3709
                                      &cancelled_filenames,
 
 
3711
                                      &mandos_client_exited,
 
 
3712
                                      &password_is_read));
 
 
3714
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3716
  const task_context *const added_read_task
 
 
3717
    = find_matching_task(queue,
 
 
3718
                         (task_context){ .func=read_inotify_event });
 
 
3719
  g_assert_nonnull(added_read_task);
 
 
3721
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3722
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3724
  char *sourcedir = make_temporary_directory();
 
 
3725
  g_assert_nonnull(sourcedir);
 
 
3727
  __attribute__((cleanup(cleanup_string)))
 
 
3728
    char *filename = make_temporary_file_in_directory(sourcedir);
 
 
3729
  g_assert_nonnull(filename);
 
 
3731
  __attribute__((cleanup(cleanup_string)))
 
 
3732
    char *targetfilename = NULL;
 
 
3733
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
 
3734
                           basename(filename)), >, 0);
 
 
3735
  g_assert_nonnull(targetfilename);
 
 
3737
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
 
3738
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
 
3741
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3742
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3744
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3745
  g_assert_nonnull(ievent);
 
 
3747
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3749
  g_assert_cmpint((int)read_size, >, 0);
 
 
3750
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
 
3751
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
 
3755
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3756
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
 
3760
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
 
3761
                                              test_fixture *fixture,
 
 
3762
                                              __attribute__((unused))
 
 
3765
  __attribute__((cleanup(cleanup_close)))
 
 
3766
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3767
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3768
  __attribute__((cleanup(cleanup_queue)))
 
 
3769
    task_queue *queue = create_queue();
 
 
3770
  g_assert_nonnull(queue);
 
 
3771
  __attribute__((cleanup(string_set_clear)))
 
 
3772
    string_set cancelled_filenames = {};
 
 
3773
  const mono_microsecs current_time = 0;
 
 
3775
  bool quit_now = false;
 
 
3776
  buffer password = {};
 
 
3777
  bool mandos_client_exited = false;
 
 
3778
  bool password_is_read = false;
 
 
3780
  __attribute__((cleanup(cleanup_string)))
 
 
3781
    char *tempdir = make_temporary_directory();
 
 
3782
  g_assert_nonnull(tempdir);
 
 
3784
  __attribute__((cleanup(cleanup_string)))
 
 
3785
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
 
3786
  g_assert_nonnull(tempfilename);
 
 
3788
  __attribute__((cleanup(cleanup_string)))
 
 
3789
    char *targetdir = make_temporary_directory();
 
 
3790
  g_assert_nonnull(targetdir);
 
 
3792
  __attribute__((cleanup(cleanup_string)))
 
 
3793
    char *targetfilename = NULL;
 
 
3794
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
 
3795
                           basename(tempfilename)), >, 0);
 
 
3796
  g_assert_nonnull(targetfilename);
 
 
3798
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3800
                                      &cancelled_filenames,
 
 
3802
                                      &mandos_client_exited,
 
 
3803
                                      &password_is_read));
 
 
3805
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
 
3807
  const task_context *const added_read_task
 
 
3808
    = find_matching_task(queue,
 
 
3809
                         (task_context){ .func=read_inotify_event });
 
 
3810
  g_assert_nonnull(added_read_task);
 
 
3812
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3813
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3815
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3816
  g_assert_nonnull(ievent);
 
 
3818
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3820
  g_assert_cmpint((int)read_size, >, 0);
 
 
3821
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
 
3822
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
 
3826
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3827
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
 
3828
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3832
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
 
3833
                                          test_fixture *fixture,
 
 
3834
                                          __attribute__((unused))
 
 
3835
                                          gconstpointer user_data){
 
 
3836
  __attribute__((cleanup(cleanup_close)))
 
 
3837
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3838
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3839
  __attribute__((cleanup(cleanup_queue)))
 
 
3840
    task_queue *queue = create_queue();
 
 
3841
  g_assert_nonnull(queue);
 
 
3842
  __attribute__((cleanup(string_set_clear)))
 
 
3843
    string_set cancelled_filenames = {};
 
 
3844
  const mono_microsecs current_time = 0;
 
 
3846
  bool quit_now = false;
 
 
3847
  buffer password = {};
 
 
3848
  bool mandos_client_exited = false;
 
 
3849
  bool password_is_read = false;
 
 
3851
  __attribute__((cleanup(cleanup_string)))
 
 
3852
    char *tempdir = make_temporary_directory();
 
 
3853
  g_assert_nonnull(tempdir);
 
 
3855
  __attribute__((cleanup(cleanup_string)))
 
 
3856
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3857
  g_assert_nonnull(tempfile);
 
 
3859
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3861
                                      &cancelled_filenames,
 
 
3863
                                      &mandos_client_exited,
 
 
3864
                                      &password_is_read));
 
 
3865
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3867
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3869
  const task_context *const added_read_task
 
 
3870
    = find_matching_task(queue,
 
 
3871
                         (task_context){ .func=read_inotify_event });
 
 
3872
  g_assert_nonnull(added_read_task);
 
 
3874
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3875
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3877
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3878
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3880
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3881
  g_assert_nonnull(ievent);
 
 
3883
  ssize_t read_size = 0;
 
 
3884
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3886
  g_assert_cmpint((int)read_size, >, 0);
 
 
3887
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3888
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3892
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3896
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
 
3897
                                               test_fixture *fixture,
 
 
3898
                                               __attribute__((unused))
 
 
3901
  __attribute__((cleanup(cleanup_close)))
 
 
3902
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3903
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3904
  __attribute__((cleanup(cleanup_queue)))
 
 
3905
    task_queue *queue = create_queue();
 
 
3906
  g_assert_nonnull(queue);
 
 
3907
  __attribute__((cleanup(string_set_clear)))
 
 
3908
    string_set cancelled_filenames = {};
 
 
3909
  const mono_microsecs current_time = 0;
 
 
3911
  bool quit_now = false;
 
 
3912
  buffer password = {};
 
 
3913
  bool mandos_client_exited = false;
 
 
3914
  bool password_is_read = false;
 
 
3916
  __attribute__((cleanup(cleanup_string)))
 
 
3917
    char *tempdir = make_temporary_directory();
 
 
3918
  g_assert_nonnull(tempdir);
 
 
3920
  __attribute__((cleanup(cleanup_string)))
 
 
3921
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3922
  g_assert_nonnull(tempfile);
 
 
3923
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
 
3925
  g_assert_cmpint(tempfile_fd, >, 2);
 
 
3927
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3929
                                      &cancelled_filenames,
 
 
3931
                                      &mandos_client_exited,
 
 
3932
                                      &password_is_read));
 
 
3933
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3935
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3937
  const task_context *const added_read_task
 
 
3938
    = find_matching_task(queue,
 
 
3939
                         (task_context){ .func=read_inotify_event });
 
 
3940
  g_assert_nonnull(added_read_task);
 
 
3942
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3943
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3945
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3946
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3948
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3949
  g_assert_nonnull(ievent);
 
 
3951
  ssize_t read_size = 0;
 
 
3952
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3954
  g_assert_cmpint((int)read_size, >, 0);
 
 
3955
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3956
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3958
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
 
3960
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
 
3961
     file not appear as an ievent, so we should not see it now. */
 
 
3962
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3963
  g_assert_cmpint((int)read_size, ==, -1);
 
 
3964
  g_assert_true(errno == EAGAIN);
 
 
3968
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3971
static void test_read_inotify_event_readerror(__attribute__((unused))
 
 
3972
                                              test_fixture *fixture,
 
 
3973
                                              __attribute__((unused))
 
 
3976
  __attribute__((cleanup(cleanup_close)))
 
 
3977
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3978
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3979
  const mono_microsecs current_time = 0;
 
 
3981
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
 
3982
  const int fd = open("/proc/self/mem",
 
 
3983
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3985
  bool quit_now = 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
    .filename=strdup("/nonexistent"),
 
 
3996
    .cancelled_filenames = &(string_set){},
 
 
3998
    .current_time=¤t_time,
 
 
4000
  g_assert_nonnull(task.filename);
 
 
4001
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4002
  g_assert_true(quit_now);
 
 
4003
  g_assert_true(queue->next_run == 0);
 
 
4004
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4006
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
4008
  g_assert_cmpint(close(fd), ==, -1);
 
 
4011
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
 
4012
                                              test_fixture *fixture,
 
 
4013
                                              __attribute__((unused))
 
 
4016
  const mono_microsecs current_time = 17;
 
 
4019
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4020
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
 
4022
  bool quit_now = false;
 
 
4023
  buffer password = {};
 
 
4024
  bool mandos_client_exited = false;
 
 
4025
  bool password_is_read = false;
 
 
4026
  __attribute__((cleanup(cleanup_queue)))
 
 
4027
    task_queue *queue = create_queue();
 
 
4028
  g_assert_nonnull(queue);
 
 
4030
  task_context task = {
 
 
4031
    .func=read_inotify_event,
 
 
4034
    .quit_now=&quit_now,
 
 
4035
    .password=&password,
 
 
4036
    .filename=strdup("/nonexistent"),
 
 
4037
    .cancelled_filenames = &(string_set){},
 
 
4039
    .current_time=¤t_time,
 
 
4040
    .mandos_client_exited=&mandos_client_exited,
 
 
4041
    .password_is_read=&password_is_read,
 
 
4043
  g_assert_nonnull(task.filename);
 
 
4044
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4046
  g_assert_nonnull(find_matching_task(queue, task));
 
 
4047
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
4049
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
 
4050
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4053
static void test_read_inotify_event_nodata(__attribute__((unused))
 
 
4054
                                           test_fixture *fixture,
 
 
4055
                                           __attribute__((unused))
 
 
4056
                                           gconstpointer user_data){
 
 
4057
  __attribute__((cleanup(cleanup_close)))
 
 
4058
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4059
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4060
  const mono_microsecs current_time = 0;
 
 
4063
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4065
  bool quit_now = false;
 
 
4066
  buffer password = {};
 
 
4067
  bool mandos_client_exited = false;
 
 
4068
  bool password_is_read = false;
 
 
4069
  __attribute__((cleanup(cleanup_queue)))
 
 
4070
    task_queue *queue = create_queue();
 
 
4071
  g_assert_nonnull(queue);
 
 
4073
  task_context task = {
 
 
4074
    .func=read_inotify_event,
 
 
4077
    .quit_now=&quit_now,
 
 
4078
    .password=&password,
 
 
4079
    .filename=strdup("/nonexistent"),
 
 
4080
    .cancelled_filenames = &(string_set){},
 
 
4082
    .current_time=¤t_time,
 
 
4083
    .mandos_client_exited=&mandos_client_exited,
 
 
4084
    .password_is_read=&password_is_read,
 
 
4086
  g_assert_nonnull(task.filename);
 
 
4087
  task.func(task, queue);
 
 
4088
  g_assert_false(quit_now);
 
 
4089
  g_assert_true(queue->next_run == 0);
 
 
4090
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4092
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4093
        .func=read_inotify_event,
 
 
4096
        .quit_now=&quit_now,
 
 
4097
        .password=&password,
 
 
4098
        .filename=task.filename,
 
 
4099
        .cancelled_filenames=task.cancelled_filenames,
 
 
4100
        .current_time=¤t_time,
 
 
4101
        .mandos_client_exited=&mandos_client_exited,
 
 
4102
        .password_is_read=&password_is_read,
 
 
4105
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4106
                                   EPOLLIN | EPOLLRDHUP));
 
 
4108
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4111
static void test_read_inotify_event_eof(__attribute__((unused))
 
 
4112
                                        test_fixture *fixture,
 
 
4113
                                        __attribute__((unused))
 
 
4114
                                        gconstpointer user_data){
 
 
4115
  __attribute__((cleanup(cleanup_close)))
 
 
4116
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4117
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4118
  const mono_microsecs current_time = 0;
 
 
4121
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4122
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4124
  bool quit_now = false;
 
 
4125
  buffer password = {};
 
 
4126
  __attribute__((cleanup(cleanup_queue)))
 
 
4127
    task_queue *queue = create_queue();
 
 
4128
  g_assert_nonnull(queue);
 
 
4130
  task_context task = {
 
 
4131
    .func=read_inotify_event,
 
 
4134
    .quit_now=&quit_now,
 
 
4135
    .password=&password,
 
 
4136
    .filename=strdup("/nonexistent"),
 
 
4137
    .cancelled_filenames = &(string_set){},
 
 
4139
    .current_time=¤t_time,
 
 
4141
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4142
  g_assert_true(quit_now);
 
 
4143
  g_assert_true(queue->next_run == 0);
 
 
4144
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4146
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
4148
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
4152
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
 
4153
                                            test_fixture *fixture,
 
 
4154
                                            __attribute__((unused))
 
 
4155
                                            gconstpointer user_data){
 
 
4156
  __attribute__((cleanup(cleanup_close)))
 
 
4157
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4158
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4159
  const mono_microsecs current_time = 0;
 
 
4162
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4164
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4165
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4167
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4169
    struct inotify_event event;
 
 
4170
    char name_buffer[NAME_MAX + 1];
 
 
4172
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4174
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4175
  ievent->mask = IN_CLOSE_WRITE;
 
 
4176
  ievent->len = sizeof(dummy_file_name);
 
 
4177
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4178
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4179
                              + sizeof(dummy_file_name));
 
 
4180
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4182
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4184
  bool quit_now = false;
 
 
4185
  buffer password = {};
 
 
4186
  bool mandos_client_exited = false;
 
 
4187
  bool password_is_read = false;
 
 
4188
  __attribute__((cleanup(cleanup_queue)))
 
 
4189
    task_queue *queue = create_queue();
 
 
4190
  g_assert_nonnull(queue);
 
 
4192
  task_context task = {
 
 
4193
    .func=read_inotify_event,
 
 
4196
    .quit_now=&quit_now,
 
 
4197
    .password=&password,
 
 
4198
    .filename=strdup("/nonexistent"),
 
 
4199
    .cancelled_filenames = &(string_set){},
 
 
4201
    .current_time=¤t_time,
 
 
4202
    .mandos_client_exited=&mandos_client_exited,
 
 
4203
    .password_is_read=&password_is_read,
 
 
4205
  task.func(task, queue);
 
 
4206
  g_assert_false(quit_now);
 
 
4207
  g_assert_true(queue->next_run != 0);
 
 
4208
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4210
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4211
        .func=read_inotify_event,
 
 
4214
        .quit_now=&quit_now,
 
 
4215
        .password=&password,
 
 
4216
        .filename=task.filename,
 
 
4217
        .cancelled_filenames=task.cancelled_filenames,
 
 
4218
        .current_time=¤t_time,
 
 
4219
        .mandos_client_exited=&mandos_client_exited,
 
 
4220
        .password_is_read=&password_is_read,
 
 
4223
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4224
                                   EPOLLIN | EPOLLRDHUP));
 
 
4226
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4228
  __attribute__((cleanup(cleanup_string)))
 
 
4229
    char *filename = NULL;
 
 
4230
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4231
                           dummy_file_name), >, 0);
 
 
4232
  g_assert_nonnull(filename);
 
 
4233
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4234
        .func=open_and_parse_question,
 
 
4237
        .question_filename=filename,
 
 
4238
        .password=&password,
 
 
4239
        .cancelled_filenames=task.cancelled_filenames,
 
 
4240
        .current_time=¤t_time,
 
 
4241
        .mandos_client_exited=&mandos_client_exited,
 
 
4242
        .password_is_read=&password_is_read,
 
 
4247
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
 
4248
                                         test_fixture *fixture,
 
 
4249
                                         __attribute__((unused))
 
 
4250
                                         gconstpointer user_data){
 
 
4251
  __attribute__((cleanup(cleanup_close)))
 
 
4252
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4253
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4254
  const mono_microsecs current_time = 0;
 
 
4257
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4259
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4260
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4262
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4264
    struct inotify_event event;
 
 
4265
    char name_buffer[NAME_MAX + 1];
 
 
4267
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4269
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4270
  ievent->mask = IN_MOVED_TO;
 
 
4271
  ievent->len = sizeof(dummy_file_name);
 
 
4272
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4273
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4274
                              + sizeof(dummy_file_name));
 
 
4275
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4277
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4279
  bool quit_now = false;
 
 
4280
  buffer password = {};
 
 
4281
  bool mandos_client_exited = false;
 
 
4282
  bool password_is_read = false;
 
 
4283
  __attribute__((cleanup(cleanup_queue)))
 
 
4284
    task_queue *queue = create_queue();
 
 
4285
  g_assert_nonnull(queue);
 
 
4287
  task_context task = {
 
 
4288
    .func=read_inotify_event,
 
 
4291
    .quit_now=&quit_now,
 
 
4292
    .password=&password,
 
 
4293
    .filename=strdup("/nonexistent"),
 
 
4294
    .cancelled_filenames = &(string_set){},
 
 
4296
    .current_time=¤t_time,
 
 
4297
    .mandos_client_exited=&mandos_client_exited,
 
 
4298
    .password_is_read=&password_is_read,
 
 
4300
  task.func(task, queue);
 
 
4301
  g_assert_false(quit_now);
 
 
4302
  g_assert_true(queue->next_run != 0);
 
 
4303
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4305
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4306
        .func=read_inotify_event,
 
 
4309
        .quit_now=&quit_now,
 
 
4310
        .password=&password,
 
 
4311
        .filename=task.filename,
 
 
4312
        .cancelled_filenames=task.cancelled_filenames,
 
 
4313
        .current_time=¤t_time,
 
 
4314
        .mandos_client_exited=&mandos_client_exited,
 
 
4315
        .password_is_read=&password_is_read,
 
 
4318
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4319
                                   EPOLLIN | EPOLLRDHUP));
 
 
4321
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4323
  __attribute__((cleanup(cleanup_string)))
 
 
4324
    char *filename = NULL;
 
 
4325
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4326
                           dummy_file_name), >, 0);
 
 
4327
  g_assert_nonnull(filename);
 
 
4328
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4329
        .func=open_and_parse_question,
 
 
4332
        .question_filename=filename,
 
 
4333
        .password=&password,
 
 
4334
        .cancelled_filenames=task.cancelled_filenames,
 
 
4335
        .current_time=¤t_time,
 
 
4336
        .mandos_client_exited=&mandos_client_exited,
 
 
4337
        .password_is_read=&password_is_read,
 
 
4342
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
 
4343
                                           test_fixture *fixture,
 
 
4344
                                           __attribute__((unused))
 
 
4345
                                           gconstpointer user_data){
 
 
4346
  __attribute__((cleanup(cleanup_close)))
 
 
4347
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4348
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4349
  __attribute__((cleanup(string_set_clear)))
 
 
4350
    string_set cancelled_filenames = {};
 
 
4351
  const mono_microsecs current_time = 0;
 
 
4354
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4356
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4357
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4359
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4361
    struct inotify_event event;
 
 
4362
    char name_buffer[NAME_MAX + 1];
 
 
4364
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4366
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4367
  ievent->mask = IN_MOVED_FROM;
 
 
4368
  ievent->len = sizeof(dummy_file_name);
 
 
4369
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4370
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4371
                              + sizeof(dummy_file_name));
 
 
4372
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4374
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4376
  bool quit_now = false;
 
 
4377
  buffer password = {};
 
 
4378
  bool mandos_client_exited = false;
 
 
4379
  bool password_is_read = false;
 
 
4380
  __attribute__((cleanup(cleanup_queue)))
 
 
4381
    task_queue *queue = create_queue();
 
 
4382
  g_assert_nonnull(queue);
 
 
4384
  task_context task = {
 
 
4385
    .func=read_inotify_event,
 
 
4388
    .quit_now=&quit_now,
 
 
4389
    .password=&password,
 
 
4390
    .filename=strdup("/nonexistent"),
 
 
4391
    .cancelled_filenames=&cancelled_filenames,
 
 
4392
    .current_time=¤t_time,
 
 
4393
    .mandos_client_exited=&mandos_client_exited,
 
 
4394
    .password_is_read=&password_is_read,
 
 
4396
  task.func(task, queue);
 
 
4397
  g_assert_false(quit_now);
 
 
4398
  g_assert_true(queue->next_run == 0);
 
 
4399
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4401
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4402
        .func=read_inotify_event,
 
 
4405
        .quit_now=&quit_now,
 
 
4406
        .password=&password,
 
 
4407
        .filename=task.filename,
 
 
4408
        .cancelled_filenames=&cancelled_filenames,
 
 
4409
        .current_time=¤t_time,
 
 
4410
        .mandos_client_exited=&mandos_client_exited,
 
 
4411
        .password_is_read=&password_is_read,
 
 
4414
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4415
                                   EPOLLIN | EPOLLRDHUP));
 
 
4417
  __attribute__((cleanup(cleanup_string)))
 
 
4418
    char *filename = NULL;
 
 
4419
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4420
                           dummy_file_name), >, 0);
 
 
4421
  g_assert_nonnull(filename);
 
 
4422
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4426
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
 
4427
                                              test_fixture *fixture,
 
 
4428
                                              __attribute__((unused))
 
 
4431
  __attribute__((cleanup(cleanup_close)))
 
 
4432
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4433
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4434
  __attribute__((cleanup(string_set_clear)))
 
 
4435
    string_set cancelled_filenames = {};
 
 
4436
  const mono_microsecs current_time = 0;
 
 
4439
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4441
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4442
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4444
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4446
    struct inotify_event event;
 
 
4447
    char name_buffer[NAME_MAX + 1];
 
 
4449
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4451
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4452
  ievent->mask = IN_DELETE;
 
 
4453
  ievent->len = sizeof(dummy_file_name);
 
 
4454
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4455
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4456
                              + sizeof(dummy_file_name));
 
 
4457
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4459
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4461
  bool quit_now = false;
 
 
4462
  buffer password = {};
 
 
4463
  bool mandos_client_exited = false;
 
 
4464
  bool password_is_read = false;
 
 
4465
  __attribute__((cleanup(cleanup_queue)))
 
 
4466
    task_queue *queue = create_queue();
 
 
4467
  g_assert_nonnull(queue);
 
 
4469
  task_context task = {
 
 
4470
    .func=read_inotify_event,
 
 
4473
    .quit_now=&quit_now,
 
 
4474
    .password=&password,
 
 
4475
    .filename=strdup("/nonexistent"),
 
 
4476
    .cancelled_filenames=&cancelled_filenames,
 
 
4477
    .current_time=¤t_time,
 
 
4478
    .mandos_client_exited=&mandos_client_exited,
 
 
4479
    .password_is_read=&password_is_read,
 
 
4481
  task.func(task, queue);
 
 
4482
  g_assert_false(quit_now);
 
 
4483
  g_assert_true(queue->next_run == 0);
 
 
4484
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4486
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4487
        .func=read_inotify_event,
 
 
4490
        .quit_now=&quit_now,
 
 
4491
        .password=&password,
 
 
4492
        .filename=task.filename,
 
 
4493
        .cancelled_filenames=&cancelled_filenames,
 
 
4494
        .current_time=¤t_time,
 
 
4495
        .mandos_client_exited=&mandos_client_exited,
 
 
4496
        .password_is_read=&password_is_read,
 
 
4499
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4500
                                   EPOLLIN | EPOLLRDHUP));
 
 
4502
  __attribute__((cleanup(cleanup_string)))
 
 
4503
    char *filename = NULL;
 
 
4504
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4505
                           dummy_file_name), >, 0);
 
 
4506
  g_assert_nonnull(filename);
 
 
4507
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4512
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
 
4513
                                               test_fixture *fixture,
 
 
4514
                                               __attribute__((unused))
 
 
4517
  __attribute__((cleanup(cleanup_close)))
 
 
4518
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4519
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4520
  const mono_microsecs current_time = 0;
 
 
4523
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4525
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4526
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4528
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4530
    struct inotify_event event;
 
 
4531
    char name_buffer[NAME_MAX + 1];
 
 
4533
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4535
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4536
  ievent->mask = IN_CLOSE_WRITE;
 
 
4537
  ievent->len = sizeof(dummy_file_name);
 
 
4538
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4539
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4540
                              + sizeof(dummy_file_name));
 
 
4541
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4543
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4545
  bool quit_now = false;
 
 
4546
  buffer password = {};
 
 
4547
  bool mandos_client_exited = false;
 
 
4548
  bool password_is_read = false;
 
 
4549
  __attribute__((cleanup(cleanup_queue)))
 
 
4550
    task_queue *queue = create_queue();
 
 
4551
  g_assert_nonnull(queue);
 
 
4553
  task_context task = {
 
 
4554
    .func=read_inotify_event,
 
 
4557
    .quit_now=&quit_now,
 
 
4558
    .password=&password,
 
 
4559
    .filename=strdup("/nonexistent"),
 
 
4560
    .cancelled_filenames = &(string_set){},
 
 
4562
    .current_time=¤t_time,
 
 
4563
    .mandos_client_exited=&mandos_client_exited,
 
 
4564
    .password_is_read=&password_is_read,
 
 
4566
  task.func(task, queue);
 
 
4567
  g_assert_false(quit_now);
 
 
4568
  g_assert_true(queue->next_run == 0);
 
 
4569
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4571
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4572
        .func=read_inotify_event,
 
 
4575
        .quit_now=&quit_now,
 
 
4576
        .password=&password,
 
 
4577
        .filename=task.filename,
 
 
4578
        .cancelled_filenames=task.cancelled_filenames,
 
 
4579
        .current_time=¤t_time,
 
 
4580
        .mandos_client_exited=&mandos_client_exited,
 
 
4581
        .password_is_read=&password_is_read,
 
 
4584
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4585
                                   EPOLLIN | EPOLLRDHUP));
 
 
4589
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
 
4590
                                            test_fixture *fixture,
 
 
4591
                                            __attribute__((unused))
 
 
4592
                                            gconstpointer user_data){
 
 
4593
  __attribute__((cleanup(cleanup_close)))
 
 
4594
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4595
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4596
  const mono_microsecs current_time = 0;
 
 
4599
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4601
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4602
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4604
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4606
    struct inotify_event event;
 
 
4607
    char name_buffer[NAME_MAX + 1];
 
 
4609
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4611
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4612
  ievent->mask = IN_MOVED_TO;
 
 
4613
  ievent->len = sizeof(dummy_file_name);
 
 
4614
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4615
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4616
                              + sizeof(dummy_file_name));
 
 
4617
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4619
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4621
  bool quit_now = false;
 
 
4622
  buffer password = {};
 
 
4623
  bool mandos_client_exited = false;
 
 
4624
  bool password_is_read = false;
 
 
4625
  __attribute__((cleanup(cleanup_queue)))
 
 
4626
    task_queue *queue = create_queue();
 
 
4627
  g_assert_nonnull(queue);
 
 
4629
  task_context task = {
 
 
4630
    .func=read_inotify_event,
 
 
4633
    .quit_now=&quit_now,
 
 
4634
    .password=&password,
 
 
4635
    .filename=strdup("/nonexistent"),
 
 
4636
    .cancelled_filenames = &(string_set){},
 
 
4638
    .current_time=¤t_time,
 
 
4639
    .mandos_client_exited=&mandos_client_exited,
 
 
4640
    .password_is_read=&password_is_read,
 
 
4642
  task.func(task, queue);
 
 
4643
  g_assert_false(quit_now);
 
 
4644
  g_assert_true(queue->next_run == 0);
 
 
4645
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4647
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4648
        .func=read_inotify_event,
 
 
4651
        .quit_now=&quit_now,
 
 
4652
        .password=&password,
 
 
4653
        .filename=task.filename,
 
 
4654
        .cancelled_filenames=task.cancelled_filenames,
 
 
4655
        .current_time=¤t_time,
 
 
4656
        .mandos_client_exited=&mandos_client_exited,
 
 
4657
        .password_is_read=&password_is_read,
 
 
4660
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4661
                                   EPOLLIN | EPOLLRDHUP));
 
 
4665
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
 
4666
                                              test_fixture *fixture,
 
 
4667
                                              __attribute__((unused))
 
 
4670
  __attribute__((cleanup(cleanup_close)))
 
 
4671
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4672
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4673
  __attribute__((cleanup(string_set_clear)))
 
 
4674
    string_set cancelled_filenames = {};
 
 
4675
  const mono_microsecs current_time = 0;
 
 
4678
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4680
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4681
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4683
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4685
    struct inotify_event event;
 
 
4686
    char name_buffer[NAME_MAX + 1];
 
 
4688
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4690
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4691
  ievent->mask = IN_MOVED_FROM;
 
 
4692
  ievent->len = sizeof(dummy_file_name);
 
 
4693
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4694
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4695
                              + sizeof(dummy_file_name));
 
 
4696
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4698
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4700
  bool quit_now = false;
 
 
4701
  buffer password = {};
 
 
4702
  bool mandos_client_exited = false;
 
 
4703
  bool password_is_read = false;
 
 
4704
  __attribute__((cleanup(cleanup_queue)))
 
 
4705
    task_queue *queue = create_queue();
 
 
4706
  g_assert_nonnull(queue);
 
 
4708
  task_context task = {
 
 
4709
    .func=read_inotify_event,
 
 
4712
    .quit_now=&quit_now,
 
 
4713
    .password=&password,
 
 
4714
    .filename=strdup("/nonexistent"),
 
 
4715
    .cancelled_filenames=&cancelled_filenames,
 
 
4716
    .current_time=¤t_time,
 
 
4717
    .mandos_client_exited=&mandos_client_exited,
 
 
4718
    .password_is_read=&password_is_read,
 
 
4720
  task.func(task, queue);
 
 
4721
  g_assert_false(quit_now);
 
 
4722
  g_assert_true(queue->next_run == 0);
 
 
4723
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4725
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4726
        .func=read_inotify_event,
 
 
4729
        .quit_now=&quit_now,
 
 
4730
        .password=&password,
 
 
4731
        .filename=task.filename,
 
 
4732
        .cancelled_filenames=&cancelled_filenames,
 
 
4733
        .current_time=¤t_time,
 
 
4734
        .mandos_client_exited=&mandos_client_exited,
 
 
4735
        .password_is_read=&password_is_read,
 
 
4738
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4739
                                   EPOLLIN | EPOLLRDHUP));
 
 
4741
  __attribute__((cleanup(cleanup_string)))
 
 
4742
    char *filename = NULL;
 
 
4743
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4744
                           dummy_file_name), >, 0);
 
 
4745
  g_assert_nonnull(filename);
 
 
4746
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4750
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
 
4751
                                               test_fixture *fixture,
 
 
4752
                                               __attribute__((unused))
 
 
4755
  __attribute__((cleanup(cleanup_close)))
 
 
4756
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4757
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4758
  __attribute__((cleanup(string_set_clear)))
 
 
4759
    string_set cancelled_filenames = {};
 
 
4760
  const mono_microsecs current_time = 0;
 
 
4763
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4765
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4766
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4768
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4770
    struct inotify_event event;
 
 
4771
    char name_buffer[NAME_MAX + 1];
 
 
4773
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4775
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4776
  ievent->mask = IN_DELETE;
 
 
4777
  ievent->len = sizeof(dummy_file_name);
 
 
4778
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4779
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4780
                              + sizeof(dummy_file_name));
 
 
4781
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4783
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4785
  bool quit_now = false;
 
 
4786
  buffer password = {};
 
 
4787
  bool mandos_client_exited = false;
 
 
4788
  bool password_is_read = false;
 
 
4789
  __attribute__((cleanup(cleanup_queue)))
 
 
4790
    task_queue *queue = create_queue();
 
 
4791
  g_assert_nonnull(queue);
 
 
4793
  task_context task = {
 
 
4794
    .func=read_inotify_event,
 
 
4797
    .quit_now=&quit_now,
 
 
4798
    .password=&password,
 
 
4799
    .filename=strdup("/nonexistent"),
 
 
4800
    .cancelled_filenames=&cancelled_filenames,
 
 
4801
    .current_time=¤t_time,
 
 
4802
    .mandos_client_exited=&mandos_client_exited,
 
 
4803
    .password_is_read=&password_is_read,
 
 
4805
  task.func(task, queue);
 
 
4806
  g_assert_false(quit_now);
 
 
4807
  g_assert_true(queue->next_run == 0);
 
 
4808
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4810
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4811
        .func=read_inotify_event,
 
 
4814
        .quit_now=&quit_now,
 
 
4815
        .password=&password,
 
 
4816
        .filename=task.filename,
 
 
4817
        .cancelled_filenames=&cancelled_filenames,
 
 
4818
        .current_time=¤t_time,
 
 
4819
        .mandos_client_exited=&mandos_client_exited,
 
 
4820
        .password_is_read=&password_is_read,
 
 
4823
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4824
                                   EPOLLIN | EPOLLRDHUP));
 
 
4826
  __attribute__((cleanup(cleanup_string)))
 
 
4827
    char *filename = NULL;
 
 
4828
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4829
                           dummy_file_name), >, 0);
 
 
4830
  g_assert_nonnull(filename);
 
 
4831
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4835
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
 
4836
                                         test_fixture *fixture,
 
 
4837
                                         __attribute__((unused))
 
 
4838
                                         gconstpointer user_data){
 
 
4839
  __attribute__((cleanup(cleanup_close)))
 
 
4840
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4841
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4842
  __attribute__((cleanup(string_set_clear)))
 
 
4843
    string_set cancelled_filenames = {};
 
 
4844
  bool mandos_client_exited = false;
 
 
4845
  bool password_is_read = false;
 
 
4846
  __attribute__((cleanup(cleanup_queue)))
 
 
4847
    task_queue *queue = create_queue();
 
 
4848
  g_assert_nonnull(queue);
 
 
4850
  char *const filename = strdup("/nonexistent");
 
 
4851
  g_assert_nonnull(filename);
 
 
4852
  task_context task = {
 
 
4853
    .func=open_and_parse_question,
 
 
4854
    .question_filename=filename,
 
 
4856
    .password=(buffer[]){{}},
 
 
4858
    .cancelled_filenames=&cancelled_filenames,
 
 
4859
    .current_time=(mono_microsecs[]){0},
 
 
4860
    .mandos_client_exited=&mandos_client_exited,
 
 
4861
    .password_is_read=&password_is_read,
 
 
4863
  task.func(task, queue);
 
 
4864
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4867
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
 
4868
                                             test_fixture *fixture,
 
 
4869
                                             __attribute__((unused))
 
 
4870
                                             gconstpointer user_data){
 
 
4871
  __attribute__((cleanup(cleanup_close)))
 
 
4872
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4873
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4874
  __attribute__((cleanup(string_set_clear)))
 
 
4875
    string_set cancelled_filenames = {};
 
 
4876
  buffer password = {};
 
 
4877
  bool mandos_client_exited = false;
 
 
4878
  bool password_is_read = false;
 
 
4879
  __attribute__((cleanup(cleanup_queue)))
 
 
4880
    task_queue *queue = create_queue();
 
 
4881
  g_assert_nonnull(queue);
 
 
4882
  const mono_microsecs current_time = 0;
 
 
4884
  char *filename = strdup("/proc/self/mem");
 
 
4885
  task_context task = {
 
 
4886
    .func=open_and_parse_question,
 
 
4887
    .question_filename=filename,
 
 
4889
    .password=&password,
 
 
4891
    .cancelled_filenames=&cancelled_filenames,
 
 
4892
    .current_time=¤t_time,
 
 
4893
    .mandos_client_exited=&mandos_client_exited,
 
 
4894
    .password_is_read=&password_is_read,
 
 
4896
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4897
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4901
test_open_and_parse_question_parse_error(__attribute__((unused))
 
 
4902
                                         test_fixture *fixture,
 
 
4903
                                         __attribute__((unused))
 
 
4904
                                         gconstpointer user_data){
 
 
4905
  __attribute__((cleanup(cleanup_close)))
 
 
4906
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4907
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4908
  __attribute__((cleanup(string_set_clear)))
 
 
4909
    string_set cancelled_filenames = {};
 
 
4910
  __attribute__((cleanup(cleanup_queue)))
 
 
4911
    task_queue *queue = create_queue();
 
 
4912
  g_assert_nonnull(queue);
 
 
4914
  __attribute__((cleanup(cleanup_string)))
 
 
4915
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4916
  g_assert_nonnull(tempfilename);
 
 
4917
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4918
  g_assert_cmpint(tempfile, >, 0);
 
 
4919
  const char bad_data[] = "this is bad syntax\n";
 
 
4920
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
 
4921
                  ==, sizeof(bad_data));
 
 
4922
  g_assert_cmpint(close(tempfile), ==, 0);
 
 
4924
  char *const filename = strdup(tempfilename);
 
 
4925
  g_assert_nonnull(filename);
 
 
4926
  task_context task = {
 
 
4927
    .func=open_and_parse_question,
 
 
4928
    .question_filename=filename,
 
 
4930
    .password=(buffer[]){{}},
 
 
4932
    .cancelled_filenames=&cancelled_filenames,
 
 
4933
    .current_time=(mono_microsecs[]){0},
 
 
4934
    .mandos_client_exited=(bool[]){false},
 
 
4935
    .password_is_read=(bool[]){false},
 
 
4937
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4939
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4941
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4945
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
 
4946
                                           test_fixture *fixture,
 
 
4947
                                           __attribute__((unused))
 
 
4948
                                           gconstpointer user_data){
 
 
4949
  __attribute__((cleanup(cleanup_close)))
 
 
4950
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4951
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4952
  __attribute__((cleanup(string_set_clear)))
 
 
4953
    string_set cancelled_filenames = {};
 
 
4954
  __attribute__((cleanup(cleanup_queue)))
 
 
4955
    task_queue *queue = create_queue();
 
 
4956
  g_assert_nonnull(queue);
 
 
4958
  __attribute__((cleanup(cleanup_string)))
 
 
4959
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4960
  g_assert_nonnull(tempfilename);
 
 
4961
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4962
  g_assert_cmpint(questionfile, >, 0);
 
 
4963
  FILE *qf = fdopen(questionfile, "w");
 
 
4964
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
 
4965
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4967
  char *const filename = strdup(tempfilename);
 
 
4968
  g_assert_nonnull(filename);
 
 
4969
  task_context task = {
 
 
4970
    .func=open_and_parse_question,
 
 
4971
    .question_filename=filename,
 
 
4973
    .password=(buffer[]){{}},
 
 
4975
    .cancelled_filenames=&cancelled_filenames,
 
 
4976
    .current_time=(mono_microsecs[]){0},
 
 
4977
    .mandos_client_exited=(bool[]){false},
 
 
4978
    .password_is_read=(bool[]){false},
 
 
4980
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4981
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4983
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4987
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
 
4988
                                            test_fixture *fixture,
 
 
4989
                                            __attribute__((unused))
 
 
4990
                                            gconstpointer user_data){
 
 
4991
  __attribute__((cleanup(cleanup_close)))
 
 
4992
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4993
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4994
  __attribute__((cleanup(string_set_clear)))
 
 
4995
    string_set cancelled_filenames = {};
 
 
4996
  __attribute__((cleanup(cleanup_queue)))
 
 
4997
    task_queue *queue = create_queue();
 
 
4998
  g_assert_nonnull(queue);
 
 
5000
  __attribute__((cleanup(cleanup_string)))
 
 
5001
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5002
  g_assert_nonnull(tempfilename);
 
 
5003
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5004
  g_assert_cmpint(questionfile, >, 0);
 
 
5005
  FILE *qf = fdopen(questionfile, "w");
 
 
5006
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
 
5007
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5009
  char *const filename = strdup(tempfilename);
 
 
5010
  g_assert_nonnull(filename);
 
 
5011
  task_context task = {
 
 
5012
    .func=open_and_parse_question,
 
 
5013
    .question_filename=filename,
 
 
5015
    .password=(buffer[]){{}},
 
 
5017
    .cancelled_filenames=&cancelled_filenames,
 
 
5018
    .current_time=(mono_microsecs[]){0},
 
 
5019
    .mandos_client_exited=(bool[]){false},
 
 
5020
    .password_is_read=(bool[]){false},
 
 
5022
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5023
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5025
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5029
void test_open_and_parse_question_nopid(__attribute__((unused))
 
 
5030
                                        test_fixture *fixture,
 
 
5031
                                        __attribute__((unused))
 
 
5032
                                        gconstpointer user_data){
 
 
5033
  __attribute__((cleanup(cleanup_close)))
 
 
5034
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5035
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5036
  __attribute__((cleanup(string_set_clear)))
 
 
5037
    string_set cancelled_filenames = {};
 
 
5038
  __attribute__((cleanup(cleanup_queue)))
 
 
5039
    task_queue *queue = create_queue();
 
 
5040
  g_assert_nonnull(queue);
 
 
5042
  __attribute__((cleanup(cleanup_string)))
 
 
5043
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5044
  g_assert_nonnull(tempfilename);
 
 
5045
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5046
  g_assert_cmpint(questionfile, >, 0);
 
 
5047
  FILE *qf = fdopen(questionfile, "w");
 
 
5048
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
 
5049
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5051
  char *const filename = strdup(tempfilename);
 
 
5052
  g_assert_nonnull(filename);
 
 
5053
  task_context task = {
 
 
5054
    .func=open_and_parse_question,
 
 
5055
    .question_filename=filename,
 
 
5057
    .password=(buffer[]){{}},
 
 
5059
    .cancelled_filenames=&cancelled_filenames,
 
 
5060
    .current_time=(mono_microsecs[]){0},
 
 
5061
    .mandos_client_exited=(bool[]){false},
 
 
5062
    .password_is_read=(bool[]){false},
 
 
5064
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5065
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5067
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5071
void test_open_and_parse_question_badpid(__attribute__((unused))
 
 
5072
                                         test_fixture *fixture,
 
 
5073
                                         __attribute__((unused))
 
 
5074
                                         gconstpointer user_data){
 
 
5075
  __attribute__((cleanup(cleanup_close)))
 
 
5076
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5077
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5078
  __attribute__((cleanup(string_set_clear)))
 
 
5079
    string_set cancelled_filenames = {};
 
 
5080
  __attribute__((cleanup(cleanup_queue)))
 
 
5081
    task_queue *queue = create_queue();
 
 
5082
  g_assert_nonnull(queue);
 
 
5084
  __attribute__((cleanup(cleanup_string)))
 
 
5085
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5086
  g_assert_nonnull(tempfilename);
 
 
5087
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5088
  g_assert_cmpint(questionfile, >, 0);
 
 
5089
  FILE *qf = fdopen(questionfile, "w");
 
 
5090
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
 
5092
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5094
  char *const filename = strdup(tempfilename);
 
 
5095
  g_assert_nonnull(filename);
 
 
5096
  task_context task = {
 
 
5097
    .func=open_and_parse_question,
 
 
5098
    .question_filename=filename,
 
 
5100
    .password=(buffer[]){{}},
 
 
5102
    .cancelled_filenames=&cancelled_filenames,
 
 
5103
    .current_time=(mono_microsecs[]){0},
 
 
5104
    .mandos_client_exited=(bool[]){false},
 
 
5105
    .password_is_read=(bool[]){false},
 
 
5107
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5108
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5110
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5114
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
 
5115
                                         test_fixture *fixture,
 
 
5116
                                         __attribute__((unused))
 
 
5117
                                         gconstpointer user_data){
 
 
5118
  __attribute__((cleanup(cleanup_close)))
 
 
5119
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5120
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5121
  __attribute__((cleanup(string_set_clear)))
 
 
5122
    string_set cancelled_filenames = {};
 
 
5123
  buffer password = {};
 
 
5124
  bool mandos_client_exited = false;
 
 
5125
  bool password_is_read = false;
 
 
5126
  __attribute__((cleanup(cleanup_queue)))
 
 
5127
    task_queue *queue = create_queue();
 
 
5128
  g_assert_nonnull(queue);
 
 
5129
  const mono_microsecs current_time = 0;
 
 
5131
  /* Find value of sysctl kernel.pid_max */
 
 
5132
  uintmax_t pid_max = 0;
 
 
5133
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
 
5134
  g_assert_nonnull(sysctl_pid_max);
 
 
5135
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
 
5137
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
 
5139
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
 
5140
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
 
5142
  __attribute__((cleanup(cleanup_string)))
 
 
5143
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5144
  g_assert_nonnull(tempfilename);
 
 
5145
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5146
  g_assert_cmpint(questionfile, >, 0);
 
 
5147
  FILE *qf = fdopen(questionfile, "w");
 
 
5148
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5149
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
 
5151
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5153
  char *const question_filename = strdup(tempfilename);
 
 
5154
  g_assert_nonnull(question_filename);
 
 
5155
  task_context task = {
 
 
5156
    .func=open_and_parse_question,
 
 
5157
    .question_filename=question_filename,
 
 
5159
    .password=&password,
 
 
5160
    .filename=question_filename,
 
 
5161
    .cancelled_filenames=&cancelled_filenames,
 
 
5162
    .current_time=¤t_time,
 
 
5163
    .mandos_client_exited=&mandos_client_exited,
 
 
5164
    .password_is_read=&password_is_read,
 
 
5166
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5167
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5169
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5173
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
 
5174
                                         test_fixture *fixture,
 
 
5175
                                         __attribute__((unused))
 
 
5176
                                         gconstpointer user_data){
 
 
5177
  __attribute__((cleanup(cleanup_close)))
 
 
5178
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5179
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5180
  __attribute__((cleanup(string_set_clear)))
 
 
5181
    string_set cancelled_filenames = {};
 
 
5182
  buffer password = {};
 
 
5183
  bool mandos_client_exited = false;
 
 
5184
  bool password_is_read = false;
 
 
5185
  __attribute__((cleanup(cleanup_queue)))
 
 
5186
    task_queue *queue = create_queue();
 
 
5187
  g_assert_nonnull(queue);
 
 
5188
  const mono_microsecs current_time = 0;
 
 
5190
  __attribute__((cleanup(cleanup_string)))
 
 
5191
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5192
  g_assert_nonnull(tempfilename);
 
 
5193
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5194
  g_assert_cmpint(questionfile, >, 0);
 
 
5195
  FILE *qf = fdopen(questionfile, "w");
 
 
5196
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5197
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
 
5198
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5200
  char *const filename = strdup(tempfilename);
 
 
5201
  g_assert_nonnull(filename);
 
 
5202
  task_context task = {
 
 
5203
    .func=open_and_parse_question,
 
 
5204
    .question_filename=filename,
 
 
5206
    .password=&password,
 
 
5208
    .cancelled_filenames=&cancelled_filenames,
 
 
5209
    .current_time=¤t_time,
 
 
5210
    .mandos_client_exited=&mandos_client_exited,
 
 
5211
    .password_is_read=&password_is_read,
 
 
5213
  task.func(task, queue);
 
 
5214
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5216
  __attribute__((cleanup(cleanup_string)))
 
 
5217
    char *socket_filename = strdup("/nonexistent");
 
 
5218
  g_assert_nonnull(socket_filename);
 
 
5219
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5220
        .func=connect_question_socket,
 
 
5221
        .question_filename=tempfilename,
 
 
5222
        .filename=socket_filename,
 
 
5224
        .password=&password,
 
 
5225
        .current_time=¤t_time,
 
 
5226
        .mandos_client_exited=&mandos_client_exited,
 
 
5227
        .password_is_read=&password_is_read,
 
 
5230
  g_assert_true(queue->next_run != 0);
 
 
5232
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5236
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
 
5237
                                          test_fixture *fixture,
 
 
5238
                                          __attribute__((unused))
 
 
5239
                                          gconstpointer user_data){
 
 
5240
  __attribute__((cleanup(cleanup_close)))
 
 
5241
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5242
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5243
  __attribute__((cleanup(string_set_clear)))
 
 
5244
    string_set cancelled_filenames = {};
 
 
5245
  buffer password = {};
 
 
5246
  bool mandos_client_exited = false;
 
 
5247
  bool password_is_read = false;
 
 
5248
  __attribute__((cleanup(cleanup_queue)))
 
 
5249
    task_queue *queue = create_queue();
 
 
5250
  g_assert_nonnull(queue);
 
 
5251
  const mono_microsecs current_time = 0;
 
 
5253
  __attribute__((cleanup(cleanup_string)))
 
 
5254
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5255
  g_assert_nonnull(tempfilename);
 
 
5256
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5257
  g_assert_cmpint(questionfile, >, 0);
 
 
5258
  FILE *qf = fdopen(questionfile, "w");
 
 
5259
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5260
                          PRIuMAX "\nNotAfter=\n",
 
 
5261
                          (uintmax_t)getpid()), >, 0);
 
 
5262
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5264
  char *const filename = strdup(tempfilename);
 
 
5265
  g_assert_nonnull(filename);
 
 
5266
  task_context task = {
 
 
5267
    .func=open_and_parse_question,
 
 
5268
    .question_filename=filename,
 
 
5270
    .password=&password,
 
 
5272
    .cancelled_filenames=&cancelled_filenames,
 
 
5273
    .current_time=¤t_time,
 
 
5274
    .mandos_client_exited=&mandos_client_exited,
 
 
5275
    .password_is_read=&password_is_read,
 
 
5277
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5278
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5280
  __attribute__((cleanup(cleanup_string)))
 
 
5281
    char *socket_filename = strdup("/nonexistent");
 
 
5282
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5283
        .func=connect_question_socket,
 
 
5284
        .question_filename=tempfilename,
 
 
5285
        .filename=socket_filename,
 
 
5287
        .password=&password,
 
 
5288
        .current_time=¤t_time,
 
 
5289
        .mandos_client_exited=&mandos_client_exited,
 
 
5290
        .password_is_read=&password_is_read,
 
 
5292
  g_assert_true(queue->next_run != 0);
 
 
5294
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5298
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
 
5300
                                                  const mono_microsecs
 
 
5302
                                                  const mono_microsecs
 
 
5304
  __attribute__((cleanup(cleanup_close)))
 
 
5305
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5306
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5307
  __attribute__((cleanup(string_set_clear)))
 
 
5308
    string_set cancelled_filenames = {};
 
 
5309
  buffer password = {};
 
 
5310
  bool mandos_client_exited = false;
 
 
5311
  bool password_is_read = false;
 
 
5312
  __attribute__((cleanup(cleanup_queue)))
 
 
5313
    task_queue *queue = create_queue();
 
 
5314
  g_assert_nonnull(queue);
 
 
5315
  queue->next_run = next_queue_run;
 
 
5317
  __attribute__((cleanup(cleanup_string)))
 
 
5318
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5319
  g_assert_nonnull(tempfilename);
 
 
5320
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5321
  g_assert_cmpint(questionfile, >, 0);
 
 
5322
  FILE *qf = fdopen(questionfile, "w");
 
 
5323
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5324
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
 
5325
                          (uintmax_t)getpid(), notafter), >, 0);
 
 
5326
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5328
  char *const filename = strdup(tempfilename);
 
 
5329
  g_assert_nonnull(filename);
 
 
5330
  task_context task = {
 
 
5331
    .func=open_and_parse_question,
 
 
5332
    .question_filename=filename,
 
 
5334
    .password=&password,
 
 
5336
    .cancelled_filenames=&cancelled_filenames,
 
 
5337
    .current_time=¤t_time,
 
 
5338
    .mandos_client_exited=&mandos_client_exited,
 
 
5339
    .password_is_read=&password_is_read,
 
 
5341
  task.func(task, queue);
 
 
5343
  if(queue->length >= 1){
 
 
5344
    __attribute__((cleanup(cleanup_string)))
 
 
5345
      char *socket_filename = strdup("/nonexistent");
 
 
5346
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5347
          .func=connect_question_socket,
 
 
5348
          .filename=socket_filename,
 
 
5350
          .password=&password,
 
 
5351
          .current_time=¤t_time,
 
 
5352
          .cancelled_filenames=&cancelled_filenames,
 
 
5353
          .mandos_client_exited=&mandos_client_exited,
 
 
5354
          .password_is_read=&password_is_read,
 
 
5356
    g_assert_true(queue->next_run != 0);
 
 
5360
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5361
  } else if(current_time >= notafter) {
 
 
5362
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5364
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5365
          .func=cancel_old_question,
 
 
5366
          .question_filename=tempfilename,
 
 
5367
          .filename=tempfilename,
 
 
5369
          .cancelled_filenames=&cancelled_filenames,
 
 
5370
          .current_time=¤t_time,
 
 
5373
  g_assert_true(queue->next_run == 1);
 
 
5375
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5379
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
 
5380
                                        test_fixture *fixture,
 
 
5381
                                        __attribute__((unused))
 
 
5382
                                        gconstpointer user_data){
 
 
5383
  /* current_time, notafter, next_queue_run */
 
 
5384
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
 
5388
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
 
5389
                                        test_fixture *fixture,
 
 
5390
                                        __attribute__((unused))
 
 
5391
                                        gconstpointer user_data){
 
 
5392
  /* current_time, notafter, next_queue_run */
 
 
5393
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
 
5397
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
 
5398
                                          test_fixture *fixture,
 
 
5399
                                          __attribute__((unused))
 
 
5400
                                          gconstpointer user_data){
 
 
5401
  /* current_time, notafter, next_queue_run */
 
 
5402
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
 
5406
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
 
5407
                                          test_fixture *fixture,
 
 
5408
                                          __attribute__((unused))
 
 
5409
                                          gconstpointer user_data){
 
 
5410
  /* current_time, notafter, next_queue_run */
 
 
5411
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
 
5415
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
 
5416
                                            test_fixture *fixture,
 
 
5417
                                            __attribute__((unused))
 
 
5418
                                            gconstpointer user_data){
 
 
5419
  /* current_time, notafter, next_queue_run */
 
 
5420
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
 
5424
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
 
5425
                                           test_fixture *fixture,
 
 
5426
                                           __attribute__((unused))
 
 
5427
                                           gconstpointer user_data){
 
 
5428
  /* current_time, notafter, next_queue_run */
 
 
5429
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
 
5432
static void assert_cancel_old_question_param(const mono_microsecs
 
 
5434
                                             const mono_microsecs
 
 
5436
                                             const mono_microsecs
 
 
5438
                                             const mono_microsecs
 
 
5440
  __attribute__((cleanup(string_set_clear)))
 
 
5441
    string_set cancelled_filenames = {};
 
 
5442
  __attribute__((cleanup(cleanup_queue)))
 
 
5443
    task_queue *queue = create_queue();
 
 
5444
  g_assert_nonnull(queue);
 
 
5445
  queue->next_run = next_queue_run;
 
 
5447
  char *const question_filename = strdup("/nonexistent");
 
 
5448
  g_assert_nonnull(question_filename);
 
 
5449
  task_context task = {
 
 
5450
    .func=cancel_old_question,
 
 
5451
    .question_filename=question_filename,
 
 
5452
    .filename=question_filename,
 
 
5454
    .cancelled_filenames=&cancelled_filenames,
 
 
5455
    .current_time=¤t_time,
 
 
5457
  task.func(task, queue);
 
 
5459
  if(current_time >= notafter){
 
 
5460
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5461
    g_assert_true(string_set_contains(cancelled_filenames,
 
 
5464
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5465
          .func=cancel_old_question,
 
 
5466
          .question_filename=question_filename,
 
 
5467
          .filename=question_filename,
 
 
5469
          .cancelled_filenames=&cancelled_filenames,
 
 
5470
          .current_time=¤t_time,
 
 
5473
    g_assert_false(string_set_contains(cancelled_filenames,
 
 
5474
                                       question_filename));
 
 
5476
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
 
5477
                   (unsigned int)next_set_to);
 
 
5480
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
 
5481
                                           test_fixture *fixture,
 
 
5482
                                           __attribute__((unused))
 
 
5483
                                           gconstpointer user_data){
 
 
5484
  /* next_queue_run unset,
 
 
5485
     cancellation should happen because time has come,
 
 
5486
     next_queue_run should be unchanged */
 
 
5487
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5488
  assert_cancel_old_question_param(0, 1, 2, 0);
 
 
5491
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
 
5492
                                           test_fixture *fixture,
 
 
5493
                                           __attribute__((unused))
 
 
5494
                                           gconstpointer user_data){
 
 
5495
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
 
5496
     and current_time is not yet notafter or greater,
 
 
5497
     update value of next_queue_run to value of notafter */
 
 
5498
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5499
  assert_cancel_old_question_param(0, 2, 1, 2);
 
 
5502
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
 
5503
                                           test_fixture *fixture,
 
 
5504
                                           __attribute__((unused))
 
 
5505
                                           gconstpointer user_data){
 
 
5506
  /* next_queue_run 1,
 
 
5507
     cancellation should happen because time has come,
 
 
5508
     next_queue_run should be unchanged */
 
 
5509
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5510
  assert_cancel_old_question_param(1, 2, 3, 1);
 
 
5513
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
 
5514
                                           test_fixture *fixture,
 
 
5515
                                           __attribute__((unused))
 
 
5516
                                           gconstpointer user_data){
 
 
5517
  /* If next_queue_run is set,
 
 
5518
     and current_time is not yet notafter or greater,
 
 
5519
     and notafter is larger than next_queue_run
 
 
5520
     next_queue_run should be unchanged */
 
 
5521
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5522
  assert_cancel_old_question_param(1, 3, 2, 1);
 
 
5525
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
 
5526
                                           test_fixture *fixture,
 
 
5527
                                           __attribute__((unused))
 
 
5528
                                           gconstpointer user_data){
 
 
5529
  /* next_queue_run 2,
 
 
5530
     cancellation should happen because time has come,
 
 
5531
     next_queue_run should be unchanged */
 
 
5532
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5533
  assert_cancel_old_question_param(2, 1, 3, 2);
 
 
5536
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
 
5537
                                           test_fixture *fixture,
 
 
5538
                                           __attribute__((unused))
 
 
5539
                                           gconstpointer user_data){
 
 
5540
  /* If next_queue_run is set,
 
 
5541
     and current_time is not yet notafter or greater,
 
 
5542
     and notafter is larger than next_queue_run
 
 
5543
     next_queue_run should be unchanged */
 
 
5544
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5545
  assert_cancel_old_question_param(2, 3, 1, 2);
 
 
5548
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
 
5549
                                           test_fixture *fixture,
 
 
5550
                                           __attribute__((unused))
 
 
5551
                                           gconstpointer user_data){
 
 
5552
  /* next_queue_run 3,
 
 
5553
     cancellation should happen because time has come,
 
 
5554
     next_queue_run should be unchanged */
 
 
5555
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5556
  assert_cancel_old_question_param(3, 1, 2, 3);
 
 
5559
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
 
5560
                                           test_fixture *fixture,
 
 
5561
                                           __attribute__((unused))
 
 
5562
                                           gconstpointer user_data){
 
 
5563
  /* If next_queue_run is set,
 
 
5564
     and current_time is not yet notafter or greater,
 
 
5565
     and notafter is smaller than next_queue_run
 
 
5566
     update value of next_queue_run to value of notafter */
 
 
5567
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5568
  assert_cancel_old_question_param(3, 2, 1, 2);
 
 
5572
test_connect_question_socket_name_too_long(__attribute__((unused))
 
 
5573
                                           test_fixture *fixture,
 
 
5574
                                           __attribute__((unused))
 
 
5575
                                           gconstpointer user_data){
 
 
5576
  __attribute__((cleanup(cleanup_close)))
 
 
5577
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5578
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5579
  const char question_filename[] = "/nonexistent/question";
 
 
5580
  __attribute__((cleanup(string_set_clear)))
 
 
5581
    string_set cancelled_filenames = {};
 
 
5582
  __attribute__((cleanup(cleanup_queue)))
 
 
5583
    task_queue *queue = create_queue();
 
 
5584
  g_assert_nonnull(queue);
 
 
5585
  __attribute__((cleanup(cleanup_string)))
 
 
5586
    char *tempdir = make_temporary_directory();
 
 
5587
  g_assert_nonnull(tempdir);
 
 
5588
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
 
5589
  char socket_name[sizeof(unix_socket.sun_path)];
 
 
5590
  memset(socket_name, 'x', sizeof(socket_name));
 
 
5591
  socket_name[sizeof(socket_name)-1] = '\0';
 
 
5592
  char *filename = NULL;
 
 
5593
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5595
  g_assert_nonnull(filename);
 
 
5597
  task_context task = {
 
 
5598
    .func=connect_question_socket,
 
 
5599
    .question_filename=strdup(question_filename),
 
 
5601
    .password=(buffer[]){{}},
 
 
5603
    .cancelled_filenames=&cancelled_filenames,
 
 
5604
    .mandos_client_exited=(bool[]){false},
 
 
5605
    .password_is_read=(bool[]){false},
 
 
5606
    .current_time=(mono_microsecs[]){0},
 
 
5608
  g_assert_nonnull(task.question_filename);
 
 
5609
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5611
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5612
                                    question_filename));
 
 
5613
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5614
  g_assert_true(queue->next_run == 0);
 
 
5616
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5620
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
 
5621
                                               test_fixture *fixture,
 
 
5622
                                               __attribute__((unused))
 
 
5625
  __attribute__((cleanup(cleanup_close)))
 
 
5626
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5627
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5628
  const char question_filename[] = "/nonexistent/question";
 
 
5629
  __attribute__((cleanup(string_set_clear)))
 
 
5630
    string_set cancelled_filenames = {};
 
 
5631
  const mono_microsecs current_time = 3;
 
 
5632
  __attribute__((cleanup(cleanup_queue)))
 
 
5633
    task_queue *queue = create_queue();
 
 
5634
  g_assert_nonnull(queue);
 
 
5635
  __attribute__((cleanup(cleanup_string)))
 
 
5636
    char *tempdir = make_temporary_directory();
 
 
5637
  g_assert_nonnull(tempdir);
 
 
5638
  char socket_name[] = "nonexistent";
 
 
5639
  char *filename = NULL;
 
 
5640
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5642
  g_assert_nonnull(filename);
 
 
5644
  task_context task = {
 
 
5645
    .func=connect_question_socket,
 
 
5646
    .question_filename=strdup(question_filename),
 
 
5648
    .password=(buffer[]){{}},
 
 
5650
    .cancelled_filenames=&cancelled_filenames,
 
 
5651
    .mandos_client_exited=(bool[]){false},
 
 
5652
    .password_is_read=(bool[]){false},
 
 
5653
    .current_time=¤t_time,
 
 
5655
  g_assert_nonnull(task.question_filename);
 
 
5656
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5658
  g_assert_nonnull(find_matching_task(queue, task));
 
 
5660
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
5661
                                     question_filename));
 
 
5662
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5663
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5665
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5669
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
 
5670
                                            test_fixture *fixture,
 
 
5671
                                            __attribute__((unused))
 
 
5672
                                            gconstpointer user_data){
 
 
5673
  __attribute__((cleanup(cleanup_close)))
 
 
5674
    const int epoll_fd = open("/dev/null",
 
 
5675
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
5676
  __attribute__((cleanup(cleanup_string)))
 
 
5677
    char *const question_filename = strdup("/nonexistent/question");
 
 
5678
  g_assert_nonnull(question_filename);
 
 
5679
  __attribute__((cleanup(string_set_clear)))
 
 
5680
    string_set cancelled_filenames = {};
 
 
5681
  const mono_microsecs current_time = 5;
 
 
5682
  __attribute__((cleanup(cleanup_queue)))
 
 
5683
    task_queue *queue = create_queue();
 
 
5684
  g_assert_nonnull(queue);
 
 
5685
  __attribute__((cleanup(cleanup_string)))
 
 
5686
    char *tempdir = make_temporary_directory();
 
 
5687
  g_assert_nonnull(tempdir);
 
 
5688
  __attribute__((cleanup(cleanup_close)))
 
 
5689
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5690
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5691
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5692
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5693
  const char socket_name[] = "socket_name";
 
 
5694
  __attribute__((cleanup(cleanup_string)))
 
 
5695
    char *filename = NULL;
 
 
5696
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5698
  g_assert_nonnull(filename);
 
 
5699
  g_assert_cmpint((int)strlen(filename), <,
 
 
5700
                  (int)sizeof(sock_name.sun_path));
 
 
5701
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5702
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5703
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5704
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5705
  task_context task = {
 
 
5706
    .func=connect_question_socket,
 
 
5707
    .question_filename=strdup(question_filename),
 
 
5709
    .password=(buffer[]){{}},
 
 
5710
    .filename=strdup(filename),
 
 
5711
    .cancelled_filenames=&cancelled_filenames,
 
 
5712
    .mandos_client_exited=(bool[]){false},
 
 
5713
    .password_is_read=(bool[]){false},
 
 
5714
    .current_time=¤t_time,
 
 
5716
  g_assert_nonnull(task.question_filename);
 
 
5717
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5719
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5720
  const task_context *const added_task
 
 
5721
    = find_matching_task(queue, task);
 
 
5722
  g_assert_nonnull(added_task);
 
 
5723
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5725
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5726
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5730
void test_connect_question_socket_usable(__attribute__((unused))
 
 
5731
                                         test_fixture *fixture,
 
 
5732
                                         __attribute__((unused))
 
 
5733
                                         gconstpointer user_data){
 
 
5734
  __attribute__((cleanup(cleanup_close)))
 
 
5735
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5736
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5737
  __attribute__((cleanup(cleanup_string)))
 
 
5738
    char *const question_filename = strdup("/nonexistent/question");
 
 
5739
  g_assert_nonnull(question_filename);
 
 
5740
  __attribute__((cleanup(string_set_clear)))
 
 
5741
    string_set cancelled_filenames = {};
 
 
5742
  buffer password = {};
 
 
5743
  bool mandos_client_exited = false;
 
 
5744
  bool password_is_read = false;
 
 
5745
  const mono_microsecs current_time = 0;
 
 
5746
  __attribute__((cleanup(cleanup_queue)))
 
 
5747
    task_queue *queue = create_queue();
 
 
5748
  g_assert_nonnull(queue);
 
 
5749
  __attribute__((cleanup(cleanup_string)))
 
 
5750
    char *tempdir = make_temporary_directory();
 
 
5751
  g_assert_nonnull(tempdir);
 
 
5752
  __attribute__((cleanup(cleanup_close)))
 
 
5753
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5754
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5755
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5756
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5757
  const char socket_name[] = "socket_name";
 
 
5758
  __attribute__((cleanup(cleanup_string)))
 
 
5759
    char *filename = NULL;
 
 
5760
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5762
  g_assert_nonnull(filename);
 
 
5763
  g_assert_cmpint((int)strlen(filename), <,
 
 
5764
                  (int)sizeof(sock_name.sun_path));
 
 
5765
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5766
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5767
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5768
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5769
  task_context task = {
 
 
5770
    .func=connect_question_socket,
 
 
5771
    .question_filename=strdup(question_filename),
 
 
5773
    .password=&password,
 
 
5774
    .filename=strdup(filename),
 
 
5775
    .cancelled_filenames=&cancelled_filenames,
 
 
5776
    .mandos_client_exited=&mandos_client_exited,
 
 
5777
    .password_is_read=&password_is_read,
 
 
5778
    .current_time=¤t_time,
 
 
5780
  g_assert_nonnull(task.question_filename);
 
 
5781
  task.func(task, queue);
 
 
5783
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5784
  const task_context *const added_task
 
 
5785
    = find_matching_task(queue, (task_context){
 
 
5786
        .func=send_password_to_socket,
 
 
5787
        .question_filename=question_filename,
 
 
5790
        .password=&password,
 
 
5791
        .cancelled_filenames=&cancelled_filenames,
 
 
5792
        .mandos_client_exited=&mandos_client_exited,
 
 
5793
        .password_is_read=&password_is_read,
 
 
5794
        .current_time=¤t_time,
 
 
5796
  g_assert_nonnull(added_task);
 
 
5797
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5799
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5802
  const int fd = added_task->fd;
 
 
5803
  g_assert_cmpint(fd, >, 0);
 
 
5804
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
 
5807
  char write_data[PIPE_BUF];
 
 
5809
    /* Construct test password buffer */
 
 
5810
    /* Start with + since that is what the real procotol uses */
 
 
5811
    write_data[0] = '+';
 
 
5812
    /* Set a special character at string end just to mark the end */
 
 
5813
    write_data[sizeof(write_data)-2] = 'y';
 
 
5814
    /* Set NUL at buffer end, as suggested by the protocol */
 
 
5815
    write_data[sizeof(write_data)-1] = '\0';
 
 
5816
    /* Fill rest of password with 'x' */
 
 
5817
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
 
5818
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
 
5819
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
 
5822
  /* read from sock_fd */
 
 
5823
  char read_data[sizeof(write_data)];
 
 
5824
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
 
5825
                  ==, sizeof(read_data));
 
 
5827
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
 
5830
  /* writing to sock_fd should fail */
 
 
5831
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
 
5832
                       MSG_NOSIGNAL), <, 0);
 
 
5834
  /* reading from fd should fail */
 
 
5835
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
 
5836
                            MSG_NOSIGNAL), <, 0);
 
 
5838
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5839
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5843
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
 
5844
                                               test_fixture *fixture,
 
 
5845
                                               __attribute__((unused))
 
 
5848
  __attribute__((cleanup(cleanup_close)))
 
 
5849
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5850
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5851
  __attribute__((cleanup(cleanup_string)))
 
 
5852
    char *const question_filename = strdup("/nonexistent/question");
 
 
5853
  g_assert_nonnull(question_filename);
 
 
5854
  __attribute__((cleanup(cleanup_string)))
 
 
5855
    char *const filename = strdup("/nonexistent/socket");
 
 
5856
  g_assert_nonnull(filename);
 
 
5857
  __attribute__((cleanup(string_set_clear)))
 
 
5858
    string_set cancelled_filenames = {};
 
 
5859
  buffer password = {};
 
 
5860
  bool password_is_read = true;
 
 
5861
  __attribute__((cleanup(cleanup_queue)))
 
 
5862
    task_queue *queue = create_queue();
 
 
5863
  g_assert_nonnull(queue);
 
 
5865
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5866
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5868
  __attribute__((cleanup(cleanup_close)))
 
 
5869
    const int read_socket = socketfds[0];
 
 
5870
  const int write_socket = socketfds[1];
 
 
5871
  task_context task = {
 
 
5872
    .func=send_password_to_socket,
 
 
5873
    .question_filename=strdup(question_filename),
 
 
5874
    .filename=strdup(filename),
 
 
5877
    .password=&password,
 
 
5878
    .cancelled_filenames=&cancelled_filenames,
 
 
5879
    .mandos_client_exited=(bool[]){false},
 
 
5880
    .password_is_read=&password_is_read,
 
 
5881
    .current_time=(mono_microsecs[]){0},
 
 
5883
  g_assert_nonnull(task.question_filename);
 
 
5885
  task.func(task, queue);
 
 
5887
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5889
  const task_context *const added_task
 
 
5890
    = find_matching_task(queue, task);
 
 
5891
  g_assert_nonnull(added_task);
 
 
5892
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5893
  g_assert_true(password_is_read);
 
 
5895
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5896
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5901
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
 
5902
                                               test_fixture *fixture,
 
 
5903
                                               __attribute__((unused))
 
 
5906
  __attribute__((cleanup(cleanup_close)))
 
 
5907
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5908
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5909
  __attribute__((cleanup(cleanup_string)))
 
 
5910
    char *const question_filename = strdup("/nonexistent/question");
 
 
5911
  g_assert_nonnull(question_filename);
 
 
5912
  __attribute__((cleanup(cleanup_string)))
 
 
5913
    char *const filename = strdup("/nonexistent/socket");
 
 
5914
  __attribute__((cleanup(string_set_clear)))
 
 
5915
    string_set cancelled_filenames = {};
 
 
5916
  buffer password = {};
 
 
5917
  __attribute__((cleanup(cleanup_queue)))
 
 
5918
    task_queue *queue = create_queue();
 
 
5919
  g_assert_nonnull(queue);
 
 
5921
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5922
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5924
  __attribute__((cleanup(cleanup_close)))
 
 
5925
    const int read_socket = socketfds[0];
 
 
5926
  const int write_socket = socketfds[1];
 
 
5927
  task_context task = {
 
 
5928
    .func=send_password_to_socket,
 
 
5929
    .question_filename=strdup(question_filename),
 
 
5930
    .filename=strdup(filename),
 
 
5933
    .password=&password,
 
 
5934
    .cancelled_filenames=&cancelled_filenames,
 
 
5935
    .mandos_client_exited=(bool[]){false},
 
 
5936
    .password_is_read=(bool[]){false},
 
 
5937
    .current_time=(mono_microsecs[]){0},
 
 
5939
  g_assert_nonnull(task.question_filename);
 
 
5941
  task.func(task, queue);
 
 
5943
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5945
  const task_context *const added_task = find_matching_task(queue,
 
 
5947
  g_assert_nonnull(added_task);
 
 
5948
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5949
  g_assert_true(queue->next_run == 0);
 
 
5951
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5952
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5957
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
 
5958
                                           test_fixture *fixture,
 
 
5959
                                           __attribute__((unused))
 
 
5960
                                           gconstpointer user_data){
 
 
5962
  g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
 
 
5964
  __attribute__((cleanup(cleanup_close)))
 
 
5965
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5966
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5967
  const char question_filename[] = "/nonexistent/question";
 
 
5968
  char *const filename = strdup("/nonexistent/socket");
 
 
5969
  __attribute__((cleanup(string_set_clear)))
 
 
5970
    string_set cancelled_filenames = {};
 
 
5971
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
 
5972
  __attribute__((cleanup(cleanup_buffer)))
 
 
5974
    .data=malloc(oversized),
 
 
5976
    .allocated=oversized,
 
 
5978
  g_assert_nonnull(password.data);
 
 
5979
  if(mlock(password.data, password.allocated) != 0){
 
 
5980
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5982
  /* Construct test password buffer */
 
 
5983
  /* Start with + since that is what the real procotol uses */
 
 
5984
  password.data[0] = '+';
 
 
5985
  /* Set a special character at string end just to mark the end */
 
 
5986
  password.data[oversized-3] = 'y';
 
 
5987
  /* Set NUL at buffer end, as suggested by the protocol */
 
 
5988
  password.data[oversized-2] = '\0';
 
 
5989
  /* Fill rest of password with 'x' */
 
 
5990
  memset(password.data+1, 'x', oversized-3);
 
 
5992
  __attribute__((cleanup(cleanup_queue)))
 
 
5993
    task_queue *queue = create_queue();
 
 
5994
  g_assert_nonnull(queue);
 
 
5996
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5997
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5999
  __attribute__((cleanup(cleanup_close)))
 
 
6000
    const int read_socket = socketfds[0];
 
 
6001
  __attribute__((cleanup(cleanup_close)))
 
 
6002
    const int write_socket = socketfds[1];
 
 
6003
  task_context task = {
 
 
6004
    .func=send_password_to_socket,
 
 
6005
    .question_filename=strdup(question_filename),
 
 
6009
    .password=&password,
 
 
6010
    .cancelled_filenames=&cancelled_filenames,
 
 
6011
    .mandos_client_exited=(bool[]){true},
 
 
6012
    .password_is_read=(bool[]){true},
 
 
6013
    .current_time=(mono_microsecs[]){0},
 
 
6015
  g_assert_nonnull(task.question_filename);
 
 
6017
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6019
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6020
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
6021
                                    question_filename));
 
 
6025
static void test_send_password_to_socket_retry(__attribute__((unused))
 
 
6026
                                               test_fixture *fixture,
 
 
6027
                                               __attribute__((unused))
 
 
6030
  __attribute__((cleanup(cleanup_close)))
 
 
6031
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6032
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6033
  __attribute__((cleanup(cleanup_string)))
 
 
6034
    char *const question_filename = strdup("/nonexistent/question");
 
 
6035
  g_assert_nonnull(question_filename);
 
 
6036
  __attribute__((cleanup(cleanup_string)))
 
 
6037
    char *const filename = strdup("/nonexistent/socket");
 
 
6038
  g_assert_nonnull(filename);
 
 
6039
  __attribute__((cleanup(string_set_clear)))
 
 
6040
    string_set cancelled_filenames = {};
 
 
6041
  __attribute__((cleanup(cleanup_buffer)))
 
 
6042
    buffer password = {};
 
 
6044
  __attribute__((cleanup(cleanup_queue)))
 
 
6045
    task_queue *queue = create_queue();
 
 
6046
  g_assert_nonnull(queue);
 
 
6048
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6049
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6051
  __attribute__((cleanup(cleanup_close)))
 
 
6052
    const int read_socket = socketfds[0];
 
 
6053
  const int write_socket = socketfds[1];
 
 
6054
  /* Close the server side socket to force ECONNRESET on client */
 
 
6055
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6056
  task_context task = {
 
 
6057
    .func=send_password_to_socket,
 
 
6058
    .question_filename=strdup(question_filename),
 
 
6059
    .filename=strdup(filename),
 
 
6062
    .password=&password,
 
 
6063
    .cancelled_filenames=&cancelled_filenames,
 
 
6064
    .mandos_client_exited=(bool[]){true},
 
 
6065
    .password_is_read=(bool[]){true},
 
 
6066
    .current_time=(mono_microsecs[]){0},
 
 
6068
  g_assert_nonnull(task.question_filename);
 
 
6070
  task.func(task, queue);
 
 
6072
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6074
  const task_context *const added_task = find_matching_task(queue,
 
 
6076
  g_assert_nonnull(added_task);
 
 
6077
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6079
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
6084
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
 
6085
                                            test_fixture *fixture,
 
 
6086
                                            __attribute__((unused))
 
 
6087
                                            gconstpointer user_data){
 
 
6088
  __attribute__((cleanup(cleanup_close)))
 
 
6089
    const int epoll_fd = open("/dev/null",
 
 
6090
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6091
  __attribute__((cleanup(cleanup_string)))
 
 
6092
    char *const question_filename = strdup("/nonexistent/question");
 
 
6093
  g_assert_nonnull(question_filename);
 
 
6094
  __attribute__((cleanup(cleanup_string)))
 
 
6095
    char *const filename = strdup("/nonexistent/socket");
 
 
6096
  g_assert_nonnull(filename);
 
 
6097
  __attribute__((cleanup(string_set_clear)))
 
 
6098
    string_set cancelled_filenames = {};
 
 
6099
  __attribute__((cleanup(cleanup_buffer)))
 
 
6100
    buffer password = {};
 
 
6102
  const mono_microsecs current_time = 11;
 
 
6103
  __attribute__((cleanup(cleanup_queue)))
 
 
6104
    task_queue *queue = create_queue();
 
 
6105
  g_assert_nonnull(queue);
 
 
6107
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6108
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6110
  __attribute__((cleanup(cleanup_close)))
 
 
6111
    const int read_socket = socketfds[0];
 
 
6112
  const int write_socket = socketfds[1];
 
 
6113
  /* Close the server side socket to force ECONNRESET on client */
 
 
6114
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6115
  task_context task = {
 
 
6116
    .func=send_password_to_socket,
 
 
6117
    .question_filename=strdup(question_filename),
 
 
6118
    .filename=strdup(filename),
 
 
6121
    .password=&password,
 
 
6122
    .cancelled_filenames=&cancelled_filenames,
 
 
6123
    .mandos_client_exited=(bool[]){true},
 
 
6124
    .password_is_read=(bool[]){true},
 
 
6125
    .current_time=¤t_time,
 
 
6127
  g_assert_nonnull(task.question_filename);
 
 
6129
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6131
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6133
  const task_context *const added_task = find_matching_task(queue,
 
 
6135
  g_assert_nonnull(added_task);
 
 
6136
  g_assert_true(queue->next_run == current_time + 1000000);
 
 
6137
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6140
static void assert_send_password_to_socket_password(buffer password){
 
 
6141
  __attribute__((cleanup(cleanup_close)))
 
 
6142
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6143
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6144
  char *const question_filename = strdup("/nonexistent/question");
 
 
6145
  g_assert_nonnull(question_filename);
 
 
6146
  char *const filename = strdup("/nonexistent/socket");
 
 
6147
  g_assert_nonnull(filename);
 
 
6148
  __attribute__((cleanup(string_set_clear)))
 
 
6149
    string_set cancelled_filenames = {};
 
 
6151
  __attribute__((cleanup(cleanup_queue)))
 
 
6152
    task_queue *queue = create_queue();
 
 
6153
  g_assert_nonnull(queue);
 
 
6155
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6156
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6158
  __attribute__((cleanup(cleanup_close)))
 
 
6159
    const int read_socket = socketfds[0];
 
 
6160
  const int write_socket = socketfds[1];
 
 
6161
  task_context task = {
 
 
6162
    .func=send_password_to_socket,
 
 
6163
    .question_filename=question_filename,
 
 
6167
    .password=&password,
 
 
6168
    .cancelled_filenames=&cancelled_filenames,
 
 
6169
    .mandos_client_exited=(bool[]){true},
 
 
6170
    .password_is_read=(bool[]){true},
 
 
6171
    .current_time=(mono_microsecs[]){0},
 
 
6174
  char *expected_written_data = malloc(password.length + 2);
 
 
6175
  g_assert_nonnull(expected_written_data);
 
 
6176
  expected_written_data[0] = '+';
 
 
6177
  expected_written_data[password.length + 1] = '\0';
 
 
6178
  if(password.length > 0){
 
 
6179
    g_assert_nonnull(password.data);
 
 
6180
    memcpy(expected_written_data + 1, password.data, password.length);
 
 
6183
  task.func(task, queue);
 
 
6186
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
 
6187
                  (int)(password.length + 2));
 
 
6188
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6190
  g_assert_true(memcmp(expected_written_data, buf,
 
 
6191
                       password.length + 2) == 0);
 
 
6193
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
 
6195
  free(expected_written_data);
 
 
6199
test_send_password_to_socket_null_password(__attribute__((unused))
 
 
6200
                                           test_fixture *fixture,
 
 
6201
                                           __attribute__((unused))
 
 
6202
                                           gconstpointer user_data){
 
 
6203
  __attribute__((cleanup(cleanup_buffer)))
 
 
6204
    buffer password = {};
 
 
6205
  assert_send_password_to_socket_password(password);
 
 
6209
test_send_password_to_socket_empty_password(__attribute__((unused))
 
 
6210
                                            test_fixture *fixture,
 
 
6211
                                            __attribute__((unused))
 
 
6212
                                            gconstpointer user_data){
 
 
6213
  __attribute__((cleanup(cleanup_buffer)))
 
 
6215
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
 
6217
    .allocated=0,               /* deliberate lie */
 
 
6219
  g_assert_nonnull(password.data);
 
 
6220
  assert_send_password_to_socket_password(password);
 
 
6224
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
 
6225
                                            test_fixture *fixture,
 
 
6226
                                            __attribute__((unused))
 
 
6227
                                            gconstpointer user_data){
 
 
6228
  __attribute__((cleanup(cleanup_buffer)))
 
 
6234
  if(mlock(password.data, password.allocated) != 0){
 
 
6235
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6237
  assert_send_password_to_socket_password(password);
 
 
6241
test_send_password_to_socket_text_password(__attribute__((unused))
 
 
6242
                                           test_fixture *fixture,
 
 
6243
                                           __attribute__((unused))
 
 
6244
                                           gconstpointer user_data){
 
 
6245
  const char dummy_test_password[] = "dummy test password";
 
 
6246
  __attribute__((cleanup(cleanup_buffer)))
 
 
6248
    .data = strdup(dummy_test_password),
 
 
6249
    .length = strlen(dummy_test_password),
 
 
6250
    .allocated = sizeof(dummy_test_password),
 
 
6252
  if(mlock(password.data, password.allocated) != 0){
 
 
6253
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6255
  assert_send_password_to_socket_password(password);
 
 
6259
test_send_password_to_socket_binary_password(__attribute__((unused))
 
 
6260
                                             test_fixture *fixture,
 
 
6261
                                             __attribute__((unused))
 
 
6262
                                             gconstpointer user_data){
 
 
6263
  __attribute__((cleanup(cleanup_buffer)))
 
 
6269
  g_assert_nonnull(password.data);
 
 
6270
  if(mlock(password.data, password.allocated) != 0){
 
 
6271
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6273
  char c = 1;                   /* Start at 1, avoiding NUL */
 
 
6274
  for(int i=0; i < 255; i++){
 
 
6275
    password.data[i] = c++;
 
 
6277
  assert_send_password_to_socket_password(password);
 
 
6281
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
 
6282
                                              test_fixture *fixture,
 
 
6283
                                              __attribute__((unused))
 
 
6286
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
 
6287
  __attribute__((cleanup(cleanup_buffer)))
 
 
6289
    .data=malloc(sizeof(test_password)),
 
 
6290
    .length=sizeof(test_password),
 
 
6291
    .allocated=sizeof(test_password),
 
 
6293
  g_assert_nonnull(password.data);
 
 
6294
  if(mlock(password.data, password.allocated) !=0){
 
 
6295
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6297
  memcpy(password.data, test_password, password.allocated);
 
 
6298
  assert_send_password_to_socket_password(password);
 
 
6301
static bool assert_add_existing_questions_to_devnull(task_queue
 
 
6314
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
 
6315
                                               test_fixture *fixture,
 
 
6316
                                               __attribute__((unused))
 
 
6319
  __attribute__((cleanup(cleanup_queue)))
 
 
6320
    task_queue *queue = create_queue();
 
 
6321
  g_assert_nonnull(queue);
 
 
6322
  __attribute__((cleanup(cleanup_close)))
 
 
6323
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6324
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6325
  __attribute__((cleanup(string_set_clear)))
 
 
6326
    string_set cancelled_filenames = {};
 
 
6328
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6331
                  (buffer[]){{}}, /* password */
 
 
6332
                  &cancelled_filenames,
 
 
6333
                  (mono_microsecs[]){0}, /* current_time */
 
 
6334
                  (bool[]){false},       /* mandos_client_exited */
 
 
6335
                  (bool[]){false},       /* password_is_read */
 
 
6336
                  "/nonexistent"));      /* dirname */
 
 
6338
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6342
bool assert_add_existing_questions_to_devnull(task_queue
 
 
6349
                                              *cancelled_filenames,
 
 
6350
                                              const mono_microsecs
 
 
6351
                                              *const current_time,
 
 
6353
                                              mandos_client_exited,
 
 
6358
  __attribute__((cleanup(cleanup_close)))
 
 
6359
    const int devnull_fd = open("/dev/null",
 
 
6360
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6361
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
6362
  __attribute__((cleanup(cleanup_close)))
 
 
6363
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
6364
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
6365
  dup2(devnull_fd, STDERR_FILENO);
 
 
6366
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
 
6367
                                          cancelled_filenames,
 
 
6369
                                          mandos_client_exited,
 
 
6370
                                          password_is_read, dirname);
 
 
6371
  dup2(real_stderr_fd, STDERR_FILENO);
 
 
6376
void test_add_existing_questions_no_questions(__attribute__((unused))
 
 
6377
                                              test_fixture *fixture,
 
 
6378
                                              __attribute__((unused))
 
 
6381
  __attribute__((cleanup(cleanup_queue)))
 
 
6382
    task_queue *queue = create_queue();
 
 
6383
  g_assert_nonnull(queue);
 
 
6384
  __attribute__((cleanup(cleanup_close)))
 
 
6385
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6386
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6387
  __attribute__((cleanup(string_set_clear)))
 
 
6388
    string_set cancelled_filenames = {};
 
 
6389
  __attribute__((cleanup(cleanup_string)))
 
 
6390
    char *tempdir = make_temporary_directory();
 
 
6391
  g_assert_nonnull(tempdir);
 
 
6393
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6396
                  (buffer[]){{}}, /* password */
 
 
6397
                  &cancelled_filenames,
 
 
6398
                  (mono_microsecs[]){0}, /* current_time */
 
 
6399
                  (bool[]){false},       /* mandos_client_exited */
 
 
6400
                  (bool[]){false},       /* password_is_read */
 
 
6403
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6405
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6408
static char *make_question_file_in_directory(const char *const);
 
 
6411
void test_add_existing_questions_one_question(__attribute__((unused))
 
 
6412
                                              test_fixture *fixture,
 
 
6413
                                              __attribute__((unused))
 
 
6416
  __attribute__((cleanup(cleanup_queue)))
 
 
6417
    task_queue *queue = create_queue();
 
 
6418
  g_assert_nonnull(queue);
 
 
6419
  __attribute__((cleanup(cleanup_close)))
 
 
6420
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6421
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6422
  __attribute__((cleanup(cleanup_buffer)))
 
 
6423
    buffer password = {};
 
 
6424
  __attribute__((cleanup(string_set_clear)))
 
 
6425
    string_set cancelled_filenames = {};
 
 
6426
  const mono_microsecs current_time = 0;
 
 
6427
  bool mandos_client_exited = false;
 
 
6428
  bool password_is_read = false;
 
 
6429
  __attribute__((cleanup(cleanup_string)))
 
 
6430
    char *tempdir = make_temporary_directory();
 
 
6431
  g_assert_nonnull(tempdir);
 
 
6432
  __attribute__((cleanup(cleanup_string)))
 
 
6433
    char *question_filename
 
 
6434
    = make_question_file_in_directory(tempdir);
 
 
6435
  g_assert_nonnull(question_filename);
 
 
6437
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6441
                 &cancelled_filenames,
 
 
6443
                 &mandos_client_exited,
 
 
6447
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6449
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6450
        .func=open_and_parse_question,
 
 
6452
        .filename=question_filename,
 
 
6453
        .question_filename=question_filename,
 
 
6454
        .password=&password,
 
 
6455
        .cancelled_filenames=&cancelled_filenames,
 
 
6456
        .current_time=¤t_time,
 
 
6457
        .mandos_client_exited=&mandos_client_exited,
 
 
6458
        .password_is_read=&password_is_read,
 
 
6461
  g_assert_true(queue->next_run == 1);
 
 
6463
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6464
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6467
static char *make_question_file_in_directory(const char
 
 
6469
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
 
6473
void test_add_existing_questions_two_questions(__attribute__((unused))
 
 
6474
                                               test_fixture *fixture,
 
 
6475
                                               __attribute__((unused))
 
 
6478
  __attribute__((cleanup(cleanup_queue)))
 
 
6479
    task_queue *queue = create_queue();
 
 
6480
  g_assert_nonnull(queue);
 
 
6481
  __attribute__((cleanup(cleanup_close)))
 
 
6482
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6483
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6484
  __attribute__((cleanup(cleanup_buffer)))
 
 
6485
    buffer password = {};
 
 
6486
  __attribute__((cleanup(string_set_clear)))
 
 
6487
    string_set cancelled_filenames = {};
 
 
6488
  const mono_microsecs current_time = 0;
 
 
6489
  bool mandos_client_exited = false;
 
 
6490
  bool password_is_read = false;
 
 
6491
  __attribute__((cleanup(cleanup_string)))
 
 
6492
    char *tempdir = make_temporary_directory();
 
 
6493
  g_assert_nonnull(tempdir);
 
 
6494
  __attribute__((cleanup(cleanup_string)))
 
 
6495
    char *question_filename1
 
 
6496
    = make_question_file_in_directory(tempdir);
 
 
6497
  g_assert_nonnull(question_filename1);
 
 
6498
  __attribute__((cleanup(cleanup_string)))
 
 
6499
    char *question_filename2
 
 
6500
    = make_question_file_in_directory(tempdir);
 
 
6501
  g_assert_nonnull(question_filename2);
 
 
6503
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6507
                 &cancelled_filenames,
 
 
6509
                 &mandos_client_exited,
 
 
6513
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
6515
  g_assert_true(queue->next_run == 1);
 
 
6517
  __attribute__((cleanup(string_set_clear)))
 
 
6518
    string_set seen_questions = {};
 
 
6520
  bool queue_contains_question_opener(char *const question_filename){
 
 
6521
    return(find_matching_task(queue, (task_context){
 
 
6522
          .func=open_and_parse_question,
 
 
6524
          .question_filename=question_filename,
 
 
6525
          .password=&password,
 
 
6526
          .cancelled_filenames=&cancelled_filenames,
 
 
6527
          .current_time=¤t_time,
 
 
6528
          .mandos_client_exited=&mandos_client_exited,
 
 
6529
          .password_is_read=&password_is_read,
 
 
6533
  g_assert_true(queue_contains_question_opener(question_filename1));
 
 
6534
  g_assert_true(queue_contains_question_opener(question_filename2));
 
 
6536
  g_assert_true(queue->next_run == 1);
 
 
6538
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6539
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6540
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6544
test_add_existing_questions_non_questions(__attribute__((unused))
 
 
6545
                                          test_fixture *fixture,
 
 
6546
                                          __attribute__((unused))
 
 
6547
                                          gconstpointer user_data){
 
 
6548
  __attribute__((cleanup(cleanup_queue)))
 
 
6549
    task_queue *queue = create_queue();
 
 
6550
  g_assert_nonnull(queue);
 
 
6551
  __attribute__((cleanup(cleanup_close)))
 
 
6552
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6553
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6554
  __attribute__((cleanup(string_set_clear)))
 
 
6555
    string_set cancelled_filenames = {};
 
 
6556
  __attribute__((cleanup(cleanup_string)))
 
 
6557
    char *tempdir = make_temporary_directory();
 
 
6558
  g_assert_nonnull(tempdir);
 
 
6559
  __attribute__((cleanup(cleanup_string)))
 
 
6560
    char *question_filename1
 
 
6561
    = make_temporary_file_in_directory(tempdir);
 
 
6562
  g_assert_nonnull(question_filename1);
 
 
6563
  __attribute__((cleanup(cleanup_string)))
 
 
6564
    char *question_filename2
 
 
6565
    = make_temporary_file_in_directory(tempdir);
 
 
6566
  g_assert_nonnull(question_filename2);
 
 
6568
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6571
                  (buffer[]){{}}, /* password */
 
 
6572
                  &cancelled_filenames,
 
 
6573
                  (mono_microsecs[]){0}, /* current_time */
 
 
6574
                  (bool[]){false},       /* mandos_client_exited */
 
 
6575
                  (bool[]){false},       /* password_is_read */
 
 
6578
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6580
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6581
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6582
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6586
test_add_existing_questions_both_types(__attribute__((unused))
 
 
6587
                                       test_fixture *fixture,
 
 
6588
                                       __attribute__((unused))
 
 
6589
                                       gconstpointer user_data){
 
 
6590
  __attribute__((cleanup(cleanup_queue)))
 
 
6591
    task_queue *queue = create_queue();
 
 
6592
  g_assert_nonnull(queue);
 
 
6593
  __attribute__((cleanup(cleanup_close)))
 
 
6594
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6595
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6596
  __attribute__((cleanup(cleanup_buffer)))
 
 
6597
    buffer password = {};
 
 
6598
  __attribute__((cleanup(string_set_clear)))
 
 
6599
    string_set cancelled_filenames = {};
 
 
6600
  const mono_microsecs current_time = 0;
 
 
6601
  bool mandos_client_exited = false;
 
 
6602
  bool password_is_read = false;
 
 
6603
  __attribute__((cleanup(cleanup_string)))
 
 
6604
    char *tempdir = make_temporary_directory();
 
 
6605
  g_assert_nonnull(tempdir);
 
 
6606
  __attribute__((cleanup(cleanup_string)))
 
 
6607
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
 
6608
  g_assert_nonnull(tempfilename1);
 
 
6609
  __attribute__((cleanup(cleanup_string)))
 
 
6610
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
 
6611
  g_assert_nonnull(tempfilename2);
 
 
6612
  __attribute__((cleanup(cleanup_string)))
 
 
6613
    char *question_filename
 
 
6614
    = make_question_file_in_directory(tempdir);
 
 
6615
  g_assert_nonnull(question_filename);
 
 
6617
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6621
                 &cancelled_filenames,
 
 
6623
                 &mandos_client_exited,
 
 
6627
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6629
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6630
        .func=open_and_parse_question,
 
 
6632
        .filename=question_filename,
 
 
6633
        .question_filename=question_filename,
 
 
6634
        .password=&password,
 
 
6635
        .cancelled_filenames=&cancelled_filenames,
 
 
6636
        .current_time=¤t_time,
 
 
6637
        .mandos_client_exited=&mandos_client_exited,
 
 
6638
        .password_is_read=&password_is_read,
 
 
6641
  g_assert_true(queue->next_run == 1);
 
 
6643
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
 
6644
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
 
6645
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6646
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6649
static void test_wait_for_event_timeout(__attribute__((unused))
 
 
6650
                                        test_fixture *fixture,
 
 
6651
                                        __attribute__((unused))
 
 
6652
                                        gconstpointer user_data){
 
 
6653
  __attribute__((cleanup(cleanup_close)))
 
 
6654
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6655
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6657
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
 
6660
static void test_wait_for_event_event(__attribute__((unused))
 
 
6661
                                      test_fixture *fixture,
 
 
6662
                                      __attribute__((unused))
 
 
6663
                                      gconstpointer user_data){
 
 
6664
  __attribute__((cleanup(cleanup_close)))
 
 
6665
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6666
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6668
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6669
  __attribute__((cleanup(cleanup_close)))
 
 
6670
    const int read_pipe = pipefds[0];
 
 
6671
  __attribute__((cleanup(cleanup_close)))
 
 
6672
    const int write_pipe = pipefds[1];
 
 
6673
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
 
6674
                            &(struct epoll_event)
 
 
6675
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
 
6676
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
 
6678
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6681
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
 
6682
                                        __attribute__((unused))
 
 
6683
                                        gconstpointer user_data){
 
 
6684
  const pid_t pid = fork();
 
 
6685
  if(pid == 0){         /* Child */
 
 
6686
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
6687
      _exit(EXIT_FAILURE);
 
 
6689
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
6690
      _exit(EXIT_FAILURE);
 
 
6694
  g_assert_true(pid != -1);
 
 
6695
  __attribute__((cleanup(cleanup_close)))
 
 
6696
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6697
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6699
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6702
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
 
6703
  g_assert_true(WIFEXITED(status));
 
 
6704
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
 
6707
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
 
6708
                                           test_fixture *fixture,
 
 
6709
                                           __attribute__((unused))
 
 
6710
                                           gconstpointer user_data){
 
 
6711
  __attribute__((cleanup(cleanup_queue)))
 
 
6712
    task_queue *queue = create_queue();
 
 
6713
  g_assert_nonnull(queue);
 
 
6714
  queue->next_run = 1;
 
 
6715
  __attribute__((cleanup(cleanup_close)))
 
 
6716
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6717
  __attribute__((cleanup(string_set_clear)))
 
 
6718
    string_set cancelled_filenames = {};
 
 
6719
  bool quit_now = false;
 
 
6721
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6722
  g_assert_false(quit_now);
 
 
6723
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
 
6727
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
 
6728
                                               test_fixture *fixture,
 
 
6729
                                               __attribute__((unused))
 
 
6732
  __attribute__((cleanup(cleanup_queue)))
 
 
6733
    task_queue *queue = create_queue();
 
 
6734
  g_assert_nonnull(queue);
 
 
6735
  __attribute__((cleanup(string_set_clear)))
 
 
6736
    string_set cancelled_filenames = {};
 
 
6737
  bool quit_now = false;
 
 
6738
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6739
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6740
                               question_filename));
 
 
6742
  g_assert_true(add_to_queue(queue,
 
 
6743
                             (task_context){ .func=dummy_func }));
 
 
6745
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6746
  g_assert_false(quit_now);
 
 
6747
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6748
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
6749
                                     question_filename));
 
 
6753
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
 
6754
                                              test_fixture *fixture,
 
 
6755
                                              __attribute__((unused))
 
 
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
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6766
  __attribute__((cleanup(cleanup_close)))
 
 
6767
    const int read_pipe = pipefds[0];
 
 
6768
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
6769
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6770
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6771
                               question_filename));
 
 
6772
  __attribute__((nonnull))
 
 
6773
    void quit_func(const task_context task,
 
 
6774
                   __attribute__((unused)) task_queue *const q){
 
 
6775
    g_assert_nonnull(task.quit_now);
 
 
6776
    *task.quit_now = true;
 
 
6778
  task_context task = {
 
 
6780
    .question_filename=strdup(question_filename),
 
 
6781
    .quit_now=&quit_now,
 
 
6784
  g_assert_nonnull(task.question_filename);
 
 
6786
  g_assert_true(add_to_queue(queue, task));
 
 
6788
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6789
  g_assert_false(quit_now);
 
 
6791
  /* read_pipe should be closed already */
 
 
6793
  bool read_pipe_closed = (close(read_pipe) == -1);
 
 
6794
  read_pipe_closed &= (errno == EBADF);
 
 
6795
  g_assert_true(read_pipe_closed);
 
 
6798
static void test_run_queue_one_task(__attribute__((unused))
 
 
6799
                                    test_fixture *fixture,
 
 
6800
                                    __attribute__((unused))
 
 
6801
                                    gconstpointer user_data){
 
 
6802
  __attribute__((cleanup(cleanup_queue)))
 
 
6803
    task_queue *queue = create_queue();
 
 
6804
  g_assert_nonnull(queue);
 
 
6805
  __attribute__((cleanup(string_set_clear)))
 
 
6806
    string_set cancelled_filenames = {};
 
 
6807
  bool quit_now = false;
 
 
6809
  __attribute__((nonnull))
 
 
6810
    void next_run_func(__attribute__((unused))
 
 
6811
                       const task_context task,
 
 
6812
                       task_queue *const q){
 
 
6816
  task_context task = {
 
 
6817
    .func=next_run_func,
 
 
6819
  g_assert_true(add_to_queue(queue, task));
 
 
6821
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6822
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6823
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6826
static void test_run_queue_two_tasks(__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
  queue->next_run = 1;
 
 
6834
  __attribute__((cleanup(string_set_clear)))
 
 
6835
    string_set cancelled_filenames = {};
 
 
6836
  bool quit_now = false;
 
 
6837
  bool mandos_client_exited = false;
 
 
6839
  __attribute__((nonnull))
 
 
6840
    void next_run_func(__attribute__((unused))
 
 
6841
                       const task_context task,
 
 
6842
                       task_queue *const q){
 
 
6846
  __attribute__((nonnull))
 
 
6847
    void exited_func(const task_context task,
 
 
6848
                     __attribute__((unused)) task_queue *const q){
 
 
6849
    *task.mandos_client_exited = true;
 
 
6852
  task_context task1 = {
 
 
6853
    .func=next_run_func,
 
 
6855
  g_assert_true(add_to_queue(queue, task1));
 
 
6857
  task_context task2 = {
 
 
6859
    .mandos_client_exited=&mandos_client_exited,
 
 
6861
  g_assert_true(add_to_queue(queue, task2));
 
 
6863
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6864
  g_assert_false(quit_now);
 
 
6865
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6866
  g_assert_true(mandos_client_exited);
 
 
6867
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6870
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
 
6871
                                          test_fixture *fixture,
 
 
6872
                                          __attribute__((unused))
 
 
6873
                                          gconstpointer user_data){
 
 
6874
  __attribute__((cleanup(cleanup_queue)))
 
 
6875
    task_queue *queue = create_queue();
 
 
6876
  g_assert_nonnull(queue);
 
 
6877
  __attribute__((cleanup(string_set_clear)))
 
 
6878
    string_set cancelled_filenames = {};
 
 
6879
  bool quit_now = false;
 
 
6880
  bool mandos_client_exited = false;
 
 
6881
  bool password_is_read = false;
 
 
6883
  __attribute__((nonnull))
 
 
6884
    void set_exited_func(const task_context task,
 
 
6885
                         __attribute__((unused)) task_queue *const q){
 
 
6886
    *task.mandos_client_exited = true;
 
 
6887
    *task.quit_now = true;
 
 
6889
  task_context task1 = {
 
 
6890
    .func=set_exited_func,
 
 
6891
    .quit_now=&quit_now,
 
 
6892
    .mandos_client_exited=&mandos_client_exited,
 
 
6894
  g_assert_true(add_to_queue(queue, task1));
 
 
6896
  __attribute__((nonnull))
 
 
6897
    void set_read_func(const task_context task,
 
 
6898
                       __attribute__((unused)) task_queue *const q){
 
 
6899
    *task.quit_now = true;
 
 
6900
    *task.password_is_read = true;
 
 
6902
  task_context task2 = {
 
 
6903
    .func=set_read_func,
 
 
6904
    .quit_now=&quit_now,
 
 
6905
    .password_is_read=&password_is_read,
 
 
6907
  g_assert_true(add_to_queue(queue, task2));
 
 
6909
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6910
  g_assert_true(quit_now);
 
 
6911
  g_assert_true(mandos_client_exited xor password_is_read);
 
 
6912
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6915
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
 
6916
                                             test_fixture *fixture,
 
 
6917
                                             __attribute__((unused))
 
 
6918
                                             gconstpointer user_data){
 
 
6919
  __attribute__((cleanup(cleanup_queue)))
 
 
6920
    task_queue *queue = create_queue();
 
 
6921
  g_assert_nonnull(queue);
 
 
6922
  __attribute__((cleanup(string_set_clear)))
 
 
6923
    string_set cancelled_filenames = {};
 
 
6925
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6926
  __attribute__((cleanup(cleanup_close)))
 
 
6927
    const int read_pipe = pipefds[0];
 
 
6928
  __attribute__((cleanup(cleanup_close)))
 
 
6929
    const int write_pipe = pipefds[1];
 
 
6930
  bool quit_now = false;
 
 
6932
  __attribute__((nonnull))
 
 
6933
    void read_func(const task_context task,
 
 
6934
                   __attribute__((unused)) task_queue *const q){
 
 
6935
    *task.quit_now = true;
 
 
6937
  task_context task1 = {
 
 
6939
    .quit_now=&quit_now,
 
 
6942
  g_assert_true(add_to_queue(queue, task1));
 
 
6944
  __attribute__((nonnull))
 
 
6945
    void write_func(const task_context task,
 
 
6946
                    __attribute__((unused)) task_queue *const q){
 
 
6947
    *task.quit_now = true;
 
 
6949
  task_context task2 = {
 
 
6951
    .quit_now=&quit_now,
 
 
6954
  g_assert_true(add_to_queue(queue, task2));
 
 
6956
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6957
  g_assert_true(quit_now);
 
 
6959
  /* Either read_pipe or write_pipe should be closed already */
 
 
6961
  bool close_read_pipe = (close(read_pipe) == -1);
 
 
6962
  close_read_pipe &= (errno == EBADF);
 
 
6964
  bool close_write_pipe = (close(write_pipe) == -1);
 
 
6965
  close_write_pipe &= (errno == EBADF);
 
 
6966
  g_assert_true(close_read_pipe xor close_write_pipe);
 
 
6967
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6970
static void test_setup_signal_handler(__attribute__((unused))
 
 
6971
                                      test_fixture *fixture,
 
 
6972
                                      __attribute__((unused))
 
 
6973
                                      gconstpointer user_data){
 
 
6974
  /* Save current SIGCHLD action, whatever it is */
 
 
6975
  struct sigaction expected_sigchld_action;
 
 
6976
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
6979
  /* Act; i.e. run the setup_signal_handler() function */
 
 
6980
  struct sigaction actual_old_sigchld_action;
 
 
6981
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
 
6983
  /* Check that the function correctly set "actual_old_sigchld_action"
 
 
6984
     to the same values as the previously saved
 
 
6985
     "expected_sigchld_action" */
 
 
6986
  /* Check member sa_handler */
 
 
6987
  g_assert_true(actual_old_sigchld_action.sa_handler
 
 
6988
                == expected_sigchld_action.sa_handler);
 
 
6989
  /* Check member sa_mask */
 
 
6990
  for(int signum = 1; signum < NSIG; signum++){
 
 
6991
    const int expected_old_block_state
 
 
6992
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
6993
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
6994
    const int actual_old_block_state
 
 
6995
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
 
6996
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
6997
    g_assert_cmpint(actual_old_block_state,
 
 
6998
                    ==, expected_old_block_state);
 
 
7000
  /* Check member sa_flags */
 
 
7001
  g_assert_true((actual_old_sigchld_action.sa_flags
 
 
7002
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7003
                == (expected_sigchld_action.sa_flags
 
 
7004
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7006
  /* Retrieve the current signal handler for SIGCHLD as set by
 
 
7007
     setup_signal_handler() */
 
 
7008
  struct sigaction actual_new_sigchld_action;
 
 
7009
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7010
                            &actual_new_sigchld_action), ==, 0);
 
 
7011
  /* Check that the signal handler (member sa_handler) is correctly
 
 
7012
     set to the "handle_sigchld" function */
 
 
7013
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
 
7014
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
 
7015
  g_assert_true(actual_new_sigchld_action.sa_handler
 
 
7017
  /* Check (in member sa_mask) that at least a handful of signals are
 
 
7018
     actually blocked during the signal handler */
 
 
7019
  for(int signum = 1; signum < NSIG; signum++){
 
 
7020
    int actual_new_block_state;
 
 
7026
      actual_new_block_state
 
 
7027
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
 
7028
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
 
7030
    case SIGKILL:               /* non-blockable */
 
 
7031
    case SIGSTOP:               /* non-blockable */
 
 
7032
    case SIGCHLD:               /* always blocked */
 
 
7037
  /* Check member sa_flags */
 
 
7038
  g_assert_true((actual_new_sigchld_action.sa_flags
 
 
7039
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7040
                == (SA_NOCLDSTOP | SA_RESTART));
 
 
7042
  /* Restore signal handler */
 
 
7043
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
 
7047
static void test_restore_signal_handler(__attribute__((unused))
 
 
7048
                                        test_fixture *fixture,
 
 
7049
                                        __attribute__((unused))
 
 
7050
                                        gconstpointer user_data){
 
 
7051
  /* Save current SIGCHLD action, whatever it is */
 
 
7052
  struct sigaction expected_sigchld_action;
 
 
7053
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7055
  /* Since we haven't established a signal handler yet, there should
 
 
7056
     not be one established.  But another test may have relied on
 
 
7057
     restore_signal_handler() to restore the signal handler, and if
 
 
7058
     restore_signal_handler() is buggy (which we should be prepared
 
 
7059
     for in this test) the signal handler may not have been restored
 
 
7060
     properly; check for this: */
 
 
7061
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
 
7063
  /* Establish a signal handler */
 
 
7064
  struct sigaction sigchld_action = {
 
 
7065
    .sa_handler=handle_sigchld,
 
 
7066
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
7068
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
 
7069
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
 
7071
  /* Act; i.e. run the restore_signal_handler() function */
 
 
7072
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
 
7074
  /* Retrieve the restored signal handler data */
 
 
7075
  struct sigaction actual_restored_sigchld_action;
 
 
7076
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7077
                            &actual_restored_sigchld_action), ==, 0);
 
 
7079
  /* Check that the function correctly restored the signal action, as
 
 
7080
     saved in "actual_restored_sigchld_action", to the same values as
 
 
7081
     the previously saved "expected_sigchld_action" */
 
 
7082
  /* Check member sa_handler */
 
 
7083
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
 
7084
                == expected_sigchld_action.sa_handler);
 
 
7085
  /* Check member sa_mask */
 
 
7086
  for(int signum = 1; signum < NSIG; signum++){
 
 
7087
    const int expected_old_block_state
 
 
7088
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7089
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7090
    const int actual_restored_block_state
 
 
7091
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
 
7092
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
 
7093
    g_assert_cmpint(actual_restored_block_state,
 
 
7094
                    ==, expected_old_block_state);
 
 
7096
  /* Check member sa_flags */
 
 
7097
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
 
7098
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7099
                == (expected_sigchld_action.sa_flags
 
 
7100
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7103
static void test_block_sigchld(__attribute__((unused))
 
 
7104
                               test_fixture *fixture,
 
 
7105
                               __attribute__((unused))
 
 
7106
                               gconstpointer user_data){
 
 
7107
  /* Save original signal mask */
 
 
7108
  sigset_t expected_sigmask;
 
 
7109
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
 
7112
  /* Make sure SIGCHLD is unblocked 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_UNBLOCK, &sigchld_sigmask,
 
 
7119
  /* Act; i.e. run the block_sigchld() function */
 
 
7120
  sigset_t actual_old_sigmask;
 
 
7121
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
 
7123
  /* Check the actual_old_sigmask; it should be the same as the
 
 
7124
     previously saved signal mask "expected_sigmask". */
 
 
7125
  for(int signum = 1; signum < NSIG; signum++){
 
 
7126
    const int expected_old_block_state
 
 
7127
      = sigismember(&expected_sigmask, signum);
 
 
7128
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7129
    const int actual_old_block_state
 
 
7130
      = sigismember(&actual_old_sigmask, signum);
 
 
7131
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7132
    g_assert_cmpint(actual_old_block_state,
 
 
7133
                    ==, expected_old_block_state);
 
 
7136
  /* Retrieve the newly set signal mask */
 
 
7137
  sigset_t actual_sigmask;
 
 
7138
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
 
7140
  /* SIGCHLD should be blocked */
 
 
7141
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
 
7143
  /* Restore signal mask */
 
 
7144
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
 
7148
static void test_restore_sigmask(__attribute__((unused))
 
 
7149
                                 test_fixture *fixture,
 
 
7150
                                 __attribute__((unused))
 
 
7151
                                 gconstpointer user_data){
 
 
7152
  /* Save original signal mask */
 
 
7153
  sigset_t orig_sigmask;
 
 
7154
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
 
7156
  /* Make sure SIGCHLD is blocked for this test */
 
 
7157
  sigset_t sigchld_sigmask;
 
 
7158
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7159
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7160
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
 
7163
  /* Act; i.e. run the restore_sigmask() function */
 
 
7164
  g_assert_true(restore_sigmask(&orig_sigmask));
 
 
7166
  /* Retrieve the newly restored signal mask */
 
 
7167
  sigset_t restored_sigmask;
 
 
7168
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
 
7171
  /* Check the restored_sigmask; it should be the same as the
 
 
7172
     previously saved signal mask "orig_sigmask". */
 
 
7173
  for(int signum = 1; signum < NSIG; signum++){
 
 
7174
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
 
7175
    g_assert_cmpint(orig_block_state, >=, 0);
 
 
7176
    const int restored_block_state = sigismember(&restored_sigmask,
 
 
7178
    g_assert_cmpint(restored_block_state, >=, 0);
 
 
7179
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
 
7182
  /* Restore signal mask */
 
 
7183
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
 
7187
static void test_parse_arguments_noargs(__attribute__((unused))
 
 
7188
                                        test_fixture *fixture,
 
 
7189
                                        __attribute__((unused))
 
 
7190
                                        gconstpointer user_data){
 
 
7194
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7196
  char *agent_directory = NULL;
 
 
7197
  char *helper_directory = NULL;
 
 
7200
  char *mandos_argz = NULL;
 
 
7201
  size_t mandos_argz_length = 0;
 
 
7203
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7204
                                &helper_directory, &user, &group,
 
 
7205
                                &mandos_argz, &mandos_argz_length));
 
 
7206
  g_assert_null(agent_directory);
 
 
7207
  g_assert_null(helper_directory);
 
 
7208
  g_assert_true(user == 0);
 
 
7209
  g_assert_true(group == 0);
 
 
7210
  g_assert_null(mandos_argz);
 
 
7211
  g_assert_true(mandos_argz_length == 0);
 
 
7213
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7218
__attribute__((nonnull))
 
 
7219
static bool parse_arguments_devnull(int argc, char *argv[],
 
 
7220
                                    const bool exit_failure,
 
 
7221
                                    char **agent_directory,
 
 
7222
                                    char **helper_directory,
 
 
7226
                                    size_t *mandos_argz_length){
 
 
7228
  FILE *real_stderr = stderr;
 
 
7229
  FILE *devnull = fopen("/dev/null", "we");
 
 
7230
  g_assert_nonnull(devnull);
 
 
7233
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
 
7235
                                   helper_directory, user, group,
 
 
7236
                                   mandos_argz, mandos_argz_length);
 
 
7237
  const error_t saved_errno = errno;
 
 
7239
  stderr = real_stderr;
 
 
7240
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
7242
  errno = saved_errno;
 
 
7247
static void test_parse_arguments_invalid(__attribute__((unused))
 
 
7248
                                         test_fixture *fixture,
 
 
7249
                                         __attribute__((unused))
 
 
7250
                                         gconstpointer user_data){
 
 
7253
    strdup("--invalid"),
 
 
7255
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7257
  char *agent_directory = NULL;
 
 
7258
  char *helper_directory = NULL;
 
 
7261
  char *mandos_argz = NULL;
 
 
7262
  size_t mandos_argz_length = 0;
 
 
7264
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7266
                                         &helper_directory, &user,
 
 
7267
                                         &group, &mandos_argz,
 
 
7268
                                         &mandos_argz_length));
 
 
7270
  g_assert_true(errno == EINVAL);
 
 
7271
  g_assert_null(agent_directory);
 
 
7272
  g_assert_null(helper_directory);
 
 
7273
  g_assert_null(mandos_argz);
 
 
7274
  g_assert_true(mandos_argz_length == 0);
 
 
7276
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7281
static void test_parse_arguments_long_dir(__attribute__((unused))
 
 
7282
                                          test_fixture *fixture,
 
 
7283
                                          __attribute__((unused))
 
 
7284
                                          gconstpointer user_data){
 
 
7287
    strdup("--agent-directory"),
 
 
7290
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7292
  __attribute__((cleanup(cleanup_string)))
 
 
7293
    char *agent_directory = NULL;
 
 
7294
  char *helper_directory = NULL;
 
 
7297
  __attribute__((cleanup(cleanup_string)))
 
 
7298
    char *mandos_argz = NULL;
 
 
7299
  size_t mandos_argz_length = 0;
 
 
7301
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7302
                                &helper_directory, &user, &group,
 
 
7303
                                &mandos_argz, &mandos_argz_length));
 
 
7305
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7306
  g_assert_null(helper_directory);
 
 
7307
  g_assert_true(user == 0);
 
 
7308
  g_assert_true(group == 0);
 
 
7309
  g_assert_null(mandos_argz);
 
 
7310
  g_assert_true(mandos_argz_length == 0);
 
 
7312
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7317
static void test_parse_arguments_short_dir(__attribute__((unused))
 
 
7318
                                           test_fixture *fixture,
 
 
7319
                                           __attribute__((unused))
 
 
7320
                                           gconstpointer user_data){
 
 
7326
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7328
  __attribute__((cleanup(cleanup_string)))
 
 
7329
    char *agent_directory = NULL;
 
 
7330
  char *helper_directory = NULL;
 
 
7333
  __attribute__((cleanup(cleanup_string)))
 
 
7334
    char *mandos_argz = NULL;
 
 
7335
  size_t mandos_argz_length = 0;
 
 
7337
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7338
                                &helper_directory, &user, &group,
 
 
7339
                                &mandos_argz, &mandos_argz_length));
 
 
7341
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7342
  g_assert_null(helper_directory);
 
 
7343
  g_assert_true(user == 0);
 
 
7344
  g_assert_true(group == 0);
 
 
7345
  g_assert_null(mandos_argz);
 
 
7346
  g_assert_true(mandos_argz_length == 0);
 
 
7348
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7354
void test_parse_arguments_helper_directory(__attribute__((unused))
 
 
7355
                                           test_fixture *fixture,
 
 
7356
                                           __attribute__((unused))
 
 
7357
                                           gconstpointer user_data){
 
 
7360
    strdup("--helper-directory"),
 
 
7363
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7365
  char *agent_directory = NULL;
 
 
7366
  __attribute__((cleanup(cleanup_string)))
 
 
7367
    char *helper_directory = NULL;
 
 
7370
  __attribute__((cleanup(cleanup_string)))
 
 
7371
    char *mandos_argz = NULL;
 
 
7372
  size_t mandos_argz_length = 0;
 
 
7374
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7375
                                &helper_directory, &user, &group,
 
 
7376
                                &mandos_argz, &mandos_argz_length));
 
 
7378
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7379
  g_assert_null(agent_directory);
 
 
7380
  g_assert_true(user == 0);
 
 
7381
  g_assert_true(group == 0);
 
 
7382
  g_assert_null(mandos_argz);
 
 
7383
  g_assert_true(mandos_argz_length == 0);
 
 
7385
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7391
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
 
7392
                                            test_fixture *fixture,
 
 
7393
                                            __attribute__((unused))
 
 
7394
                                            gconstpointer user_data){
 
 
7397
    strdup("--plugin-helper-dir"),
 
 
7400
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7402
  char *agent_directory = NULL;
 
 
7403
  __attribute__((cleanup(cleanup_string)))
 
 
7404
    char *helper_directory = NULL;
 
 
7407
  __attribute__((cleanup(cleanup_string)))
 
 
7408
    char *mandos_argz = NULL;
 
 
7409
  size_t mandos_argz_length = 0;
 
 
7411
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7412
                                &helper_directory, &user, &group,
 
 
7413
                                &mandos_argz, &mandos_argz_length));
 
 
7415
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7416
  g_assert_null(agent_directory);
 
 
7417
  g_assert_true(user == 0);
 
 
7418
  g_assert_true(group == 0);
 
 
7419
  g_assert_null(mandos_argz);
 
 
7420
  g_assert_true(mandos_argz_length == 0);
 
 
7422
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7427
static void test_parse_arguments_user(__attribute__((unused))
 
 
7428
                                      test_fixture *fixture,
 
 
7429
                                      __attribute__((unused))
 
 
7430
                                      gconstpointer user_data){
 
 
7436
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7438
  char *agent_directory = NULL;
 
 
7439
  __attribute__((cleanup(cleanup_string)))
 
 
7440
    char *helper_directory = NULL;
 
 
7443
  __attribute__((cleanup(cleanup_string)))
 
 
7444
    char *mandos_argz = NULL;
 
 
7445
  size_t mandos_argz_length = 0;
 
 
7447
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7448
                                &helper_directory, &user, &group,
 
 
7449
                                &mandos_argz, &mandos_argz_length));
 
 
7451
  g_assert_null(helper_directory);
 
 
7452
  g_assert_null(agent_directory);
 
 
7453
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
 
7454
  g_assert_true(group == 0);
 
 
7455
  g_assert_null(mandos_argz);
 
 
7456
  g_assert_true(mandos_argz_length == 0);
 
 
7458
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7463
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
 
7464
                                              test_fixture *fixture,
 
 
7465
                                              __attribute__((unused))
 
 
7473
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7475
  char *agent_directory = NULL;
 
 
7476
  __attribute__((cleanup(cleanup_string)))
 
 
7477
    char *helper_directory = NULL;
 
 
7480
  __attribute__((cleanup(cleanup_string)))
 
 
7481
    char *mandos_argz = NULL;
 
 
7482
  size_t mandos_argz_length = 0;
 
 
7484
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7486
                                         &helper_directory, &user,
 
 
7487
                                         &group, &mandos_argz,
 
 
7488
                                         &mandos_argz_length));
 
 
7490
  g_assert_null(helper_directory);
 
 
7491
  g_assert_null(agent_directory);
 
 
7492
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7493
  g_assert_true(group == 0);
 
 
7494
  g_assert_null(mandos_argz);
 
 
7495
  g_assert_true(mandos_argz_length == 0);
 
 
7497
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7503
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
 
7504
                                            test_fixture *fixture,
 
 
7505
                                            __attribute__((unused))
 
 
7506
                                            gconstpointer user_data){
 
 
7512
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7514
  char *agent_directory = NULL;
 
 
7515
  __attribute__((cleanup(cleanup_string)))
 
 
7516
    char *helper_directory = NULL;
 
 
7519
  __attribute__((cleanup(cleanup_string)))
 
 
7520
    char *mandos_argz = NULL;
 
 
7521
  size_t mandos_argz_length = 0;
 
 
7523
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7525
                                         &helper_directory, &user,
 
 
7526
                                         &group, &mandos_argz,
 
 
7527
                                         &mandos_argz_length));
 
 
7529
  g_assert_null(helper_directory);
 
 
7530
  g_assert_null(agent_directory);
 
 
7531
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7532
  g_assert_true(group == 0);
 
 
7533
  g_assert_null(mandos_argz);
 
 
7534
  g_assert_true(mandos_argz_length == 0);
 
 
7536
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7541
static void test_parse_arguments_group(__attribute__((unused))
 
 
7542
                                       test_fixture *fixture,
 
 
7543
                                       __attribute__((unused))
 
 
7544
                                       gconstpointer user_data){
 
 
7550
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7552
  char *agent_directory = NULL;
 
 
7553
  __attribute__((cleanup(cleanup_string)))
 
 
7554
    char *helper_directory = NULL;
 
 
7557
  __attribute__((cleanup(cleanup_string)))
 
 
7558
    char *mandos_argz = NULL;
 
 
7559
  size_t mandos_argz_length = 0;
 
 
7561
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7562
                                &helper_directory, &user, &group,
 
 
7563
                                &mandos_argz, &mandos_argz_length));
 
 
7565
  g_assert_null(helper_directory);
 
 
7566
  g_assert_null(agent_directory);
 
 
7567
  g_assert_true(user == 0);
 
 
7568
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
 
7569
  g_assert_null(mandos_argz);
 
 
7570
  g_assert_true(mandos_argz_length == 0);
 
 
7572
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7577
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
 
7578
                                               test_fixture *fixture,
 
 
7579
                                               __attribute__((unused))
 
 
7587
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7589
  char *agent_directory = NULL;
 
 
7590
  __attribute__((cleanup(cleanup_string)))
 
 
7591
    char *helper_directory = NULL;
 
 
7594
  __attribute__((cleanup(cleanup_string)))
 
 
7595
    char *mandos_argz = NULL;
 
 
7596
  size_t mandos_argz_length = 0;
 
 
7598
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7600
                                         &helper_directory, &user,
 
 
7601
                                         &group, &mandos_argz,
 
 
7602
                                         &mandos_argz_length));
 
 
7604
  g_assert_null(helper_directory);
 
 
7605
  g_assert_null(agent_directory);
 
 
7606
  g_assert_true(user == 0);
 
 
7607
  g_assert_true(group == 0);
 
 
7608
  g_assert_null(mandos_argz);
 
 
7609
  g_assert_true(mandos_argz_length == 0);
 
 
7611
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7617
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
 
7618
                                             test_fixture *fixture,
 
 
7619
                                             __attribute__((unused))
 
 
7620
                                             gconstpointer user_data){
 
 
7626
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7628
  char *agent_directory = NULL;
 
 
7629
  __attribute__((cleanup(cleanup_string)))
 
 
7630
    char *helper_directory = NULL;
 
 
7633
  __attribute__((cleanup(cleanup_string)))
 
 
7634
    char *mandos_argz = NULL;
 
 
7635
  size_t mandos_argz_length = 0;
 
 
7637
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7639
                                         &helper_directory, &user,
 
 
7640
                                         &group, &mandos_argz,
 
 
7641
                                         &mandos_argz_length));
 
 
7643
  g_assert_null(helper_directory);
 
 
7644
  g_assert_null(agent_directory);
 
 
7645
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
 
7646
  g_assert_true(group == 0);
 
 
7647
  g_assert_null(mandos_argz);
 
 
7648
  g_assert_true(mandos_argz_length == 0);
 
 
7650
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7655
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
 
7656
                                               test_fixture *fixture,
 
 
7657
                                               __attribute__((unused))
 
 
7662
    strdup("mandos-client"),
 
 
7664
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7666
  __attribute__((cleanup(cleanup_string)))
 
 
7667
    char *agent_directory = NULL;
 
 
7668
  __attribute__((cleanup(cleanup_string)))
 
 
7669
    char *helper_directory = NULL;
 
 
7672
  __attribute__((cleanup(cleanup_string)))
 
 
7673
    char *mandos_argz = NULL;
 
 
7674
  size_t mandos_argz_length = 0;
 
 
7676
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7677
                                &helper_directory, &user, &group,
 
 
7678
                                &mandos_argz, &mandos_argz_length));
 
 
7680
  g_assert_null(agent_directory);
 
 
7681
  g_assert_null(helper_directory);
 
 
7682
  g_assert_true(user == 0);
 
 
7683
  g_assert_true(group == 0);
 
 
7684
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
 
7685
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7686
                                            mandos_argz_length),
 
 
7689
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7694
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
 
7695
                                             test_fixture *fixture,
 
 
7696
                                             __attribute__((unused))
 
 
7697
                                             gconstpointer user_data){
 
 
7700
    strdup("mandos-client"),
 
 
7705
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7707
  __attribute__((cleanup(cleanup_string)))
 
 
7708
    char *agent_directory = NULL;
 
 
7709
  __attribute__((cleanup(cleanup_string)))
 
 
7710
    char *helper_directory = NULL;
 
 
7713
  __attribute__((cleanup(cleanup_string)))
 
 
7714
    char *mandos_argz = NULL;
 
 
7715
  size_t mandos_argz_length = 0;
 
 
7717
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7718
                                &helper_directory, &user, &group,
 
 
7719
                                &mandos_argz, &mandos_argz_length));
 
 
7721
  g_assert_null(agent_directory);
 
 
7722
  g_assert_null(helper_directory);
 
 
7723
  g_assert_true(user == 0);
 
 
7724
  g_assert_true(group == 0);
 
 
7725
  char *marg = mandos_argz;
 
 
7726
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7727
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7728
  g_assert_cmpstr(marg, ==, "one");
 
 
7729
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7730
  g_assert_cmpstr(marg, ==, "two");
 
 
7731
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7732
  g_assert_cmpstr(marg, ==, "three");
 
 
7733
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7734
                                            mandos_argz_length),
 
 
7737
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7742
static void test_parse_arguments_all_args(__attribute__((unused))
 
 
7743
                                          test_fixture *fixture,
 
 
7744
                                          __attribute__((unused))
 
 
7745
                                          gconstpointer user_data){
 
 
7748
    strdup("--agent-directory"),
 
 
7750
    strdup("--helper-directory"),
 
 
7756
    strdup("mandos-client"),
 
 
7761
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7763
  __attribute__((cleanup(cleanup_string)))
 
 
7764
    char *agent_directory = NULL;
 
 
7765
  __attribute__((cleanup(cleanup_string)))
 
 
7766
    char *helper_directory = NULL;
 
 
7769
  __attribute__((cleanup(cleanup_string)))
 
 
7770
    char *mandos_argz = NULL;
 
 
7771
  size_t mandos_argz_length = 0;
 
 
7773
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7774
                                &helper_directory, &user, &group,
 
 
7775
                                &mandos_argz, &mandos_argz_length));
 
 
7777
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7778
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7779
  g_assert_true(user == 1);
 
 
7780
  g_assert_true(group == 2);
 
 
7781
  char *marg = mandos_argz;
 
 
7782
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7783
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7784
  g_assert_cmpstr(marg, ==, "one");
 
 
7785
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7786
  g_assert_cmpstr(marg, ==, "two");
 
 
7787
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7788
  g_assert_cmpstr(marg, ==, "three");
 
 
7789
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7790
                                            mandos_argz_length),
 
 
7793
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7798
static void test_parse_arguments_mixed(__attribute__((unused))
 
 
7799
                                       test_fixture *fixture,
 
 
7800
                                       __attribute__((unused))
 
 
7801
                                       gconstpointer user_data){
 
 
7804
    strdup("mandos-client"),
 
 
7808
    strdup("--agent-directory"),
 
 
7812
    strdup("--helper-directory=/var/tmp"),
 
 
7814
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7816
  __attribute__((cleanup(cleanup_string)))
 
 
7817
    char *agent_directory = NULL;
 
 
7818
  __attribute__((cleanup(cleanup_string)))
 
 
7819
    char *helper_directory = NULL;
 
 
7822
  __attribute__((cleanup(cleanup_string)))
 
 
7823
    char *mandos_argz = NULL;
 
 
7824
  size_t mandos_argz_length = 0;
 
 
7826
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7827
                                &helper_directory, &user, &group,
 
 
7828
                                &mandos_argz, &mandos_argz_length));
 
 
7830
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7831
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7832
  g_assert_true(user == 1);
 
 
7833
  g_assert_true(group == 0);
 
 
7834
  char *marg = mandos_argz;
 
 
7835
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7836
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7837
  g_assert_cmpstr(marg, ==, "one");
 
 
7838
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7839
  g_assert_cmpstr(marg, ==, "two");
 
 
7840
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7841
  g_assert_cmpstr(marg, ==, "three");
 
 
7842
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7843
                                            mandos_argz_length),
 
 
7846
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7851
/* End of tests section */
 
 
7853
/* Test boilerplate section; New tests should be added to the test
 
 
7854
   suite definition here, in the "run_tests" function.
 
 
7856
   Finally, this section also contains the should_only_run_tests()
 
 
7857
   function used by main() for deciding if tests should be run or to
 
 
7860
__attribute__((cold))
 
 
7861
static bool run_tests(int argc, char *argv[]){
 
 
7862
  g_test_init(&argc, &argv, NULL);
 
 
7864
  /* A macro to add a test with no setup or teardown functions */
 
 
7865
#define test_add(testpath, testfunc)                    \
 
 
7867
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
 
7868
               (testfunc), NULL);                       \
 
 
7871
  /* Test the signal-related functions first, since some other tests
 
 
7872
     depend on these functions in their setups and teardowns */
 
 
7873
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
 
7874
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
 
7875
  test_add("/signal-handling/block", test_block_sigchld);
 
 
7876
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
 
7878
  /* Regular non-signal-related tests; these use no setups or
 
 
7880
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
 
7881
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
 
7882
  test_add("/parse_arguments/long-dir",
 
 
7883
           test_parse_arguments_long_dir);
 
 
7884
  test_add("/parse_arguments/short-dir",
 
 
7885
           test_parse_arguments_short_dir);
 
 
7886
  test_add("/parse_arguments/helper-directory",
 
 
7887
           test_parse_arguments_helper_directory);
 
 
7888
  test_add("/parse_arguments/plugin-helper-dir",
 
 
7889
           test_parse_arguments_plugin_helper_dir);
 
 
7890
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
 
7891
  test_add("/parse_arguments/user-invalid",
 
 
7892
           test_parse_arguments_user_invalid);
 
 
7893
  test_add("/parse_arguments/user-zero-invalid",
 
 
7894
           test_parse_arguments_user_zero_invalid);
 
 
7895
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
 
7896
  test_add("/parse_arguments/group-invalid",
 
 
7897
           test_parse_arguments_group_invalid);
 
 
7898
  test_add("/parse_arguments/group-zero-invalid",
 
 
7899
           test_parse_arguments_group_zero_invalid);
 
 
7900
  test_add("/parse_arguments/mandos-noargs",
 
 
7901
           test_parse_arguments_mandos_noargs);
 
 
7902
  test_add("/parse_arguments/mandos-args",
 
 
7903
           test_parse_arguments_mandos_args);
 
 
7904
  test_add("/parse_arguments/all-args",
 
 
7905
           test_parse_arguments_all_args);
 
 
7906
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
 
7907
  test_add("/queue/create", test_create_queue);
 
 
7908
  test_add("/queue/add", test_add_to_queue);
 
 
7909
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
 
7910
  test_add("/queue/has_question/empty",
 
 
7911
           test_queue_has_question_empty);
 
 
7912
  test_add("/queue/has_question/false",
 
 
7913
           test_queue_has_question_false);
 
 
7914
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
 
7915
  test_add("/queue/has_question/false2",
 
 
7916
           test_queue_has_question_false2);
 
 
7917
  test_add("/queue/has_question/true2",
 
 
7918
           test_queue_has_question_true2);
 
 
7919
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
 
7920
  test_add("/string_set/net-set-contains-nothing",
 
 
7921
           test_string_set_new_set_contains_nothing);
 
 
7922
  test_add("/string_set/with-added-string-contains-it",
 
 
7923
           test_string_set_with_added_string_contains_it);
 
 
7924
  test_add("/string_set/cleared-does-not-contain-string",
 
 
7925
           test_string_set_cleared_does_not_contain_str);
 
 
7926
  test_add("/string_set/swap/one-with-empty",
 
 
7927
           test_string_set_swap_one_with_empty);
 
 
7928
  test_add("/string_set/swap/empty-with-one",
 
 
7929
           test_string_set_swap_empty_with_one);
 
 
7930
  test_add("/string_set/swap/one-with-one",
 
 
7931
           test_string_set_swap_one_with_one);
 
 
7933
  /* A macro to add a test using the setup and teardown functions */
 
 
7934
#define test_add_st(path, func)                                 \
 
 
7936
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
 
7940
  /* Signal-related tests; these use setups and teardowns which
 
 
7941
     establish, during each test run, a signal handler for, and a
 
 
7942
     signal mask blocking, the SIGCHLD signal, just like main() */
 
 
7943
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
 
7944
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
 
7945
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
 
7946
  test_add_st("/run_queue/zeroes-next-run",
 
 
7947
              test_run_queue_zeroes_next_run);
 
 
7948
  test_add_st("/run_queue/clears-cancelled_filenames",
 
 
7949
              test_run_queue_clears_cancelled_filenames);
 
 
7950
  test_add_st("/run_queue/skips-cancelled-filenames",
 
 
7951
              test_run_queue_skips_cancelled_filenames);
 
 
7952
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
 
7953
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
 
7954
  test_add_st("/run_queue/two-tasks/quit",
 
 
7955
              test_run_queue_two_tasks_quit);
 
 
7956
  test_add_st("/run_queue/two-tasks-cleanup",
 
 
7957
              test_run_queue_two_tasks_cleanup);
 
 
7958
  test_add_st("/task-creators/start_mandos_client",
 
 
7959
              test_start_mandos_client);
 
 
7960
  test_add_st("/task-creators/start_mandos_client/execv",
 
 
7961
              test_start_mandos_client_execv);
 
 
7962
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
 
7963
              test_start_mandos_client_suid_euid);
 
 
7964
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
 
7965
              test_start_mandos_client_suid_egid);
 
 
7966
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
 
7967
              test_start_mandos_client_suid_ruid);
 
 
7968
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
 
7969
              test_start_mandos_client_suid_rgid);
 
 
7970
  test_add_st("/task-creators/start_mandos_client/read",
 
 
7971
              test_start_mandos_client_read);
 
 
7972
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
 
7973
              test_start_mandos_client_helper_directory);
 
 
7974
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
 
7975
              test_start_mandos_client_sigmask);
 
 
7976
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
 
7977
              test_wait_for_mandos_client_exit_badpid);
 
 
7978
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
 
7979
              test_wait_for_mandos_client_exit_noexit);
 
 
7980
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
 
7981
              test_wait_for_mandos_client_exit_success);
 
 
7982
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
 
7983
              test_wait_for_mandos_client_exit_failure);
 
 
7984
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
 
7985
              test_wait_for_mandos_client_exit_killed);
 
 
7986
  test_add_st("/task/read_mandos_client_output/readerror",
 
 
7987
              test_read_mandos_client_output_readerror);
 
 
7988
  test_add_st("/task/read_mandos_client_output/nodata",
 
 
7989
              test_read_mandos_client_output_nodata);
 
 
7990
  test_add_st("/task/read_mandos_client_output/eof",
 
 
7991
              test_read_mandos_client_output_eof);
 
 
7992
  test_add_st("/task/read_mandos_client_output/once",
 
 
7993
              test_read_mandos_client_output_once);
 
 
7994
  test_add_st("/task/read_mandos_client_output/malloc",
 
 
7995
              test_read_mandos_client_output_malloc);
 
 
7996
  test_add_st("/task/read_mandos_client_output/append",
 
 
7997
              test_read_mandos_client_output_append);
 
 
7998
  test_add_st("/task-creators/add_inotify_dir_watch",
 
 
7999
              test_add_inotify_dir_watch);
 
 
8000
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
 
8001
              test_add_inotify_dir_watch_fail);
 
 
8002
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
 
8003
              test_add_inotify_dir_watch_nondir);
 
 
8004
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
 
8005
              test_add_inotify_dir_watch_EAGAIN);
 
 
8006
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
 
8007
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
 
8008
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
 
8009
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
 
8010
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
 
8011
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
 
8012
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
 
8013
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
 
8014
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
 
8015
              test_add_inotify_dir_watch_IN_DELETE);
 
 
8016
  test_add_st("/task/read_inotify_event/readerror",
 
 
8017
              test_read_inotify_event_readerror);
 
 
8018
  test_add_st("/task/read_inotify_event/bad-epoll",
 
 
8019
              test_read_inotify_event_bad_epoll);
 
 
8020
  test_add_st("/task/read_inotify_event/nodata",
 
 
8021
              test_read_inotify_event_nodata);
 
 
8022
  test_add_st("/task/read_inotify_event/eof",
 
 
8023
              test_read_inotify_event_eof);
 
 
8024
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
 
8025
              test_read_inotify_event_IN_CLOSE_WRITE);
 
 
8026
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
 
8027
              test_read_inotify_event_IN_MOVED_TO);
 
 
8028
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
 
8029
              test_read_inotify_event_IN_MOVED_FROM);
 
 
8030
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
 
8031
              test_read_inotify_event_IN_DELETE);
 
 
8032
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
 
8033
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
 
8034
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
 
8035
              test_read_inotify_event_IN_MOVED_TO_badname);
 
 
8036
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
 
8037
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
 
8038
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
 
8039
              test_read_inotify_event_IN_DELETE_badname);
 
 
8040
  test_add_st("/task/open_and_parse_question/ENOENT",
 
 
8041
              test_open_and_parse_question_ENOENT);
 
 
8042
  test_add_st("/task/open_and_parse_question/EIO",
 
 
8043
              test_open_and_parse_question_EIO);
 
 
8044
  test_add_st("/task/open_and_parse_question/parse-error",
 
 
8045
              test_open_and_parse_question_parse_error);
 
 
8046
  test_add_st("/task/open_and_parse_question/nosocket",
 
 
8047
              test_open_and_parse_question_nosocket);
 
 
8048
  test_add_st("/task/open_and_parse_question/badsocket",
 
 
8049
              test_open_and_parse_question_badsocket);
 
 
8050
  test_add_st("/task/open_and_parse_question/nopid",
 
 
8051
              test_open_and_parse_question_nopid);
 
 
8052
  test_add_st("/task/open_and_parse_question/badpid",
 
 
8053
              test_open_and_parse_question_badpid);
 
 
8054
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
 
8055
              test_open_and_parse_question_noexist_pid);
 
 
8056
  test_add_st("/task/open_and_parse_question/no-notafter",
 
 
8057
              test_open_and_parse_question_no_notafter);
 
 
8058
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
 
8059
              test_open_and_parse_question_bad_notafter);
 
 
8060
  test_add_st("/task/open_and_parse_question/notafter-0",
 
 
8061
              test_open_and_parse_question_notafter_0);
 
 
8062
  test_add_st("/task/open_and_parse_question/notafter-1",
 
 
8063
              test_open_and_parse_question_notafter_1);
 
 
8064
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
 
8065
              test_open_and_parse_question_notafter_1_1);
 
 
8066
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
 
8067
              test_open_and_parse_question_notafter_1_2);
 
 
8068
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
 
8069
              test_open_and_parse_question_equal_notafter);
 
 
8070
  test_add_st("/task/open_and_parse_question/late-notafter",
 
 
8071
              test_open_and_parse_question_late_notafter);
 
 
8072
  test_add_st("/task/cancel_old_question/0-1-2",
 
 
8073
              test_cancel_old_question_0_1_2);
 
 
8074
  test_add_st("/task/cancel_old_question/0-2-1",
 
 
8075
              test_cancel_old_question_0_2_1);
 
 
8076
  test_add_st("/task/cancel_old_question/1-2-3",
 
 
8077
              test_cancel_old_question_1_2_3);
 
 
8078
  test_add_st("/task/cancel_old_question/1-3-2",
 
 
8079
              test_cancel_old_question_1_3_2);
 
 
8080
  test_add_st("/task/cancel_old_question/2-1-3",
 
 
8081
              test_cancel_old_question_2_1_3);
 
 
8082
  test_add_st("/task/cancel_old_question/2-3-1",
 
 
8083
              test_cancel_old_question_2_3_1);
 
 
8084
  test_add_st("/task/cancel_old_question/3-1-2",
 
 
8085
              test_cancel_old_question_3_1_2);
 
 
8086
  test_add_st("/task/cancel_old_question/3-2-1",
 
 
8087
              test_cancel_old_question_3_2_1);
 
 
8088
  test_add_st("/task/connect_question_socket/name-too-long",
 
 
8089
              test_connect_question_socket_name_too_long);
 
 
8090
  test_add_st("/task/connect_question_socket/connect-fail",
 
 
8091
              test_connect_question_socket_connect_fail);
 
 
8092
  test_add_st("/task/connect_question_socket/bad-epoll",
 
 
8093
              test_connect_question_socket_bad_epoll);
 
 
8094
  test_add_st("/task/connect_question_socket/usable",
 
 
8095
              test_connect_question_socket_usable);
 
 
8096
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
 
8097
              test_send_password_to_socket_client_not_exited);
 
 
8098
  test_add_st("/task/send_password_to_socket/password-not-read",
 
 
8099
              test_send_password_to_socket_password_not_read);
 
 
8100
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
 
8101
              test_send_password_to_socket_EMSGSIZE);
 
 
8102
  test_add_st("/task/send_password_to_socket/retry",
 
 
8103
              test_send_password_to_socket_retry);
 
 
8104
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
 
8105
              test_send_password_to_socket_bad_epoll);
 
 
8106
  test_add_st("/task/send_password_to_socket/null-password",
 
 
8107
              test_send_password_to_socket_null_password);
 
 
8108
  test_add_st("/task/send_password_to_socket/empty-password",
 
 
8109
              test_send_password_to_socket_empty_password);
 
 
8110
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
 
8111
              test_send_password_to_socket_empty_str_pass);
 
 
8112
  test_add_st("/task/send_password_to_socket/text-password",
 
 
8113
              test_send_password_to_socket_text_password);
 
 
8114
  test_add_st("/task/send_password_to_socket/binary-password",
 
 
8115
              test_send_password_to_socket_binary_password);
 
 
8116
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
 
8117
              test_send_password_to_socket_nuls_in_password);
 
 
8118
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
 
8119
              test_add_existing_questions_ENOENT);
 
 
8120
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
 
8121
              test_add_existing_questions_no_questions);
 
 
8122
  test_add_st("/task-creators/add_existing_questions/one-question",
 
 
8123
              test_add_existing_questions_one_question);
 
 
8124
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
 
8125
              test_add_existing_questions_two_questions);
 
 
8126
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
 
8127
              test_add_existing_questions_non_questions);
 
 
8128
  test_add_st("/task-creators/add_existing_questions/both-types",
 
 
8129
              test_add_existing_questions_both_types);
 
 
8131
  return g_test_run() == 0;
 
 
8134
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
 
8135
  GOptionContext *context = g_option_context_new("");
 
 
8137
  g_option_context_set_help_enabled(context, FALSE);
 
 
8138
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
 
8140
  gboolean should_run_tests = FALSE;
 
 
8141
  GOptionEntry entries[] = {
 
 
8142
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
 
8143
      &should_run_tests, "Run tests", NULL },
 
 
8146
  g_option_context_add_main_entries(context, entries, NULL);
 
 
8148
  GError *error = NULL;
 
 
8150
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
 
8151
    g_option_context_free(context);
 
 
8152
    g_error("Failed to parse options: %s", error->message);
 
 
8155
  g_option_context_free(context);
 
 
8156
  return should_run_tests != FALSE;