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-2020 Teddy Hogeborn
 
 
6
 * Copyright © 2019-2020 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://systemd.io/PASSWORD_AGENTS/> for
 
 
1197
     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://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
 
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 = (ssret < 0) ? errno : 0;
 
 
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, saved_errno, "Password of size %" PRIuMAX
 
 
1530
                " is too big", (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 %s: %"
 
 
1539
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
 
1540
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
 
1545
          __attribute__((fallthrough));
 
 
1548
          error(0, saved_errno, "Failed to send() to socket %s",
 
 
1550
          if(not string_set_add(cancelled_filenames,
 
 
1551
                                question_filename)){
 
 
1552
            error(0, errno, "Failed to cancel question for file %s",
 
 
1555
          cleanup_task(&task);
 
 
1560
        cleanup_task(&task);
 
 
1566
  /* We failed or are not ready yet; retry later */
 
 
1568
  if(not add_to_queue(queue, task)){
 
 
1569
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1570
          " file %s and socket %s to queue", question_filename,
 
 
1572
    cleanup_task(&task);
 
 
1575
  /* Add the fd to the epoll set */
 
 
1576
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1577
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1579
    error(0, errno, "Failed to add socket file descriptor %d for"
 
 
1580
          " socket %s to epoll set", fd, filename);
 
 
1581
    /* Force the added task (send_password_to_socket) to run again, at
 
 
1582
       most one second from now */
 
 
1583
    if((queue->next_run == 0)
 
 
1584
       or (queue->next_run > (*current_time + 1000000))){
 
 
1585
      queue->next_run = *current_time + 1000000;
 
 
1590
__attribute__((warn_unused_result))
 
 
1591
bool add_existing_questions(task_queue *const queue,
 
 
1593
                            buffer *const password,
 
 
1594
                            string_set *cancelled_filenames,
 
 
1595
                            const mono_microsecs *const current_time,
 
 
1596
                            bool *const mandos_client_exited,
 
 
1597
                            bool *const password_is_read,
 
 
1598
                            const char *const dirname){
 
 
1599
  __attribute__((cleanup(cleanup_string)))
 
 
1600
    char *dir_pattern = NULL;
 
 
1601
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
 
1602
  if(ret < 0 or dir_pattern == NULL){
 
 
1603
    error(0, errno, "Could not create glob pattern for directory %s",
 
 
1607
  __attribute__((cleanup(globfree)))
 
 
1608
    glob_t question_filenames = {};
 
 
1609
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
 
1610
              NULL, &question_filenames)){
 
 
1613
    error(0, errno, "Failed to open directory %s", dirname);
 
 
1616
    error(0, errno, "There are no question files in %s", dirname);
 
 
1619
    error(0, errno, "Could not allocate memory for question file"
 
 
1620
          " names in %s", dirname);
 
 
1624
    __attribute__((fallthrough));
 
 
1627
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
 
1628
      char *const question_filename = strdup(question_filenames
 
 
1630
      const task_context task = {
 
 
1631
        .func=open_and_parse_question,
 
 
1633
        .question_filename=question_filename,
 
 
1634
        .filename=question_filename,
 
 
1636
        .cancelled_filenames=cancelled_filenames,
 
 
1637
        .current_time=current_time,
 
 
1638
        .mandos_client_exited=mandos_client_exited,
 
 
1639
        .password_is_read=password_is_read,
 
 
1642
      if(question_filename == NULL
 
 
1643
         or not add_to_queue(queue, task)){
 
 
1644
        error(0, errno, "Failed to add open_and_parse_question for"
 
 
1645
              " file %s to queue",
 
 
1646
              question_filenames.gl_pathv[i]);
 
 
1647
        free(question_filename);
 
 
1649
        queue->next_run = 1;
 
 
1656
__attribute__((nonnull, warn_unused_result))
 
 
1657
bool wait_for_event(const int epoll_fd,
 
 
1658
                    const mono_microsecs queue_next_run,
 
 
1659
                    const mono_microsecs current_time){
 
 
1660
  __attribute__((const))
 
 
1661
    int milliseconds_to_wait(const mono_microsecs currtime,
 
 
1662
                             const mono_microsecs nextrun){
 
 
1663
    if(currtime >= nextrun){
 
 
1666
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
 
1667
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
 
1670
    return (int)wait_time_ms;
 
 
1673
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
 
1676
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
 
1677
  sigset_t temporary_unblocked_sigmask;
 
 
1678
  /* Get current signal mask */
 
 
1679
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
 
1682
  /* Remove SIGCHLD from the signal mask */
 
 
1683
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
 
1686
  struct epoll_event events[8]; /* Ignored */
 
 
1687
  int ret = epoll_pwait(epoll_fd, events,
 
 
1688
                        sizeof(events) / sizeof(struct epoll_event),
 
 
1689
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
 
1690
                        &temporary_unblocked_sigmask);
 
 
1691
  if(ret < 0 and errno != EINTR){
 
 
1692
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
 
1694
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
 
1697
  return clear_all_fds_from_epoll_set(epoll_fd);
 
 
1700
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
 
1701
  /* Create a new empty epoll set */
 
 
1702
  __attribute__((cleanup(cleanup_close)))
 
 
1703
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
1704
  if(new_epoll_fd < 0){
 
 
1707
  /* dup3() the new epoll set fd over the old one, replacing it */
 
 
1708
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
 
1714
__attribute__((nonnull, warn_unused_result))
 
 
1715
bool run_queue(task_queue **const queue,
 
 
1716
               string_set *const cancelled_filenames,
 
 
1717
               bool *const quit_now){
 
 
1719
  task_queue *new_queue = create_queue();
 
 
1720
  if(new_queue == NULL){
 
 
1724
  __attribute__((cleanup(string_set_clear)))
 
 
1725
    string_set old_cancelled_filenames = {};
 
 
1726
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
 
1728
  /* Declare i outside the for loop, since we might need i after the
 
 
1729
     loop in case we aborted in the middle */
 
 
1731
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
 
1732
    task_context *const task = &((*queue)->tasks[i]);
 
 
1733
    const char *const question_filename = task->question_filename;
 
 
1734
    /* Skip any task referencing a cancelled question filename */
 
 
1735
    if(question_filename != NULL
 
 
1736
       and string_set_contains(old_cancelled_filenames,
 
 
1737
                               question_filename)){
 
 
1741
    task->func(*task, new_queue);
 
 
1745
    /* we might be in the middle of the queue, so clean up any
 
 
1746
       remaining tasks in the current queue */
 
 
1747
    for(; i < (*queue)->length; i++){
 
 
1748
      cleanup_task(&((*queue)->tasks[i]));
 
 
1762
/* End of regular code section */
 
 
1764
/* Start of tests section; here are the tests for the above code */
 
 
1766
/* This "fixture" data structure is used by the test setup and
 
 
1767
   teardown functions */
 
 
1769
  struct sigaction orig_sigaction;
 
 
1770
  sigset_t orig_sigmask;
 
 
1773
static void test_setup(test_fixture *fixture,
 
 
1774
                       __attribute__((unused))
 
 
1775
                       gconstpointer user_data){
 
 
1776
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
 
1777
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
 
1780
static void test_teardown(test_fixture *fixture,
 
 
1781
                          __attribute__((unused))
 
 
1782
                          gconstpointer user_data){
 
 
1783
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
 
1784
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
 
1787
/* Utility function used by tests to search queue for matching task */
 
 
1788
__attribute__((pure, nonnull, warn_unused_result))
 
 
1789
static task_context *find_matching_task(const task_queue *const queue,
 
 
1790
                                        const task_context task){
 
 
1791
  /* The argument "task" structure is a pattern to match; 0 in any
 
 
1792
     member means any value matches, otherwise the value must match.
 
 
1793
     The filename strings are compared by strcmp(), not by pointer. */
 
 
1794
  for(size_t i = 0; i < queue->length; i++){
 
 
1795
    task_context *const current_task = queue->tasks+i;
 
 
1796
    /* Check all members of task_context, if set to a non-zero value.
 
 
1797
       If a member does not match, continue to next task in queue */
 
 
1799
    /* task_func *const func */
 
 
1800
    if(task.func != NULL and current_task->func != task.func){
 
 
1803
    /* char *const question_filename; */
 
 
1804
    if(task.question_filename != NULL
 
 
1805
       and (current_task->question_filename == NULL
 
 
1806
            or strcmp(current_task->question_filename,
 
 
1807
                      task.question_filename) != 0)){
 
 
1810
    /* const pid_t pid; */
 
 
1811
    if(task.pid != 0 and current_task->pid != task.pid){
 
 
1814
    /* const int epoll_fd; */
 
 
1815
    if(task.epoll_fd != 0
 
 
1816
       and current_task->epoll_fd != task.epoll_fd){
 
 
1819
    /* bool *const quit_now; */
 
 
1820
    if(task.quit_now != NULL
 
 
1821
       and current_task->quit_now != task.quit_now){
 
 
1825
    if(task.fd != 0 and current_task->fd != task.fd){
 
 
1828
    /* bool *const mandos_client_exited; */
 
 
1829
    if(task.mandos_client_exited != NULL
 
 
1830
       and current_task->mandos_client_exited
 
 
1831
       != task.mandos_client_exited){
 
 
1834
    /* buffer *const password; */
 
 
1835
    if(task.password != NULL
 
 
1836
       and current_task->password != task.password){
 
 
1839
    /* bool *const password_is_read; */
 
 
1840
    if(task.password_is_read != NULL
 
 
1841
       and current_task->password_is_read != task.password_is_read){
 
 
1844
    /* char *filename; */
 
 
1845
    if(task.filename != NULL
 
 
1846
       and (current_task->filename == NULL
 
 
1847
            or strcmp(current_task->filename, task.filename) != 0)){
 
 
1850
    /* string_set *const cancelled_filenames; */
 
 
1851
    if(task.cancelled_filenames != NULL
 
 
1852
       and current_task->cancelled_filenames
 
 
1853
       != task.cancelled_filenames){
 
 
1856
    /* const mono_microsecs notafter; */
 
 
1857
    if(task.notafter != 0
 
 
1858
       and current_task->notafter != task.notafter){
 
 
1861
    /* const mono_microsecs *const current_time; */
 
 
1862
    if(task.current_time != NULL
 
 
1863
       and current_task->current_time != task.current_time){
 
 
1866
    /* Current task matches all members; return it */
 
 
1867
    return current_task;
 
 
1869
  /* No task in queue matches passed pattern task */
 
 
1873
static void test_create_queue(__attribute__((unused))
 
 
1874
                              test_fixture *fixture,
 
 
1875
                              __attribute__((unused))
 
 
1876
                              gconstpointer user_data){
 
 
1877
  __attribute__((cleanup(cleanup_queue)))
 
 
1878
    task_queue *const queue = create_queue();
 
 
1879
  g_assert_nonnull(queue);
 
 
1880
  g_assert_null(queue->tasks);
 
 
1881
  g_assert_true(queue->length == 0);
 
 
1882
  g_assert_true(queue->next_run == 0);
 
 
1885
static task_func dummy_func;
 
 
1887
static void test_add_to_queue(__attribute__((unused))
 
 
1888
                              test_fixture *fixture,
 
 
1889
                              __attribute__((unused))
 
 
1890
                              gconstpointer user_data){
 
 
1891
  __attribute__((cleanup(cleanup_queue)))
 
 
1892
    task_queue *queue = create_queue();
 
 
1893
  g_assert_nonnull(queue);
 
 
1895
  g_assert_true(add_to_queue(queue,
 
 
1896
                             (task_context){ .func=dummy_func }));
 
 
1897
  g_assert_true(queue->length == 1);
 
 
1898
  g_assert_nonnull(queue->tasks);
 
 
1899
  g_assert_true(queue->tasks[0].func == dummy_func);
 
 
1902
static void test_add_to_queue_overflow(__attribute__((unused))
 
 
1903
                                       test_fixture *fixture,
 
 
1904
                                       __attribute__((unused))
 
 
1905
                                       gconstpointer user_data){
 
 
1906
  __attribute__((cleanup(cleanup_queue)))
 
 
1907
    task_queue *queue = create_queue();
 
 
1908
  g_assert_nonnull(queue);
 
 
1909
  g_assert_true(queue->length == 0);
 
 
1910
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
 
1912
  FILE *real_stderr = stderr;
 
 
1913
  FILE *devnull = fopen("/dev/null", "we");
 
 
1914
  g_assert_nonnull(devnull);
 
 
1916
  const bool ret = add_to_queue(queue,
 
 
1917
                                (task_context){ .func=dummy_func });
 
 
1918
  g_assert_true(errno == ENOMEM);
 
 
1919
  g_assert_false(ret);
 
 
1920
  stderr = real_stderr;
 
 
1921
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
1922
  queue->length = 0;            /* Restore real size */
 
 
1925
static void dummy_func(__attribute__((unused))
 
 
1926
                       const task_context task,
 
 
1927
                       __attribute__((unused))
 
 
1928
                       task_queue *const queue){
 
 
1931
static void test_queue_has_question_empty(__attribute__((unused))
 
 
1932
                                          test_fixture *fixture,
 
 
1933
                                          __attribute__((unused))
 
 
1934
                                          gconstpointer user_data){
 
 
1935
  __attribute__((cleanup(cleanup_queue)))
 
 
1936
    task_queue *queue = create_queue();
 
 
1937
  g_assert_nonnull(queue);
 
 
1938
  g_assert_false(queue_has_question(queue));
 
 
1941
static void test_queue_has_question_false(__attribute__((unused))
 
 
1942
                                          test_fixture *fixture,
 
 
1943
                                          __attribute__((unused))
 
 
1944
                                          gconstpointer user_data){
 
 
1945
  __attribute__((cleanup(cleanup_queue)))
 
 
1946
    task_queue *queue = create_queue();
 
 
1947
  g_assert_nonnull(queue);
 
 
1948
  g_assert_true(add_to_queue(queue,
 
 
1949
                             (task_context){ .func=dummy_func }));
 
 
1950
  g_assert_false(queue_has_question(queue));
 
 
1953
static void test_queue_has_question_true(__attribute__((unused))
 
 
1954
                                         test_fixture *fixture,
 
 
1955
                                         __attribute__((unused))
 
 
1956
                                         gconstpointer user_data){
 
 
1957
  __attribute__((cleanup(cleanup_queue)))
 
 
1958
    task_queue *queue = create_queue();
 
 
1959
  g_assert_nonnull(queue);
 
 
1960
  char *const question_filename
 
 
1961
    = strdup("/nonexistent/question_filename");
 
 
1962
  g_assert_nonnull(question_filename);
 
 
1963
  task_context task = {
 
 
1965
    .question_filename=question_filename,
 
 
1967
  g_assert_true(add_to_queue(queue, task));
 
 
1968
  g_assert_true(queue_has_question(queue));
 
 
1971
static void test_queue_has_question_false2(__attribute__((unused))
 
 
1972
                                           test_fixture *fixture,
 
 
1973
                                           __attribute__((unused))
 
 
1974
                                           gconstpointer user_data){
 
 
1975
  __attribute__((cleanup(cleanup_queue)))
 
 
1976
    task_queue *queue = create_queue();
 
 
1977
  g_assert_nonnull(queue);
 
 
1978
  task_context task = { .func=dummy_func };
 
 
1979
  g_assert_true(add_to_queue(queue, task));
 
 
1980
  g_assert_true(add_to_queue(queue, task));
 
 
1981
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
1982
  g_assert_false(queue_has_question(queue));
 
 
1985
static void test_queue_has_question_true2(__attribute__((unused))
 
 
1986
                                          test_fixture *fixture,
 
 
1987
                                          __attribute__((unused))
 
 
1988
                                          gconstpointer user_data){
 
 
1989
  __attribute__((cleanup(cleanup_queue)))
 
 
1990
    task_queue *queue = create_queue();
 
 
1991
  g_assert_nonnull(queue);
 
 
1992
  task_context task1 = { .func=dummy_func };
 
 
1993
  g_assert_true(add_to_queue(queue, task1));
 
 
1994
  char *const question_filename
 
 
1995
    = strdup("/nonexistent/question_filename");
 
 
1996
  g_assert_nonnull(question_filename);
 
 
1997
  task_context task2 = {
 
 
1999
    .question_filename=question_filename,
 
 
2001
  g_assert_true(add_to_queue(queue, task2));
 
 
2002
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
2003
  g_assert_true(queue_has_question(queue));
 
 
2006
static void test_cleanup_buffer(__attribute__((unused))
 
 
2007
                                test_fixture *fixture,
 
 
2008
                                __attribute__((unused))
 
 
2009
                                gconstpointer user_data){
 
 
2012
  const size_t buffersize = 10;
 
 
2014
  buf.data = malloc(buffersize);
 
 
2015
  g_assert_nonnull(buf.data);
 
 
2016
  if(mlock(buf.data, buffersize) != 0){
 
 
2017
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
2020
  cleanup_buffer(&buf);
 
 
2021
  g_assert_null(buf.data);
 
 
2025
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
 
2026
                                              test_fixture *fixture,
 
 
2027
                                              __attribute__((unused))
 
 
2030
  __attribute__((cleanup(string_set_clear)))
 
 
2031
    string_set set = {};
 
 
2032
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
 
2033
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2037
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
 
2038
                                              test_fixture *fixture,
 
 
2039
                                              __attribute__((unused))
 
 
2042
  __attribute__((cleanup(string_set_clear)))
 
 
2043
    string_set set = {};
 
 
2044
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2045
  g_assert_true(string_set_contains(set, "test_string"));
 
 
2049
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
 
2050
                                             test_fixture *fixture,
 
 
2051
                                             __attribute__((unused))
 
 
2052
                                             gconstpointer user_data){
 
 
2053
  __attribute__((cleanup(string_set_clear)))
 
 
2054
    string_set set = {};
 
 
2055
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2056
  string_set_clear(&set);
 
 
2057
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2061
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
 
2062
                                         test_fixture *fixture,
 
 
2063
                                         __attribute__((unused))
 
 
2064
                                         gconstpointer user_data){
 
 
2065
  __attribute__((cleanup(string_set_clear)))
 
 
2066
    string_set set1 = {};
 
 
2067
  __attribute__((cleanup(string_set_clear)))
 
 
2068
    string_set set2 = {};
 
 
2069
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2070
  string_set_swap(&set1, &set2);
 
 
2071
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2072
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2076
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
 
2077
                                         test_fixture *fixture,
 
 
2078
                                         __attribute__((unused))
 
 
2079
                                         gconstpointer user_data){
 
 
2080
  __attribute__((cleanup(string_set_clear)))
 
 
2081
    string_set set1 = {};
 
 
2082
  __attribute__((cleanup(string_set_clear)))
 
 
2083
    string_set set2 = {};
 
 
2084
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2085
  string_set_swap(&set1, &set2);
 
 
2086
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2087
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2090
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
 
2091
                                              test_fixture *fixture,
 
 
2092
                                              __attribute__((unused))
 
 
2095
  __attribute__((cleanup(string_set_clear)))
 
 
2096
    string_set set1 = {};
 
 
2097
  __attribute__((cleanup(string_set_clear)))
 
 
2098
    string_set set2 = {};
 
 
2099
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2100
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2101
  string_set_swap(&set1, &set2);
 
 
2102
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2103
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2104
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2105
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2108
static bool fd_has_cloexec_and_nonblock(const int);
 
 
2110
static bool epoll_set_contains(int, int, uint32_t);
 
 
2112
static void test_start_mandos_client(test_fixture *fixture,
 
 
2113
                                     __attribute__((unused))
 
 
2114
                                     gconstpointer user_data){
 
 
2116
  bool mandos_client_exited = false;
 
 
2117
  bool quit_now = false;
 
 
2118
  __attribute__((cleanup(cleanup_close)))
 
 
2119
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2120
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2121
  __attribute__((cleanup(cleanup_queue)))
 
 
2122
    task_queue *queue = create_queue();
 
 
2123
  g_assert_nonnull(queue);
 
 
2124
  buffer password = {};
 
 
2125
  bool password_is_read = false;
 
 
2126
  const char helper_directory[] = "/nonexistent";
 
 
2127
  const char *const argv[] = { "/bin/true", NULL };
 
 
2129
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2130
                                    &mandos_client_exited, &quit_now,
 
 
2131
                                    &password, &password_is_read,
 
 
2132
                                    &fixture->orig_sigaction,
 
 
2133
                                    fixture->orig_sigmask,
 
 
2134
                                    helper_directory, 0, 0, argv));
 
 
2136
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
2138
  const task_context *const added_wait_task
 
 
2139
    = find_matching_task(queue, (task_context){
 
 
2140
        .func=wait_for_mandos_client_exit,
 
 
2141
        .mandos_client_exited=&mandos_client_exited,
 
 
2142
        .quit_now=&quit_now,
 
 
2144
  g_assert_nonnull(added_wait_task);
 
 
2145
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
 
2146
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
 
2147
  waitpid(added_wait_task->pid, NULL, 0);
 
 
2149
  const task_context *const added_read_task
 
 
2150
    = find_matching_task(queue, (task_context){
 
 
2151
        .func=read_mandos_client_output,
 
 
2153
        .password=&password,
 
 
2154
        .password_is_read=&password_is_read,
 
 
2155
        .quit_now=&quit_now,
 
 
2157
  g_assert_nonnull(added_read_task);
 
 
2158
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
2159
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
2160
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
 
2161
                                   EPOLLIN | EPOLLRDHUP));
 
 
2164
static bool fd_has_cloexec_and_nonblock(const int fd){
 
 
2165
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
 
2166
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
 
2167
  return ((socket_fd_flags >= 0)
 
 
2168
          and (socket_fd_flags & FD_CLOEXEC)
 
 
2169
          and (socket_file_flags >= 0)
 
 
2170
          and (socket_file_flags & O_NONBLOCK));
 
 
2173
__attribute__((const))
 
 
2174
bool is_privileged(void){
 
 
2175
  uid_t user = getuid() + 1;
 
 
2176
  if(user == 0){                /* Overflow check */
 
 
2179
  gid_t group = getuid() + 1;
 
 
2180
  if(group == 0){               /* Overflow check */
 
 
2183
  const pid_t pid = fork();
 
 
2184
  if(pid == 0){                 /* Child */
 
 
2185
    if(setresgid((uid_t)-1, group, group) == -1){
 
 
2187
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
 
2188
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
2192
    if(setresuid((uid_t)-1, user, user) == -1){
 
 
2194
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
 
2195
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
2202
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
 
2206
  waitpid(pid, &status, 0);
 
 
2207
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
 
2213
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
 
2214
  /* Only scan for events in this eventmask */
 
 
2215
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
 
2216
  __attribute__((cleanup(cleanup_string)))
 
 
2217
    char *fdinfo_name = NULL;
 
 
2218
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
 
2219
  g_assert_cmpint(ret, >, 0);
 
 
2220
  g_assert_nonnull(fdinfo_name);
 
 
2222
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
 
2223
  g_assert_nonnull(fdinfo);
 
 
2224
  uint32_t reported_events;
 
 
2229
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
 
2232
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
 
2233
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
 
2234
              &found_fd, &reported_events) == 2){
 
 
2239
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
 
2240
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
 
2247
    /* Don't check events if none are given */
 
 
2250
  return (reported_events & eventmask) == (events & eventmask);
 
 
2253
static void test_start_mandos_client_execv(test_fixture *fixture,
 
 
2254
                                           __attribute__((unused))
 
 
2255
                                           gconstpointer user_data){
 
 
2256
  bool mandos_client_exited = false;
 
 
2257
  bool quit_now = false;
 
 
2258
  __attribute__((cleanup(cleanup_close)))
 
 
2259
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2260
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2261
  __attribute__((cleanup(cleanup_queue)))
 
 
2262
    task_queue *queue = create_queue();
 
 
2263
  g_assert_nonnull(queue);
 
 
2264
  __attribute__((cleanup(cleanup_buffer)))
 
 
2265
    buffer password = {};
 
 
2266
  const char helper_directory[] = "/nonexistent";
 
 
2267
  /* Can't execv("/", ...), so this should fail */
 
 
2268
  const char *const argv[] = { "/", NULL };
 
 
2271
    __attribute__((cleanup(cleanup_close)))
 
 
2272
      const int devnull_fd = open("/dev/null",
 
 
2273
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2274
    g_assert_cmpint(devnull_fd, >=, 0);
 
 
2275
    __attribute__((cleanup(cleanup_close)))
 
 
2276
      const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2277
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2278
    dup2(devnull_fd, STDERR_FILENO);
 
 
2280
    const bool success = start_mandos_client(queue, epoll_fd,
 
 
2281
                                             &mandos_client_exited,
 
 
2285
                                             &fixture->orig_sigaction,
 
 
2286
                                             fixture->orig_sigmask,
 
 
2287
                                             helper_directory, 0, 0,
 
 
2289
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2290
    g_assert_true(success);
 
 
2292
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
2294
  struct timespec starttime, currtime;
 
 
2295
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2297
    queue->next_run = 0;
 
 
2298
    string_set cancelled_filenames = {};
 
 
2301
      __attribute__((cleanup(cleanup_close)))
 
 
2302
        const int devnull_fd = open("/dev/null",
 
 
2303
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2304
      g_assert_cmpint(devnull_fd, >=, 0);
 
 
2305
      __attribute__((cleanup(cleanup_close)))
 
 
2306
        const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2307
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2308
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2309
      dup2(devnull_fd, STDERR_FILENO);
 
 
2310
      const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2312
      dup2(real_stderr_fd, STDERR_FILENO);
 
 
2317
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2318
  } while(((queue->length) > 0)
 
 
2320
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2322
  g_assert_true(quit_now);
 
 
2323
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2324
  g_assert_true(mandos_client_exited);
 
 
2327
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
 
2328
                                               __attribute__((unused))
 
 
2331
  if(not is_privileged()){
 
 
2332
    g_test_skip("Not privileged");
 
 
2336
  bool mandos_client_exited = false;
 
 
2337
  bool quit_now = false;
 
 
2338
  __attribute__((cleanup(cleanup_close)))
 
 
2339
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2340
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2341
  __attribute__((cleanup(cleanup_queue)))
 
 
2342
    task_queue *queue = create_queue();
 
 
2343
  g_assert_nonnull(queue);
 
 
2344
  __attribute__((cleanup(cleanup_buffer)))
 
 
2345
    buffer password = {};
 
 
2346
  bool password_is_read = false;
 
 
2347
  const char helper_directory[] = "/nonexistent";
 
 
2348
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
 
2352
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2353
                                           &mandos_client_exited,
 
 
2354
                                           &quit_now, &password,
 
 
2356
                                           &fixture->orig_sigaction,
 
 
2357
                                           fixture->orig_sigmask,
 
 
2358
                                           helper_directory, user,
 
 
2360
  g_assert_true(success);
 
 
2361
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2363
  struct timespec starttime, currtime;
 
 
2364
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2366
    queue->next_run = 0;
 
 
2367
    string_set cancelled_filenames = {};
 
 
2368
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2369
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2370
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2371
  } while(((queue->length) > 0)
 
 
2373
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2375
  g_assert_false(quit_now);
 
 
2376
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2377
  g_assert_true(mandos_client_exited);
 
 
2379
  g_assert_true(password_is_read);
 
 
2380
  g_assert_nonnull(password.data);
 
 
2383
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2385
  g_assert_true((uid_t)id == id);
 
 
2387
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2390
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
 
2391
                                               __attribute__((unused))
 
 
2394
  if(not is_privileged()){
 
 
2395
    g_test_skip("Not privileged");
 
 
2399
  bool mandos_client_exited = false;
 
 
2400
  bool quit_now = false;
 
 
2401
  __attribute__((cleanup(cleanup_close)))
 
 
2402
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2403
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2404
  __attribute__((cleanup(cleanup_queue)))
 
 
2405
    task_queue *queue = create_queue();
 
 
2406
  g_assert_nonnull(queue);
 
 
2407
  __attribute__((cleanup(cleanup_buffer)))
 
 
2408
    buffer password = {};
 
 
2409
  bool password_is_read = false;
 
 
2410
  const char helper_directory[] = "/nonexistent";
 
 
2411
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
 
2415
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2416
                                           &mandos_client_exited,
 
 
2417
                                           &quit_now, &password,
 
 
2419
                                           &fixture->orig_sigaction,
 
 
2420
                                           fixture->orig_sigmask,
 
 
2421
                                           helper_directory, user,
 
 
2423
  g_assert_true(success);
 
 
2424
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2426
  struct timespec starttime, currtime;
 
 
2427
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2429
    queue->next_run = 0;
 
 
2430
    string_set cancelled_filenames = {};
 
 
2431
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2432
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2433
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2434
  } while(((queue->length) > 0)
 
 
2436
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2438
  g_assert_false(quit_now);
 
 
2439
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2440
  g_assert_true(mandos_client_exited);
 
 
2442
  g_assert_true(password_is_read);
 
 
2443
  g_assert_nonnull(password.data);
 
 
2446
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2448
  g_assert_true((gid_t)id == id);
 
 
2450
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2453
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
 
2454
                                               __attribute__((unused))
 
 
2457
  if(not is_privileged()){
 
 
2458
    g_test_skip("Not privileged");
 
 
2462
  bool mandos_client_exited = false;
 
 
2463
  bool quit_now = false;
 
 
2464
  __attribute__((cleanup(cleanup_close)))
 
 
2465
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2466
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2467
  __attribute__((cleanup(cleanup_queue)))
 
 
2468
    task_queue *queue = create_queue();
 
 
2469
  g_assert_nonnull(queue);
 
 
2470
  __attribute__((cleanup(cleanup_buffer)))
 
 
2471
    buffer password = {};
 
 
2472
  bool password_is_read = false;
 
 
2473
  const char helper_directory[] = "/nonexistent";
 
 
2474
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
 
2479
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2480
                                           &mandos_client_exited,
 
 
2481
                                           &quit_now, &password,
 
 
2483
                                           &fixture->orig_sigaction,
 
 
2484
                                           fixture->orig_sigmask,
 
 
2485
                                           helper_directory, user,
 
 
2487
  g_assert_true(success);
 
 
2488
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2490
  struct timespec starttime, currtime;
 
 
2491
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2493
    queue->next_run = 0;
 
 
2494
    string_set cancelled_filenames = {};
 
 
2495
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2496
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2497
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2498
  } while(((queue->length) > 0)
 
 
2500
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2502
  g_assert_false(quit_now);
 
 
2503
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2504
  g_assert_true(mandos_client_exited);
 
 
2506
  g_assert_true(password_is_read);
 
 
2507
  g_assert_nonnull(password.data);
 
 
2510
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2512
  g_assert_true((uid_t)id == id);
 
 
2514
  g_assert_cmpuint((unsigned int)id, ==, user);
 
 
2517
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
 
2518
                                               __attribute__((unused))
 
 
2521
  if(not is_privileged()){
 
 
2522
    g_test_skip("Not privileged");
 
 
2526
  bool mandos_client_exited = false;
 
 
2527
  bool quit_now = false;
 
 
2528
  __attribute__((cleanup(cleanup_close)))
 
 
2529
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2530
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2531
  __attribute__((cleanup(cleanup_queue)))
 
 
2532
    task_queue *queue = create_queue();
 
 
2533
  g_assert_nonnull(queue);
 
 
2534
  __attribute__((cleanup(cleanup_buffer)))
 
 
2535
    buffer password = {};
 
 
2536
  bool password_is_read = false;
 
 
2537
  const char helper_directory[] = "/nonexistent";
 
 
2538
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
 
2543
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2544
                                           &mandos_client_exited,
 
 
2545
                                           &quit_now, &password,
 
 
2547
                                           &fixture->orig_sigaction,
 
 
2548
                                           fixture->orig_sigmask,
 
 
2549
                                           helper_directory, user,
 
 
2551
  g_assert_true(success);
 
 
2552
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2554
  struct timespec starttime, currtime;
 
 
2555
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2557
    queue->next_run = 0;
 
 
2558
    string_set cancelled_filenames = {};
 
 
2559
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2560
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2561
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2562
  } while(((queue->length) > 0)
 
 
2564
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2566
  g_assert_false(quit_now);
 
 
2567
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2568
  g_assert_true(mandos_client_exited);
 
 
2570
  g_assert_true(password_is_read);
 
 
2571
  g_assert_nonnull(password.data);
 
 
2574
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2576
  g_assert_true((gid_t)id == id);
 
 
2578
  g_assert_cmpuint((unsigned int)id, ==, group);
 
 
2581
static void test_start_mandos_client_read(test_fixture *fixture,
 
 
2582
                                          __attribute__((unused))
 
 
2583
                                          gconstpointer user_data){
 
 
2584
  bool mandos_client_exited = false;
 
 
2585
  bool quit_now = false;
 
 
2586
  __attribute__((cleanup(cleanup_close)))
 
 
2587
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2588
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2589
  __attribute__((cleanup(cleanup_queue)))
 
 
2590
    task_queue *queue = create_queue();
 
 
2591
  g_assert_nonnull(queue);
 
 
2592
  __attribute__((cleanup(cleanup_buffer)))
 
 
2593
    buffer password = {};
 
 
2594
  bool password_is_read = false;
 
 
2595
  const char dummy_test_password[] = "dummy test password";
 
 
2596
  const char helper_directory[] = "/nonexistent";
 
 
2597
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
 
2600
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2601
                                           &mandos_client_exited,
 
 
2602
                                           &quit_now, &password,
 
 
2604
                                           &fixture->orig_sigaction,
 
 
2605
                                           fixture->orig_sigmask,
 
 
2606
                                           helper_directory, 0, 0,
 
 
2608
  g_assert_true(success);
 
 
2609
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2611
  struct timespec starttime, currtime;
 
 
2612
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2614
    queue->next_run = 0;
 
 
2615
    string_set cancelled_filenames = {};
 
 
2616
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2617
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2618
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2619
  } while(((queue->length) > 0)
 
 
2621
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2623
  g_assert_false(quit_now);
 
 
2624
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2625
  g_assert_true(mandos_client_exited);
 
 
2627
  g_assert_true(password_is_read);
 
 
2628
  g_assert_cmpint((int)password.length, ==,
 
 
2629
                  sizeof(dummy_test_password)-1);
 
 
2630
  g_assert_nonnull(password.data);
 
 
2631
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
 
2632
                         sizeof(dummy_test_password)-1), ==, 0);
 
 
2636
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
 
2637
                                               __attribute__((unused))
 
 
2640
  bool mandos_client_exited = false;
 
 
2641
  bool quit_now = false;
 
 
2642
  __attribute__((cleanup(cleanup_close)))
 
 
2643
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2644
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2645
  __attribute__((cleanup(cleanup_queue)))
 
 
2646
    task_queue *queue = create_queue();
 
 
2647
  g_assert_nonnull(queue);
 
 
2648
  __attribute__((cleanup(cleanup_buffer)))
 
 
2649
    buffer password = {};
 
 
2650
  bool password_is_read = false;
 
 
2651
  const char helper_directory[] = "/nonexistent";
 
 
2652
  const char *const argv[] = { "/bin/sh", "-c",
 
 
2653
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
 
2655
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2656
                                           &mandos_client_exited,
 
 
2657
                                           &quit_now, &password,
 
 
2659
                                           &fixture->orig_sigaction,
 
 
2660
                                           fixture->orig_sigmask,
 
 
2661
                                           helper_directory, 0, 0,
 
 
2663
  g_assert_true(success);
 
 
2664
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2666
  struct timespec starttime, currtime;
 
 
2667
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2669
    queue->next_run = 0;
 
 
2670
    string_set cancelled_filenames = {};
 
 
2671
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2672
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2673
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2674
  } while(((queue->length) > 0)
 
 
2676
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2678
  g_assert_false(quit_now);
 
 
2679
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2680
  g_assert_true(mandos_client_exited);
 
 
2682
  g_assert_true(password_is_read);
 
 
2683
  g_assert_cmpint((int)password.length, ==,
 
 
2684
                  sizeof(helper_directory)-1);
 
 
2685
  g_assert_nonnull(password.data);
 
 
2686
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
 
2687
                         sizeof(helper_directory)-1), ==, 0);
 
 
2690
__attribute__((nonnull, warn_unused_result))
 
 
2691
static bool proc_status_sigblk_to_sigset(const char *const,
 
 
2694
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
 
2695
                                             __attribute__((unused))
 
 
2696
                                             gconstpointer user_data){
 
 
2697
  bool mandos_client_exited = false;
 
 
2698
  bool quit_now = false;
 
 
2699
  __attribute__((cleanup(cleanup_close)))
 
 
2700
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2701
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2702
  __attribute__((cleanup(cleanup_queue)))
 
 
2703
    task_queue *queue = create_queue();
 
 
2704
  g_assert_nonnull(queue);
 
 
2705
  __attribute__((cleanup(cleanup_buffer)))
 
 
2706
    buffer password = {};
 
 
2707
  bool password_is_read = false;
 
 
2708
  const char helper_directory[] = "/nonexistent";
 
 
2709
  /* see proc(5) for format of /proc/self/status */
 
 
2710
  const char *const argv[] = { "/usr/bin/awk",
 
 
2711
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
 
2713
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2714
                                    &mandos_client_exited, &quit_now,
 
 
2715
                                    &password, &password_is_read,
 
 
2716
                                    &fixture->orig_sigaction,
 
 
2717
                                    fixture->orig_sigmask,
 
 
2718
                                    helper_directory, 0, 0, argv));
 
 
2720
  struct timespec starttime, currtime;
 
 
2721
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2723
    queue->next_run = 0;
 
 
2724
    string_set cancelled_filenames = {};
 
 
2725
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2726
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2727
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2728
  } while((not (mandos_client_exited and password_is_read))
 
 
2730
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2731
  g_assert_true(mandos_client_exited);
 
 
2732
  g_assert_true(password_is_read);
 
 
2734
  sigset_t parsed_sigmask;
 
 
2735
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
 
2738
  for(int signum = 1; signum < NSIG; signum++){
 
 
2739
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
 
2740
    if(sigismember(&fixture->orig_sigmask, signum)){
 
 
2741
      g_assert_true(has_signal);
 
 
2743
      g_assert_false(has_signal);
 
 
2748
__attribute__((nonnull, warn_unused_result))
 
 
2749
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
 
2750
                                         sigset_t *const sigmask){
 
 
2751
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
 
2752
  uintmax_t scanned_sigmask;
 
 
2753
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
 
2756
  if(sigemptyset(sigmask) != 0){
 
 
2759
  for(int signum = 1; signum < NSIG; signum++){
 
 
2760
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
 
2761
      if(sigaddset(sigmask, signum) != 0){
 
 
2769
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2770
                                             task_queue *const queue);
 
 
2773
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
 
2774
                                             test_fixture *fixture,
 
 
2775
                                             __attribute__((unused))
 
 
2776
                                             gconstpointer user_data){
 
 
2778
  bool mandos_client_exited = false;
 
 
2779
  bool quit_now = false;
 
 
2781
  __attribute__((cleanup(cleanup_queue)))
 
 
2782
    task_queue *queue = create_queue();
 
 
2783
  g_assert_nonnull(queue);
 
 
2784
  const task_context task = {
 
 
2785
    .func=wait_for_mandos_client_exit,
 
 
2787
    .mandos_client_exited=&mandos_client_exited,
 
 
2788
    .quit_now=&quit_now,
 
 
2790
  run_task_with_stderr_to_dev_null(task, queue);
 
 
2792
  g_assert_false(mandos_client_exited);
 
 
2793
  g_assert_true(quit_now);
 
 
2794
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2797
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2798
                                             task_queue *const queue){
 
 
2799
  FILE *real_stderr = stderr;
 
 
2800
  FILE *devnull = fopen("/dev/null", "we");
 
 
2801
  g_assert_nonnull(devnull);
 
 
2804
  task.func(task, queue);
 
 
2805
  stderr = real_stderr;
 
 
2807
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
2811
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
 
2812
                                             __attribute__((unused))
 
 
2813
                                             gconstpointer user_data){
 
 
2814
  bool mandos_client_exited = false;
 
 
2815
  bool quit_now = false;
 
 
2817
  pid_t create_eternal_process(void){
 
 
2818
    const pid_t pid = fork();
 
 
2819
    if(pid == 0){               /* Child */
 
 
2820
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2821
        _exit(EXIT_FAILURE);
 
 
2823
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2824
        _exit(EXIT_FAILURE);
 
 
2832
  pid_t pid = create_eternal_process();
 
 
2833
  g_assert_true(pid != -1);
 
 
2835
  __attribute__((cleanup(cleanup_queue)))
 
 
2836
    task_queue *queue = create_queue();
 
 
2837
  g_assert_nonnull(queue);
 
 
2838
  const task_context task = {
 
 
2839
    .func=wait_for_mandos_client_exit,
 
 
2841
    .mandos_client_exited=&mandos_client_exited,
 
 
2842
    .quit_now=&quit_now,
 
 
2844
  task.func(task, queue);
 
 
2846
  g_assert_false(mandos_client_exited);
 
 
2847
  g_assert_false(quit_now);
 
 
2848
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
2850
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
2851
        .func=wait_for_mandos_client_exit,
 
 
2853
        .mandos_client_exited=&mandos_client_exited,
 
 
2854
        .quit_now=&quit_now,
 
 
2859
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
 
2860
                                              __attribute__((unused))
 
 
2863
  bool mandos_client_exited = false;
 
 
2864
  bool quit_now = false;
 
 
2866
  pid_t create_successful_process(void){
 
 
2867
    const pid_t pid = fork();
 
 
2868
    if(pid == 0){               /* Child */
 
 
2869
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2870
        _exit(EXIT_FAILURE);
 
 
2872
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2873
        _exit(EXIT_FAILURE);
 
 
2879
  const pid_t pid = create_successful_process();
 
 
2880
  g_assert_true(pid != -1);
 
 
2882
  __attribute__((cleanup(cleanup_queue)))
 
 
2883
    task_queue *queue = create_queue();
 
 
2884
  g_assert_nonnull(queue);
 
 
2885
  const task_context initial_task = {
 
 
2886
    .func=wait_for_mandos_client_exit,
 
 
2888
    .mandos_client_exited=&mandos_client_exited,
 
 
2889
    .quit_now=&quit_now,
 
 
2891
  g_assert_true(add_to_queue(queue, initial_task));
 
 
2893
  struct timespec starttime, currtime;
 
 
2894
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2895
  __attribute__((cleanup(cleanup_close)))
 
 
2896
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2898
    queue->next_run = 0;
 
 
2899
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2900
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
 
2901
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2902
  } while((not mandos_client_exited)
 
 
2904
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2906
  g_assert_true(mandos_client_exited);
 
 
2907
  g_assert_false(quit_now);
 
 
2908
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2912
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
 
2913
                                              __attribute__((unused))
 
 
2916
  bool mandos_client_exited = false;
 
 
2917
  bool quit_now = false;
 
 
2919
  pid_t create_failing_process(void){
 
 
2920
    const pid_t pid = fork();
 
 
2921
    if(pid == 0){               /* Child */
 
 
2922
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2923
        _exit(EXIT_FAILURE);
 
 
2925
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2926
        _exit(EXIT_FAILURE);
 
 
2932
  const pid_t pid = create_failing_process();
 
 
2933
  g_assert_true(pid != -1);
 
 
2935
  __attribute__((cleanup(string_set_clear)))
 
 
2936
    string_set cancelled_filenames = {};
 
 
2937
  __attribute__((cleanup(cleanup_close)))
 
 
2938
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2939
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2940
  __attribute__((cleanup(cleanup_queue)))
 
 
2941
    task_queue *queue = create_queue();
 
 
2942
  g_assert_nonnull(queue);
 
 
2943
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2944
        .func=wait_for_mandos_client_exit,
 
 
2946
        .mandos_client_exited=&mandos_client_exited,
 
 
2947
        .quit_now=&quit_now,
 
 
2950
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
 
2952
  __attribute__((cleanup(cleanup_close)))
 
 
2953
    const int devnull_fd = open("/dev/null",
 
 
2954
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2955
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2956
  __attribute__((cleanup(cleanup_close)))
 
 
2957
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2958
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2960
  struct timespec starttime, currtime;
 
 
2961
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2963
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2964
    dup2(devnull_fd, STDERR_FILENO);
 
 
2965
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2967
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2972
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2973
  } while((not mandos_client_exited)
 
 
2975
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2977
  g_assert_true(quit_now);
 
 
2978
  g_assert_true(mandos_client_exited);
 
 
2979
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2983
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
 
2984
                                             __attribute__((unused))
 
 
2985
                                             gconstpointer user_data){
 
 
2986
  bool mandos_client_exited = false;
 
 
2987
  bool quit_now = false;
 
 
2989
  pid_t create_killed_process(void){
 
 
2990
    const pid_t pid = fork();
 
 
2991
    if(pid == 0){               /* Child */
 
 
2992
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2993
        _exit(EXIT_FAILURE);
 
 
2995
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2996
        _exit(EXIT_FAILURE);
 
 
3005
  const pid_t pid = create_killed_process();
 
 
3006
  g_assert_true(pid != -1);
 
 
3008
  __attribute__((cleanup(string_set_clear)))
 
 
3009
    string_set cancelled_filenames = {};
 
 
3010
  __attribute__((cleanup(cleanup_close)))
 
 
3011
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3012
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3013
  __attribute__((cleanup(cleanup_queue)))
 
 
3014
    task_queue *queue = create_queue();
 
 
3015
  g_assert_nonnull(queue);
 
 
3016
  g_assert_true(add_to_queue(queue, (task_context){
 
 
3017
        .func=wait_for_mandos_client_exit,
 
 
3019
        .mandos_client_exited=&mandos_client_exited,
 
 
3020
        .quit_now=&quit_now,
 
 
3023
  __attribute__((cleanup(cleanup_close)))
 
 
3024
    const int devnull_fd = open("/dev/null",
 
 
3025
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
 
3026
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
3027
  __attribute__((cleanup(cleanup_close)))
 
 
3028
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
3029
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
3031
  struct timespec starttime, currtime;
 
 
3032
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
3034
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
3035
    dup2(devnull_fd, STDERR_FILENO);
 
 
3036
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
3038
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
3043
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
3044
  } while((not mandos_client_exited)
 
 
3046
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
3048
  g_assert_true(mandos_client_exited);
 
 
3049
  g_assert_true(quit_now);
 
 
3050
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3053
static bool epoll_set_does_not_contain(int, int);
 
 
3056
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
 
3057
                                              test_fixture *fixture,
 
 
3058
                                              __attribute__((unused))
 
 
3061
  __attribute__((cleanup(cleanup_close)))
 
 
3062
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3063
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3065
  __attribute__((cleanup(cleanup_buffer)))
 
 
3066
    buffer password = {};
 
 
3068
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
 
3069
  const int fd = open("/proc/self/mem",
 
 
3070
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3072
  bool password_is_read = false;
 
 
3073
  bool quit_now = false;
 
 
3074
  __attribute__((cleanup(cleanup_queue)))
 
 
3075
    task_queue *queue = create_queue();
 
 
3076
  g_assert_nonnull(queue);
 
 
3078
  task_context task = {
 
 
3079
    .func=read_mandos_client_output,
 
 
3082
    .password=&password,
 
 
3083
    .password_is_read=&password_is_read,
 
 
3084
    .quit_now=&quit_now,
 
 
3086
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3087
  g_assert_false(password_is_read);
 
 
3088
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3089
  g_assert_true(quit_now);
 
 
3090
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3092
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3094
  g_assert_cmpint(close(fd), ==, -1);
 
 
3097
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
 
3098
  return not epoll_set_contains(epoll_fd, fd, 0);
 
 
3102
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
 
3103
                                           test_fixture *fixture,
 
 
3104
                                           __attribute__((unused))
 
 
3105
                                           gconstpointer user_data){
 
 
3106
  __attribute__((cleanup(cleanup_close)))
 
 
3107
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3108
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3111
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3113
  __attribute__((cleanup(cleanup_buffer)))
 
 
3114
    buffer password = {};
 
 
3116
  bool password_is_read = false;
 
 
3117
  bool quit_now = false;
 
 
3118
  __attribute__((cleanup(cleanup_queue)))
 
 
3119
    task_queue *queue = create_queue();
 
 
3120
  g_assert_nonnull(queue);
 
 
3122
  task_context task = {
 
 
3123
    .func=read_mandos_client_output,
 
 
3126
    .password=&password,
 
 
3127
    .password_is_read=&password_is_read,
 
 
3128
    .quit_now=&quit_now,
 
 
3130
  task.func(task, queue);
 
 
3131
  g_assert_false(password_is_read);
 
 
3132
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3133
  g_assert_false(quit_now);
 
 
3134
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3136
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3137
        .func=read_mandos_client_output,
 
 
3140
        .password=&password,
 
 
3141
        .password_is_read=&password_is_read,
 
 
3142
        .quit_now=&quit_now,
 
 
3145
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3146
                                   EPOLLIN | EPOLLRDHUP));
 
 
3148
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3151
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
 
3152
                                               test_fixture *fixture,
 
 
3153
                                               __attribute__((unused))
 
 
3156
  __attribute__((cleanup(cleanup_close)))
 
 
3157
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3158
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3161
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3162
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3164
  __attribute__((cleanup(cleanup_buffer)))
 
 
3165
    buffer password = {};
 
 
3167
  bool password_is_read = false;
 
 
3168
  bool quit_now = false;
 
 
3169
  __attribute__((cleanup(cleanup_queue)))
 
 
3170
    task_queue *queue = create_queue();
 
 
3171
  g_assert_nonnull(queue);
 
 
3173
  task_context task = {
 
 
3174
    .func=read_mandos_client_output,
 
 
3177
    .password=&password,
 
 
3178
    .password_is_read=&password_is_read,
 
 
3179
    .quit_now=&quit_now,
 
 
3181
  task.func(task, queue);
 
 
3182
  g_assert_true(password_is_read);
 
 
3183
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3184
  g_assert_false(quit_now);
 
 
3185
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3187
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3189
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3193
void test_read_mandos_client_output_once(__attribute__((unused))
 
 
3194
                                         test_fixture *fixture,
 
 
3195
                                         __attribute__((unused))
 
 
3196
                                         gconstpointer user_data){
 
 
3197
  __attribute__((cleanup(cleanup_close)))
 
 
3198
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3199
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3202
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3204
  const char dummy_test_password[] = "dummy test password";
 
 
3205
  /* Start with a pre-allocated buffer */
 
 
3206
  __attribute__((cleanup(cleanup_buffer)))
 
 
3208
    .data=malloc(sizeof(dummy_test_password)),
 
 
3210
    .allocated=sizeof(dummy_test_password),
 
 
3212
  g_assert_nonnull(password.data);
 
 
3213
  if(mlock(password.data, password.allocated) != 0){
 
 
3214
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3217
  bool password_is_read = false;
 
 
3218
  bool quit_now = false;
 
 
3219
  __attribute__((cleanup(cleanup_queue)))
 
 
3220
    task_queue *queue = create_queue();
 
 
3221
  g_assert_nonnull(queue);
 
 
3223
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3224
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3225
                             sizeof(dummy_test_password)),
 
 
3226
                  ==, (int)sizeof(dummy_test_password));
 
 
3228
  task_context task = {
 
 
3229
    .func=read_mandos_client_output,
 
 
3232
    .password=&password,
 
 
3233
    .password_is_read=&password_is_read,
 
 
3234
    .quit_now=&quit_now,
 
 
3236
  task.func(task, queue);
 
 
3238
  g_assert_false(password_is_read);
 
 
3239
  g_assert_cmpint((int)password.length, ==,
 
 
3240
                  (int)sizeof(dummy_test_password));
 
 
3241
  g_assert_nonnull(password.data);
 
 
3242
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3243
                         sizeof(dummy_test_password)), ==, 0);
 
 
3245
  g_assert_false(quit_now);
 
 
3246
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3248
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3249
        .func=read_mandos_client_output,
 
 
3252
        .password=&password,
 
 
3253
        .password_is_read=&password_is_read,
 
 
3254
        .quit_now=&quit_now,
 
 
3257
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3258
                                   EPOLLIN | EPOLLRDHUP));
 
 
3260
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3264
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
 
3265
                                           test_fixture *fixture,
 
 
3266
                                           __attribute__((unused))
 
 
3267
                                           gconstpointer user_data){
 
 
3268
  __attribute__((cleanup(cleanup_close)))
 
 
3269
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3270
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3273
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3275
  const char dummy_test_password[] = "dummy test password";
 
 
3276
  /* Start with an empty buffer */
 
 
3277
  __attribute__((cleanup(cleanup_buffer)))
 
 
3278
    buffer password = {};
 
 
3280
  bool password_is_read = false;
 
 
3281
  bool quit_now = false;
 
 
3282
  __attribute__((cleanup(cleanup_queue)))
 
 
3283
    task_queue *queue = create_queue();
 
 
3284
  g_assert_nonnull(queue);
 
 
3286
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3287
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3288
                             sizeof(dummy_test_password)),
 
 
3289
                  ==, (int)sizeof(dummy_test_password));
 
 
3291
  task_context task = {
 
 
3292
    .func=read_mandos_client_output,
 
 
3295
    .password=&password,
 
 
3296
    .password_is_read=&password_is_read,
 
 
3297
    .quit_now=&quit_now,
 
 
3299
  task.func(task, queue);
 
 
3301
  g_assert_false(password_is_read);
 
 
3302
  g_assert_cmpint((int)password.length, ==,
 
 
3303
                  (int)sizeof(dummy_test_password));
 
 
3304
  g_assert_nonnull(password.data);
 
 
3305
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3306
                         sizeof(dummy_test_password)), ==, 0);
 
 
3308
  g_assert_false(quit_now);
 
 
3309
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3311
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3312
        .func=read_mandos_client_output,
 
 
3315
        .password=&password,
 
 
3316
        .password_is_read=&password_is_read,
 
 
3317
        .quit_now=&quit_now,
 
 
3320
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3321
                                   EPOLLIN | EPOLLRDHUP));
 
 
3323
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3327
void test_read_mandos_client_output_append(__attribute__((unused))
 
 
3328
                                           test_fixture *fixture,
 
 
3329
                                           __attribute__((unused))
 
 
3330
                                           gconstpointer user_data){
 
 
3331
  __attribute__((cleanup(cleanup_close)))
 
 
3332
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3333
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3336
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3338
  const char dummy_test_password[] = "dummy test password";
 
 
3339
  __attribute__((cleanup(cleanup_buffer)))
 
 
3341
    .data=malloc(PIPE_BUF),
 
 
3343
    .allocated=PIPE_BUF,
 
 
3345
  g_assert_nonnull(password.data);
 
 
3346
  if(mlock(password.data, password.allocated) != 0){
 
 
3347
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3350
  memset(password.data, 'x', PIPE_BUF);
 
 
3351
  char password_expected[PIPE_BUF];
 
 
3352
  memcpy(password_expected, password.data, PIPE_BUF);
 
 
3354
  bool password_is_read = false;
 
 
3355
  bool quit_now = false;
 
 
3356
  __attribute__((cleanup(cleanup_queue)))
 
 
3357
    task_queue *queue = create_queue();
 
 
3358
  g_assert_nonnull(queue);
 
 
3360
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3361
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3362
                             sizeof(dummy_test_password)),
 
 
3363
                  ==, (int)sizeof(dummy_test_password));
 
 
3365
  task_context task = {
 
 
3366
    .func=read_mandos_client_output,
 
 
3369
    .password=&password,
 
 
3370
    .password_is_read=&password_is_read,
 
 
3371
    .quit_now=&quit_now,
 
 
3373
  task.func(task, queue);
 
 
3375
  g_assert_false(password_is_read);
 
 
3376
  g_assert_cmpint((int)password.length, ==,
 
 
3377
                  PIPE_BUF + sizeof(dummy_test_password));
 
 
3378
  g_assert_nonnull(password.data);
 
 
3379
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
 
3381
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
 
3382
                         dummy_test_password,
 
 
3383
                         sizeof(dummy_test_password)), ==, 0);
 
 
3384
  g_assert_false(quit_now);
 
 
3385
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3387
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3388
        .func=read_mandos_client_output,
 
 
3391
        .password=&password,
 
 
3392
        .password_is_read=&password_is_read,
 
 
3393
        .quit_now=&quit_now,
 
 
3396
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3397
                                   EPOLLIN | EPOLLRDHUP));
 
 
3400
static char *make_temporary_directory(void);
 
 
3402
static void test_add_inotify_dir_watch(__attribute__((unused))
 
 
3403
                                       test_fixture *fixture,
 
 
3404
                                       __attribute__((unused))
 
 
3405
                                       gconstpointer user_data){
 
 
3406
  __attribute__((cleanup(cleanup_close)))
 
 
3407
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3408
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3409
  __attribute__((cleanup(cleanup_queue)))
 
 
3410
    task_queue *queue = create_queue();
 
 
3411
  g_assert_nonnull(queue);
 
 
3412
  __attribute__((cleanup(string_set_clear)))
 
 
3413
    string_set cancelled_filenames = {};
 
 
3414
  const mono_microsecs current_time = 0;
 
 
3416
  bool quit_now = false;
 
 
3417
  buffer password = {};
 
 
3418
  bool mandos_client_exited = false;
 
 
3419
  bool password_is_read = false;
 
 
3421
  __attribute__((cleanup(cleanup_string)))
 
 
3422
    char *tempdir = make_temporary_directory();
 
 
3423
  g_assert_nonnull(tempdir);
 
 
3425
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3427
                                      &cancelled_filenames,
 
 
3429
                                      &mandos_client_exited,
 
 
3430
                                      &password_is_read));
 
 
3432
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3434
  const task_context *const added_read_task
 
 
3435
    = find_matching_task(queue, (task_context){
 
 
3436
        .func=read_inotify_event,
 
 
3438
        .quit_now=&quit_now,
 
 
3439
        .password=&password,
 
 
3441
        .cancelled_filenames=&cancelled_filenames,
 
 
3442
        .current_time=¤t_time,
 
 
3443
        .mandos_client_exited=&mandos_client_exited,
 
 
3444
        .password_is_read=&password_is_read,
 
 
3446
  g_assert_nonnull(added_read_task);
 
 
3448
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3449
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3450
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
 
3451
                                   added_read_task->fd,
 
 
3452
                                   EPOLLIN | EPOLLRDHUP));
 
 
3454
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3457
static char *make_temporary_directory(void){
 
 
3458
  char *name = strdup("/tmp/mandosXXXXXX");
 
 
3459
  g_assert_nonnull(name);
 
 
3460
  char *result = mkdtemp(name);
 
 
3467
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
 
3468
                                            test_fixture *fixture,
 
 
3469
                                            __attribute__((unused))
 
 
3470
                                            gconstpointer user_data){
 
 
3471
  __attribute__((cleanup(cleanup_close)))
 
 
3472
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3473
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3474
  __attribute__((cleanup(cleanup_queue)))
 
 
3475
    task_queue *queue = create_queue();
 
 
3476
  g_assert_nonnull(queue);
 
 
3477
  __attribute__((cleanup(string_set_clear)))
 
 
3478
    string_set cancelled_filenames = {};
 
 
3479
  const mono_microsecs current_time = 0;
 
 
3481
  bool quit_now = false;
 
 
3482
  buffer password = {};
 
 
3483
  bool mandos_client_exited = false;
 
 
3484
  bool password_is_read = false;
 
 
3486
  const char nonexistent_dir[] = "/nonexistent";
 
 
3488
  FILE *real_stderr = stderr;
 
 
3489
  FILE *devnull = fopen("/dev/null", "we");
 
 
3490
  g_assert_nonnull(devnull);
 
 
3492
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3493
                                       &password, nonexistent_dir,
 
 
3494
                                       &cancelled_filenames,
 
 
3496
                                       &mandos_client_exited,
 
 
3497
                                       &password_is_read));
 
 
3498
  stderr = real_stderr;
 
 
3499
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3501
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3504
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
 
3505
                                              test_fixture *fixture,
 
 
3506
                                            __attribute__((unused))
 
 
3509
  __attribute__((cleanup(cleanup_close)))
 
 
3510
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3511
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3512
  __attribute__((cleanup(cleanup_queue)))
 
 
3513
    task_queue *queue = create_queue();
 
 
3514
  g_assert_nonnull(queue);
 
 
3515
  __attribute__((cleanup(string_set_clear)))
 
 
3516
    string_set cancelled_filenames = {};
 
 
3517
  const mono_microsecs current_time = 0;
 
 
3519
  bool quit_now = false;
 
 
3520
  buffer password = {};
 
 
3521
  bool mandos_client_exited = false;
 
 
3522
  bool password_is_read = false;
 
 
3524
  const char not_a_directory[] = "/dev/tty";
 
 
3526
  FILE *real_stderr = stderr;
 
 
3527
  FILE *devnull = fopen("/dev/null", "we");
 
 
3528
  g_assert_nonnull(devnull);
 
 
3530
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3531
                                       &password, not_a_directory,
 
 
3532
                                       &cancelled_filenames,
 
 
3534
                                       &mandos_client_exited,
 
 
3535
                                       &password_is_read));
 
 
3536
  stderr = real_stderr;
 
 
3537
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3539
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3542
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
 
3543
                                              test_fixture *fixture,
 
 
3544
                                              __attribute__((unused))
 
 
3547
  __attribute__((cleanup(cleanup_close)))
 
 
3548
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3549
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3550
  __attribute__((cleanup(cleanup_queue)))
 
 
3551
    task_queue *queue = create_queue();
 
 
3552
  g_assert_nonnull(queue);
 
 
3553
  __attribute__((cleanup(string_set_clear)))
 
 
3554
    string_set cancelled_filenames = {};
 
 
3555
  const mono_microsecs current_time = 0;
 
 
3557
  bool quit_now = false;
 
 
3558
  buffer password = {};
 
 
3559
  bool mandos_client_exited = false;
 
 
3560
  bool password_is_read = false;
 
 
3562
  __attribute__((cleanup(cleanup_string)))
 
 
3563
    char *tempdir = make_temporary_directory();
 
 
3564
  g_assert_nonnull(tempdir);
 
 
3566
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3568
                                      &cancelled_filenames,
 
 
3570
                                      &mandos_client_exited,
 
 
3571
                                      &password_is_read));
 
 
3573
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3575
  const task_context *const added_read_task
 
 
3576
    = find_matching_task(queue,
 
 
3577
                         (task_context){ .func=read_inotify_event });
 
 
3578
  g_assert_nonnull(added_read_task);
 
 
3580
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3581
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3583
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3584
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3586
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3587
  g_assert_nonnull(ievent);
 
 
3589
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
 
3591
  g_assert_cmpint(errno, ==, EAGAIN);
 
 
3595
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3598
static char *make_temporary_file_in_directory(const char
 
 
3602
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3603
                                               test_fixture *fixture,
 
 
3604
                                               __attribute__((unused))
 
 
3607
  __attribute__((cleanup(cleanup_close)))
 
 
3608
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3609
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3610
  __attribute__((cleanup(cleanup_queue)))
 
 
3611
    task_queue *queue = create_queue();
 
 
3612
  g_assert_nonnull(queue);
 
 
3613
  __attribute__((cleanup(string_set_clear)))
 
 
3614
    string_set cancelled_filenames = {};
 
 
3615
  const mono_microsecs current_time = 0;
 
 
3617
  bool quit_now = false;
 
 
3618
  buffer password = {};
 
 
3619
  bool mandos_client_exited = false;
 
 
3620
  bool password_is_read = false;
 
 
3622
  __attribute__((cleanup(cleanup_string)))
 
 
3623
    char *tempdir = make_temporary_directory();
 
 
3624
  g_assert_nonnull(tempdir);
 
 
3626
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3628
                                      &cancelled_filenames,
 
 
3630
                                      &mandos_client_exited,
 
 
3631
                                      &password_is_read));
 
 
3633
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3635
  const task_context *const added_read_task
 
 
3636
    = find_matching_task(queue,
 
 
3637
                         (task_context){ .func=read_inotify_event });
 
 
3638
  g_assert_nonnull(added_read_task);
 
 
3640
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3641
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3643
  __attribute__((cleanup(cleanup_string)))
 
 
3644
    char *filename = make_temporary_file_in_directory(tempdir);
 
 
3645
  g_assert_nonnull(filename);
 
 
3647
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3648
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3650
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3651
  g_assert_nonnull(ievent);
 
 
3653
  ssize_t read_size = 0;
 
 
3654
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3656
  g_assert_cmpint((int)read_size, >, 0);
 
 
3657
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
 
3658
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
 
3662
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
3663
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3666
static char *make_temporary_prefixed_file_in_directory(const char
 
 
3670
  char *filename = NULL;
 
 
3671
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
 
3673
  g_assert_nonnull(filename);
 
 
3674
  const int fd = mkostemp(filename, O_CLOEXEC);
 
 
3675
  g_assert_cmpint(fd, >=, 0);
 
 
3676
  g_assert_cmpint(close(fd), ==, 0);
 
 
3680
static char *make_temporary_file_in_directory(const char
 
 
3682
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
 
3686
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
 
3687
                                            test_fixture *fixture,
 
 
3688
                                            __attribute__((unused))
 
 
3689
                                            gconstpointer user_data){
 
 
3690
  __attribute__((cleanup(cleanup_close)))
 
 
3691
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3692
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3693
  __attribute__((cleanup(cleanup_queue)))
 
 
3694
    task_queue *queue = create_queue();
 
 
3695
  g_assert_nonnull(queue);
 
 
3696
  __attribute__((cleanup(string_set_clear)))
 
 
3697
    string_set cancelled_filenames = {};
 
 
3698
  const mono_microsecs current_time = 0;
 
 
3700
  bool quit_now = false;
 
 
3701
  buffer password = {};
 
 
3702
  bool mandos_client_exited = false;
 
 
3703
  bool password_is_read = false;
 
 
3705
  __attribute__((cleanup(cleanup_string)))
 
 
3706
    char *watchdir = make_temporary_directory();
 
 
3707
  g_assert_nonnull(watchdir);
 
 
3709
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3710
                                      &password, watchdir,
 
 
3711
                                      &cancelled_filenames,
 
 
3713
                                      &mandos_client_exited,
 
 
3714
                                      &password_is_read));
 
 
3716
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3718
  const task_context *const added_read_task
 
 
3719
    = find_matching_task(queue,
 
 
3720
                         (task_context){ .func=read_inotify_event });
 
 
3721
  g_assert_nonnull(added_read_task);
 
 
3723
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3724
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3726
  char *sourcedir = make_temporary_directory();
 
 
3727
  g_assert_nonnull(sourcedir);
 
 
3729
  __attribute__((cleanup(cleanup_string)))
 
 
3730
    char *filename = make_temporary_file_in_directory(sourcedir);
 
 
3731
  g_assert_nonnull(filename);
 
 
3733
  __attribute__((cleanup(cleanup_string)))
 
 
3734
    char *targetfilename = NULL;
 
 
3735
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
 
3736
                           basename(filename)), >, 0);
 
 
3737
  g_assert_nonnull(targetfilename);
 
 
3739
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
 
3740
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
 
3743
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3744
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3746
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3747
  g_assert_nonnull(ievent);
 
 
3749
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3751
  g_assert_cmpint((int)read_size, >, 0);
 
 
3752
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
 
3753
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
 
3757
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3758
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
 
3762
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
 
3763
                                              test_fixture *fixture,
 
 
3764
                                              __attribute__((unused))
 
 
3767
  __attribute__((cleanup(cleanup_close)))
 
 
3768
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3769
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3770
  __attribute__((cleanup(cleanup_queue)))
 
 
3771
    task_queue *queue = create_queue();
 
 
3772
  g_assert_nonnull(queue);
 
 
3773
  __attribute__((cleanup(string_set_clear)))
 
 
3774
    string_set cancelled_filenames = {};
 
 
3775
  const mono_microsecs current_time = 0;
 
 
3777
  bool quit_now = false;
 
 
3778
  buffer password = {};
 
 
3779
  bool mandos_client_exited = false;
 
 
3780
  bool password_is_read = false;
 
 
3782
  __attribute__((cleanup(cleanup_string)))
 
 
3783
    char *tempdir = make_temporary_directory();
 
 
3784
  g_assert_nonnull(tempdir);
 
 
3786
  __attribute__((cleanup(cleanup_string)))
 
 
3787
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
 
3788
  g_assert_nonnull(tempfilename);
 
 
3790
  __attribute__((cleanup(cleanup_string)))
 
 
3791
    char *targetdir = make_temporary_directory();
 
 
3792
  g_assert_nonnull(targetdir);
 
 
3794
  __attribute__((cleanup(cleanup_string)))
 
 
3795
    char *targetfilename = NULL;
 
 
3796
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
 
3797
                           basename(tempfilename)), >, 0);
 
 
3798
  g_assert_nonnull(targetfilename);
 
 
3800
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3802
                                      &cancelled_filenames,
 
 
3804
                                      &mandos_client_exited,
 
 
3805
                                      &password_is_read));
 
 
3807
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
 
3809
  const task_context *const added_read_task
 
 
3810
    = find_matching_task(queue,
 
 
3811
                         (task_context){ .func=read_inotify_event });
 
 
3812
  g_assert_nonnull(added_read_task);
 
 
3814
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3815
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3817
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3818
  g_assert_nonnull(ievent);
 
 
3820
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3822
  g_assert_cmpint((int)read_size, >, 0);
 
 
3823
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
 
3824
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
 
3828
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3829
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
 
3830
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3834
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
 
3835
                                          test_fixture *fixture,
 
 
3836
                                          __attribute__((unused))
 
 
3837
                                          gconstpointer user_data){
 
 
3838
  __attribute__((cleanup(cleanup_close)))
 
 
3839
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3840
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3841
  __attribute__((cleanup(cleanup_queue)))
 
 
3842
    task_queue *queue = create_queue();
 
 
3843
  g_assert_nonnull(queue);
 
 
3844
  __attribute__((cleanup(string_set_clear)))
 
 
3845
    string_set cancelled_filenames = {};
 
 
3846
  const mono_microsecs current_time = 0;
 
 
3848
  bool quit_now = false;
 
 
3849
  buffer password = {};
 
 
3850
  bool mandos_client_exited = false;
 
 
3851
  bool password_is_read = false;
 
 
3853
  __attribute__((cleanup(cleanup_string)))
 
 
3854
    char *tempdir = make_temporary_directory();
 
 
3855
  g_assert_nonnull(tempdir);
 
 
3857
  __attribute__((cleanup(cleanup_string)))
 
 
3858
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3859
  g_assert_nonnull(tempfile);
 
 
3861
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3863
                                      &cancelled_filenames,
 
 
3865
                                      &mandos_client_exited,
 
 
3866
                                      &password_is_read));
 
 
3867
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3869
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3871
  const task_context *const added_read_task
 
 
3872
    = find_matching_task(queue,
 
 
3873
                         (task_context){ .func=read_inotify_event });
 
 
3874
  g_assert_nonnull(added_read_task);
 
 
3876
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3877
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3879
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3880
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3882
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3883
  g_assert_nonnull(ievent);
 
 
3885
  ssize_t read_size = 0;
 
 
3886
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3888
  g_assert_cmpint((int)read_size, >, 0);
 
 
3889
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3890
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3894
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3898
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
 
3899
                                               test_fixture *fixture,
 
 
3900
                                               __attribute__((unused))
 
 
3903
  __attribute__((cleanup(cleanup_close)))
 
 
3904
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3905
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3906
  __attribute__((cleanup(cleanup_queue)))
 
 
3907
    task_queue *queue = create_queue();
 
 
3908
  g_assert_nonnull(queue);
 
 
3909
  __attribute__((cleanup(string_set_clear)))
 
 
3910
    string_set cancelled_filenames = {};
 
 
3911
  const mono_microsecs current_time = 0;
 
 
3913
  bool quit_now = false;
 
 
3914
  buffer password = {};
 
 
3915
  bool mandos_client_exited = false;
 
 
3916
  bool password_is_read = false;
 
 
3918
  __attribute__((cleanup(cleanup_string)))
 
 
3919
    char *tempdir = make_temporary_directory();
 
 
3920
  g_assert_nonnull(tempdir);
 
 
3922
  __attribute__((cleanup(cleanup_string)))
 
 
3923
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3924
  g_assert_nonnull(tempfile);
 
 
3925
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
 
3927
  g_assert_cmpint(tempfile_fd, >, 2);
 
 
3929
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3931
                                      &cancelled_filenames,
 
 
3933
                                      &mandos_client_exited,
 
 
3934
                                      &password_is_read));
 
 
3935
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3937
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3939
  const task_context *const added_read_task
 
 
3940
    = find_matching_task(queue,
 
 
3941
                         (task_context){ .func=read_inotify_event });
 
 
3942
  g_assert_nonnull(added_read_task);
 
 
3944
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3945
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3947
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3948
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3950
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3951
  g_assert_nonnull(ievent);
 
 
3953
  ssize_t read_size = 0;
 
 
3954
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3956
  g_assert_cmpint((int)read_size, >, 0);
 
 
3957
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3958
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3960
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
 
3962
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
 
3963
     file not appear as an ievent, so we should not see it now. */
 
 
3964
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3965
  g_assert_cmpint((int)read_size, ==, -1);
 
 
3966
  g_assert_true(errno == EAGAIN);
 
 
3970
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3973
static void test_read_inotify_event_readerror(__attribute__((unused))
 
 
3974
                                              test_fixture *fixture,
 
 
3975
                                              __attribute__((unused))
 
 
3978
  __attribute__((cleanup(cleanup_close)))
 
 
3979
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3980
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3981
  const mono_microsecs current_time = 0;
 
 
3983
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
 
3984
  const int fd = open("/proc/self/mem",
 
 
3985
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3987
  bool quit_now = false;
 
 
3988
  __attribute__((cleanup(cleanup_queue)))
 
 
3989
    task_queue *queue = create_queue();
 
 
3990
  g_assert_nonnull(queue);
 
 
3992
  task_context task = {
 
 
3993
    .func=read_inotify_event,
 
 
3996
    .quit_now=&quit_now,
 
 
3997
    .filename=strdup("/nonexistent"),
 
 
3998
    .cancelled_filenames = &(string_set){},
 
 
4000
    .current_time=¤t_time,
 
 
4002
  g_assert_nonnull(task.filename);
 
 
4003
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4004
  g_assert_true(quit_now);
 
 
4005
  g_assert_true(queue->next_run == 0);
 
 
4006
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4008
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
4010
  g_assert_cmpint(close(fd), ==, -1);
 
 
4013
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
 
4014
                                              test_fixture *fixture,
 
 
4015
                                              __attribute__((unused))
 
 
4018
  const mono_microsecs current_time = 17;
 
 
4021
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4022
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
 
4024
  bool quit_now = false;
 
 
4025
  buffer password = {};
 
 
4026
  bool mandos_client_exited = false;
 
 
4027
  bool password_is_read = false;
 
 
4028
  __attribute__((cleanup(cleanup_queue)))
 
 
4029
    task_queue *queue = create_queue();
 
 
4030
  g_assert_nonnull(queue);
 
 
4032
  task_context task = {
 
 
4033
    .func=read_inotify_event,
 
 
4036
    .quit_now=&quit_now,
 
 
4037
    .password=&password,
 
 
4038
    .filename=strdup("/nonexistent"),
 
 
4039
    .cancelled_filenames = &(string_set){},
 
 
4041
    .current_time=¤t_time,
 
 
4042
    .mandos_client_exited=&mandos_client_exited,
 
 
4043
    .password_is_read=&password_is_read,
 
 
4045
  g_assert_nonnull(task.filename);
 
 
4046
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4048
  g_assert_nonnull(find_matching_task(queue, task));
 
 
4049
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
4051
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
 
4052
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4055
static void test_read_inotify_event_nodata(__attribute__((unused))
 
 
4056
                                           test_fixture *fixture,
 
 
4057
                                           __attribute__((unused))
 
 
4058
                                           gconstpointer user_data){
 
 
4059
  __attribute__((cleanup(cleanup_close)))
 
 
4060
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4061
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4062
  const mono_microsecs current_time = 0;
 
 
4065
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4067
  bool quit_now = false;
 
 
4068
  buffer password = {};
 
 
4069
  bool mandos_client_exited = false;
 
 
4070
  bool password_is_read = false;
 
 
4071
  __attribute__((cleanup(cleanup_queue)))
 
 
4072
    task_queue *queue = create_queue();
 
 
4073
  g_assert_nonnull(queue);
 
 
4075
  task_context task = {
 
 
4076
    .func=read_inotify_event,
 
 
4079
    .quit_now=&quit_now,
 
 
4080
    .password=&password,
 
 
4081
    .filename=strdup("/nonexistent"),
 
 
4082
    .cancelled_filenames = &(string_set){},
 
 
4084
    .current_time=¤t_time,
 
 
4085
    .mandos_client_exited=&mandos_client_exited,
 
 
4086
    .password_is_read=&password_is_read,
 
 
4088
  g_assert_nonnull(task.filename);
 
 
4089
  task.func(task, queue);
 
 
4090
  g_assert_false(quit_now);
 
 
4091
  g_assert_true(queue->next_run == 0);
 
 
4092
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4094
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4095
        .func=read_inotify_event,
 
 
4098
        .quit_now=&quit_now,
 
 
4099
        .password=&password,
 
 
4100
        .filename=task.filename,
 
 
4101
        .cancelled_filenames=task.cancelled_filenames,
 
 
4102
        .current_time=¤t_time,
 
 
4103
        .mandos_client_exited=&mandos_client_exited,
 
 
4104
        .password_is_read=&password_is_read,
 
 
4107
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4108
                                   EPOLLIN | EPOLLRDHUP));
 
 
4110
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4113
static void test_read_inotify_event_eof(__attribute__((unused))
 
 
4114
                                        test_fixture *fixture,
 
 
4115
                                        __attribute__((unused))
 
 
4116
                                        gconstpointer user_data){
 
 
4117
  __attribute__((cleanup(cleanup_close)))
 
 
4118
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4119
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4120
  const mono_microsecs current_time = 0;
 
 
4123
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4124
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4126
  bool quit_now = false;
 
 
4127
  buffer password = {};
 
 
4128
  __attribute__((cleanup(cleanup_queue)))
 
 
4129
    task_queue *queue = create_queue();
 
 
4130
  g_assert_nonnull(queue);
 
 
4132
  task_context task = {
 
 
4133
    .func=read_inotify_event,
 
 
4136
    .quit_now=&quit_now,
 
 
4137
    .password=&password,
 
 
4138
    .filename=strdup("/nonexistent"),
 
 
4139
    .cancelled_filenames = &(string_set){},
 
 
4141
    .current_time=¤t_time,
 
 
4143
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4144
  g_assert_true(quit_now);
 
 
4145
  g_assert_true(queue->next_run == 0);
 
 
4146
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4148
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
4150
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
4154
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
 
4155
                                            test_fixture *fixture,
 
 
4156
                                            __attribute__((unused))
 
 
4157
                                            gconstpointer user_data){
 
 
4158
  __attribute__((cleanup(cleanup_close)))
 
 
4159
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4160
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4161
  const mono_microsecs current_time = 0;
 
 
4164
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4166
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4167
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4169
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4171
    struct inotify_event event;
 
 
4172
    char name_buffer[NAME_MAX + 1];
 
 
4174
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4176
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4177
  ievent->mask = IN_CLOSE_WRITE;
 
 
4178
  ievent->len = sizeof(dummy_file_name);
 
 
4179
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4180
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4181
                              + sizeof(dummy_file_name));
 
 
4182
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4184
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4186
  bool quit_now = false;
 
 
4187
  buffer password = {};
 
 
4188
  bool mandos_client_exited = false;
 
 
4189
  bool password_is_read = false;
 
 
4190
  __attribute__((cleanup(cleanup_queue)))
 
 
4191
    task_queue *queue = create_queue();
 
 
4192
  g_assert_nonnull(queue);
 
 
4194
  task_context task = {
 
 
4195
    .func=read_inotify_event,
 
 
4198
    .quit_now=&quit_now,
 
 
4199
    .password=&password,
 
 
4200
    .filename=strdup("/nonexistent"),
 
 
4201
    .cancelled_filenames = &(string_set){},
 
 
4203
    .current_time=¤t_time,
 
 
4204
    .mandos_client_exited=&mandos_client_exited,
 
 
4205
    .password_is_read=&password_is_read,
 
 
4207
  task.func(task, queue);
 
 
4208
  g_assert_false(quit_now);
 
 
4209
  g_assert_true(queue->next_run != 0);
 
 
4210
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4212
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4213
        .func=read_inotify_event,
 
 
4216
        .quit_now=&quit_now,
 
 
4217
        .password=&password,
 
 
4218
        .filename=task.filename,
 
 
4219
        .cancelled_filenames=task.cancelled_filenames,
 
 
4220
        .current_time=¤t_time,
 
 
4221
        .mandos_client_exited=&mandos_client_exited,
 
 
4222
        .password_is_read=&password_is_read,
 
 
4225
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4226
                                   EPOLLIN | EPOLLRDHUP));
 
 
4228
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4230
  __attribute__((cleanup(cleanup_string)))
 
 
4231
    char *filename = NULL;
 
 
4232
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4233
                           dummy_file_name), >, 0);
 
 
4234
  g_assert_nonnull(filename);
 
 
4235
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4236
        .func=open_and_parse_question,
 
 
4239
        .question_filename=filename,
 
 
4240
        .password=&password,
 
 
4241
        .cancelled_filenames=task.cancelled_filenames,
 
 
4242
        .current_time=¤t_time,
 
 
4243
        .mandos_client_exited=&mandos_client_exited,
 
 
4244
        .password_is_read=&password_is_read,
 
 
4249
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
 
4250
                                         test_fixture *fixture,
 
 
4251
                                         __attribute__((unused))
 
 
4252
                                         gconstpointer user_data){
 
 
4253
  __attribute__((cleanup(cleanup_close)))
 
 
4254
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4255
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4256
  const mono_microsecs current_time = 0;
 
 
4259
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4261
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4262
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4264
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4266
    struct inotify_event event;
 
 
4267
    char name_buffer[NAME_MAX + 1];
 
 
4269
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4271
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4272
  ievent->mask = IN_MOVED_TO;
 
 
4273
  ievent->len = sizeof(dummy_file_name);
 
 
4274
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4275
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4276
                              + sizeof(dummy_file_name));
 
 
4277
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4279
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4281
  bool quit_now = false;
 
 
4282
  buffer password = {};
 
 
4283
  bool mandos_client_exited = false;
 
 
4284
  bool password_is_read = false;
 
 
4285
  __attribute__((cleanup(cleanup_queue)))
 
 
4286
    task_queue *queue = create_queue();
 
 
4287
  g_assert_nonnull(queue);
 
 
4289
  task_context task = {
 
 
4290
    .func=read_inotify_event,
 
 
4293
    .quit_now=&quit_now,
 
 
4294
    .password=&password,
 
 
4295
    .filename=strdup("/nonexistent"),
 
 
4296
    .cancelled_filenames = &(string_set){},
 
 
4298
    .current_time=¤t_time,
 
 
4299
    .mandos_client_exited=&mandos_client_exited,
 
 
4300
    .password_is_read=&password_is_read,
 
 
4302
  task.func(task, queue);
 
 
4303
  g_assert_false(quit_now);
 
 
4304
  g_assert_true(queue->next_run != 0);
 
 
4305
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4307
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4308
        .func=read_inotify_event,
 
 
4311
        .quit_now=&quit_now,
 
 
4312
        .password=&password,
 
 
4313
        .filename=task.filename,
 
 
4314
        .cancelled_filenames=task.cancelled_filenames,
 
 
4315
        .current_time=¤t_time,
 
 
4316
        .mandos_client_exited=&mandos_client_exited,
 
 
4317
        .password_is_read=&password_is_read,
 
 
4320
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4321
                                   EPOLLIN | EPOLLRDHUP));
 
 
4323
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4325
  __attribute__((cleanup(cleanup_string)))
 
 
4326
    char *filename = NULL;
 
 
4327
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4328
                           dummy_file_name), >, 0);
 
 
4329
  g_assert_nonnull(filename);
 
 
4330
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4331
        .func=open_and_parse_question,
 
 
4334
        .question_filename=filename,
 
 
4335
        .password=&password,
 
 
4336
        .cancelled_filenames=task.cancelled_filenames,
 
 
4337
        .current_time=¤t_time,
 
 
4338
        .mandos_client_exited=&mandos_client_exited,
 
 
4339
        .password_is_read=&password_is_read,
 
 
4344
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
 
4345
                                           test_fixture *fixture,
 
 
4346
                                           __attribute__((unused))
 
 
4347
                                           gconstpointer user_data){
 
 
4348
  __attribute__((cleanup(cleanup_close)))
 
 
4349
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4350
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4351
  __attribute__((cleanup(string_set_clear)))
 
 
4352
    string_set cancelled_filenames = {};
 
 
4353
  const mono_microsecs current_time = 0;
 
 
4356
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4358
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4359
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4361
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4363
    struct inotify_event event;
 
 
4364
    char name_buffer[NAME_MAX + 1];
 
 
4366
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4368
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4369
  ievent->mask = IN_MOVED_FROM;
 
 
4370
  ievent->len = sizeof(dummy_file_name);
 
 
4371
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4372
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4373
                              + sizeof(dummy_file_name));
 
 
4374
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4376
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4378
  bool quit_now = false;
 
 
4379
  buffer password = {};
 
 
4380
  bool mandos_client_exited = false;
 
 
4381
  bool password_is_read = false;
 
 
4382
  __attribute__((cleanup(cleanup_queue)))
 
 
4383
    task_queue *queue = create_queue();
 
 
4384
  g_assert_nonnull(queue);
 
 
4386
  task_context task = {
 
 
4387
    .func=read_inotify_event,
 
 
4390
    .quit_now=&quit_now,
 
 
4391
    .password=&password,
 
 
4392
    .filename=strdup("/nonexistent"),
 
 
4393
    .cancelled_filenames=&cancelled_filenames,
 
 
4394
    .current_time=¤t_time,
 
 
4395
    .mandos_client_exited=&mandos_client_exited,
 
 
4396
    .password_is_read=&password_is_read,
 
 
4398
  task.func(task, queue);
 
 
4399
  g_assert_false(quit_now);
 
 
4400
  g_assert_true(queue->next_run == 0);
 
 
4401
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4403
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4404
        .func=read_inotify_event,
 
 
4407
        .quit_now=&quit_now,
 
 
4408
        .password=&password,
 
 
4409
        .filename=task.filename,
 
 
4410
        .cancelled_filenames=&cancelled_filenames,
 
 
4411
        .current_time=¤t_time,
 
 
4412
        .mandos_client_exited=&mandos_client_exited,
 
 
4413
        .password_is_read=&password_is_read,
 
 
4416
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4417
                                   EPOLLIN | EPOLLRDHUP));
 
 
4419
  __attribute__((cleanup(cleanup_string)))
 
 
4420
    char *filename = NULL;
 
 
4421
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4422
                           dummy_file_name), >, 0);
 
 
4423
  g_assert_nonnull(filename);
 
 
4424
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4428
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
 
4429
                                              test_fixture *fixture,
 
 
4430
                                              __attribute__((unused))
 
 
4433
  __attribute__((cleanup(cleanup_close)))
 
 
4434
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4435
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4436
  __attribute__((cleanup(string_set_clear)))
 
 
4437
    string_set cancelled_filenames = {};
 
 
4438
  const mono_microsecs current_time = 0;
 
 
4441
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4443
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4444
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4446
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4448
    struct inotify_event event;
 
 
4449
    char name_buffer[NAME_MAX + 1];
 
 
4451
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4453
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4454
  ievent->mask = IN_DELETE;
 
 
4455
  ievent->len = sizeof(dummy_file_name);
 
 
4456
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4457
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4458
                              + sizeof(dummy_file_name));
 
 
4459
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4461
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4463
  bool quit_now = false;
 
 
4464
  buffer password = {};
 
 
4465
  bool mandos_client_exited = false;
 
 
4466
  bool password_is_read = false;
 
 
4467
  __attribute__((cleanup(cleanup_queue)))
 
 
4468
    task_queue *queue = create_queue();
 
 
4469
  g_assert_nonnull(queue);
 
 
4471
  task_context task = {
 
 
4472
    .func=read_inotify_event,
 
 
4475
    .quit_now=&quit_now,
 
 
4476
    .password=&password,
 
 
4477
    .filename=strdup("/nonexistent"),
 
 
4478
    .cancelled_filenames=&cancelled_filenames,
 
 
4479
    .current_time=¤t_time,
 
 
4480
    .mandos_client_exited=&mandos_client_exited,
 
 
4481
    .password_is_read=&password_is_read,
 
 
4483
  task.func(task, queue);
 
 
4484
  g_assert_false(quit_now);
 
 
4485
  g_assert_true(queue->next_run == 0);
 
 
4486
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4488
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4489
        .func=read_inotify_event,
 
 
4492
        .quit_now=&quit_now,
 
 
4493
        .password=&password,
 
 
4494
        .filename=task.filename,
 
 
4495
        .cancelled_filenames=&cancelled_filenames,
 
 
4496
        .current_time=¤t_time,
 
 
4497
        .mandos_client_exited=&mandos_client_exited,
 
 
4498
        .password_is_read=&password_is_read,
 
 
4501
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4502
                                   EPOLLIN | EPOLLRDHUP));
 
 
4504
  __attribute__((cleanup(cleanup_string)))
 
 
4505
    char *filename = NULL;
 
 
4506
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4507
                           dummy_file_name), >, 0);
 
 
4508
  g_assert_nonnull(filename);
 
 
4509
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4514
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
 
4515
                                               test_fixture *fixture,
 
 
4516
                                               __attribute__((unused))
 
 
4519
  __attribute__((cleanup(cleanup_close)))
 
 
4520
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4521
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4522
  const mono_microsecs current_time = 0;
 
 
4525
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4527
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4528
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4530
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4532
    struct inotify_event event;
 
 
4533
    char name_buffer[NAME_MAX + 1];
 
 
4535
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4537
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4538
  ievent->mask = IN_CLOSE_WRITE;
 
 
4539
  ievent->len = sizeof(dummy_file_name);
 
 
4540
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4541
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4542
                              + sizeof(dummy_file_name));
 
 
4543
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4545
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4547
  bool quit_now = false;
 
 
4548
  buffer password = {};
 
 
4549
  bool mandos_client_exited = false;
 
 
4550
  bool password_is_read = false;
 
 
4551
  __attribute__((cleanup(cleanup_queue)))
 
 
4552
    task_queue *queue = create_queue();
 
 
4553
  g_assert_nonnull(queue);
 
 
4555
  task_context task = {
 
 
4556
    .func=read_inotify_event,
 
 
4559
    .quit_now=&quit_now,
 
 
4560
    .password=&password,
 
 
4561
    .filename=strdup("/nonexistent"),
 
 
4562
    .cancelled_filenames = &(string_set){},
 
 
4564
    .current_time=¤t_time,
 
 
4565
    .mandos_client_exited=&mandos_client_exited,
 
 
4566
    .password_is_read=&password_is_read,
 
 
4568
  task.func(task, queue);
 
 
4569
  g_assert_false(quit_now);
 
 
4570
  g_assert_true(queue->next_run == 0);
 
 
4571
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4573
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4574
        .func=read_inotify_event,
 
 
4577
        .quit_now=&quit_now,
 
 
4578
        .password=&password,
 
 
4579
        .filename=task.filename,
 
 
4580
        .cancelled_filenames=task.cancelled_filenames,
 
 
4581
        .current_time=¤t_time,
 
 
4582
        .mandos_client_exited=&mandos_client_exited,
 
 
4583
        .password_is_read=&password_is_read,
 
 
4586
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4587
                                   EPOLLIN | EPOLLRDHUP));
 
 
4591
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
 
4592
                                            test_fixture *fixture,
 
 
4593
                                            __attribute__((unused))
 
 
4594
                                            gconstpointer user_data){
 
 
4595
  __attribute__((cleanup(cleanup_close)))
 
 
4596
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4597
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4598
  const mono_microsecs current_time = 0;
 
 
4601
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4603
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4604
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4606
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4608
    struct inotify_event event;
 
 
4609
    char name_buffer[NAME_MAX + 1];
 
 
4611
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4613
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4614
  ievent->mask = IN_MOVED_TO;
 
 
4615
  ievent->len = sizeof(dummy_file_name);
 
 
4616
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4617
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4618
                              + sizeof(dummy_file_name));
 
 
4619
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4621
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4623
  bool quit_now = false;
 
 
4624
  buffer password = {};
 
 
4625
  bool mandos_client_exited = false;
 
 
4626
  bool password_is_read = false;
 
 
4627
  __attribute__((cleanup(cleanup_queue)))
 
 
4628
    task_queue *queue = create_queue();
 
 
4629
  g_assert_nonnull(queue);
 
 
4631
  task_context task = {
 
 
4632
    .func=read_inotify_event,
 
 
4635
    .quit_now=&quit_now,
 
 
4636
    .password=&password,
 
 
4637
    .filename=strdup("/nonexistent"),
 
 
4638
    .cancelled_filenames = &(string_set){},
 
 
4640
    .current_time=¤t_time,
 
 
4641
    .mandos_client_exited=&mandos_client_exited,
 
 
4642
    .password_is_read=&password_is_read,
 
 
4644
  task.func(task, queue);
 
 
4645
  g_assert_false(quit_now);
 
 
4646
  g_assert_true(queue->next_run == 0);
 
 
4647
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4649
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4650
        .func=read_inotify_event,
 
 
4653
        .quit_now=&quit_now,
 
 
4654
        .password=&password,
 
 
4655
        .filename=task.filename,
 
 
4656
        .cancelled_filenames=task.cancelled_filenames,
 
 
4657
        .current_time=¤t_time,
 
 
4658
        .mandos_client_exited=&mandos_client_exited,
 
 
4659
        .password_is_read=&password_is_read,
 
 
4662
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4663
                                   EPOLLIN | EPOLLRDHUP));
 
 
4667
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
 
4668
                                              test_fixture *fixture,
 
 
4669
                                              __attribute__((unused))
 
 
4672
  __attribute__((cleanup(cleanup_close)))
 
 
4673
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4674
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4675
  __attribute__((cleanup(string_set_clear)))
 
 
4676
    string_set cancelled_filenames = {};
 
 
4677
  const mono_microsecs current_time = 0;
 
 
4680
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4682
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4683
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4685
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4687
    struct inotify_event event;
 
 
4688
    char name_buffer[NAME_MAX + 1];
 
 
4690
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4692
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4693
  ievent->mask = IN_MOVED_FROM;
 
 
4694
  ievent->len = sizeof(dummy_file_name);
 
 
4695
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4696
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4697
                              + sizeof(dummy_file_name));
 
 
4698
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4700
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4702
  bool quit_now = false;
 
 
4703
  buffer password = {};
 
 
4704
  bool mandos_client_exited = false;
 
 
4705
  bool password_is_read = false;
 
 
4706
  __attribute__((cleanup(cleanup_queue)))
 
 
4707
    task_queue *queue = create_queue();
 
 
4708
  g_assert_nonnull(queue);
 
 
4710
  task_context task = {
 
 
4711
    .func=read_inotify_event,
 
 
4714
    .quit_now=&quit_now,
 
 
4715
    .password=&password,
 
 
4716
    .filename=strdup("/nonexistent"),
 
 
4717
    .cancelled_filenames=&cancelled_filenames,
 
 
4718
    .current_time=¤t_time,
 
 
4719
    .mandos_client_exited=&mandos_client_exited,
 
 
4720
    .password_is_read=&password_is_read,
 
 
4722
  task.func(task, queue);
 
 
4723
  g_assert_false(quit_now);
 
 
4724
  g_assert_true(queue->next_run == 0);
 
 
4725
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4727
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4728
        .func=read_inotify_event,
 
 
4731
        .quit_now=&quit_now,
 
 
4732
        .password=&password,
 
 
4733
        .filename=task.filename,
 
 
4734
        .cancelled_filenames=&cancelled_filenames,
 
 
4735
        .current_time=¤t_time,
 
 
4736
        .mandos_client_exited=&mandos_client_exited,
 
 
4737
        .password_is_read=&password_is_read,
 
 
4740
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4741
                                   EPOLLIN | EPOLLRDHUP));
 
 
4743
  __attribute__((cleanup(cleanup_string)))
 
 
4744
    char *filename = NULL;
 
 
4745
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4746
                           dummy_file_name), >, 0);
 
 
4747
  g_assert_nonnull(filename);
 
 
4748
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4752
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
 
4753
                                               test_fixture *fixture,
 
 
4754
                                               __attribute__((unused))
 
 
4757
  __attribute__((cleanup(cleanup_close)))
 
 
4758
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4759
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4760
  __attribute__((cleanup(string_set_clear)))
 
 
4761
    string_set cancelled_filenames = {};
 
 
4762
  const mono_microsecs current_time = 0;
 
 
4765
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4767
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4768
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4770
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4772
    struct inotify_event event;
 
 
4773
    char name_buffer[NAME_MAX + 1];
 
 
4775
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4777
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4778
  ievent->mask = IN_DELETE;
 
 
4779
  ievent->len = sizeof(dummy_file_name);
 
 
4780
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4781
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4782
                              + sizeof(dummy_file_name));
 
 
4783
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4785
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4787
  bool quit_now = false;
 
 
4788
  buffer password = {};
 
 
4789
  bool mandos_client_exited = false;
 
 
4790
  bool password_is_read = false;
 
 
4791
  __attribute__((cleanup(cleanup_queue)))
 
 
4792
    task_queue *queue = create_queue();
 
 
4793
  g_assert_nonnull(queue);
 
 
4795
  task_context task = {
 
 
4796
    .func=read_inotify_event,
 
 
4799
    .quit_now=&quit_now,
 
 
4800
    .password=&password,
 
 
4801
    .filename=strdup("/nonexistent"),
 
 
4802
    .cancelled_filenames=&cancelled_filenames,
 
 
4803
    .current_time=¤t_time,
 
 
4804
    .mandos_client_exited=&mandos_client_exited,
 
 
4805
    .password_is_read=&password_is_read,
 
 
4807
  task.func(task, queue);
 
 
4808
  g_assert_false(quit_now);
 
 
4809
  g_assert_true(queue->next_run == 0);
 
 
4810
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4812
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4813
        .func=read_inotify_event,
 
 
4816
        .quit_now=&quit_now,
 
 
4817
        .password=&password,
 
 
4818
        .filename=task.filename,
 
 
4819
        .cancelled_filenames=&cancelled_filenames,
 
 
4820
        .current_time=¤t_time,
 
 
4821
        .mandos_client_exited=&mandos_client_exited,
 
 
4822
        .password_is_read=&password_is_read,
 
 
4825
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4826
                                   EPOLLIN | EPOLLRDHUP));
 
 
4828
  __attribute__((cleanup(cleanup_string)))
 
 
4829
    char *filename = NULL;
 
 
4830
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4831
                           dummy_file_name), >, 0);
 
 
4832
  g_assert_nonnull(filename);
 
 
4833
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4837
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
 
4838
                                         test_fixture *fixture,
 
 
4839
                                         __attribute__((unused))
 
 
4840
                                         gconstpointer user_data){
 
 
4841
  __attribute__((cleanup(cleanup_close)))
 
 
4842
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4843
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4844
  __attribute__((cleanup(string_set_clear)))
 
 
4845
    string_set cancelled_filenames = {};
 
 
4846
  bool mandos_client_exited = false;
 
 
4847
  bool password_is_read = false;
 
 
4848
  __attribute__((cleanup(cleanup_queue)))
 
 
4849
    task_queue *queue = create_queue();
 
 
4850
  g_assert_nonnull(queue);
 
 
4852
  char *const filename = strdup("/nonexistent");
 
 
4853
  g_assert_nonnull(filename);
 
 
4854
  task_context task = {
 
 
4855
    .func=open_and_parse_question,
 
 
4856
    .question_filename=filename,
 
 
4858
    .password=(buffer[]){{}},
 
 
4860
    .cancelled_filenames=&cancelled_filenames,
 
 
4861
    .current_time=(mono_microsecs[]){0},
 
 
4862
    .mandos_client_exited=&mandos_client_exited,
 
 
4863
    .password_is_read=&password_is_read,
 
 
4865
  task.func(task, queue);
 
 
4866
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4869
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
 
4870
                                             test_fixture *fixture,
 
 
4871
                                             __attribute__((unused))
 
 
4872
                                             gconstpointer user_data){
 
 
4873
  __attribute__((cleanup(cleanup_close)))
 
 
4874
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4875
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4876
  __attribute__((cleanup(string_set_clear)))
 
 
4877
    string_set cancelled_filenames = {};
 
 
4878
  buffer password = {};
 
 
4879
  bool mandos_client_exited = false;
 
 
4880
  bool password_is_read = false;
 
 
4881
  __attribute__((cleanup(cleanup_queue)))
 
 
4882
    task_queue *queue = create_queue();
 
 
4883
  g_assert_nonnull(queue);
 
 
4884
  const mono_microsecs current_time = 0;
 
 
4886
  char *filename = strdup("/proc/self/mem");
 
 
4887
  task_context task = {
 
 
4888
    .func=open_and_parse_question,
 
 
4889
    .question_filename=filename,
 
 
4891
    .password=&password,
 
 
4893
    .cancelled_filenames=&cancelled_filenames,
 
 
4894
    .current_time=¤t_time,
 
 
4895
    .mandos_client_exited=&mandos_client_exited,
 
 
4896
    .password_is_read=&password_is_read,
 
 
4898
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4899
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4903
test_open_and_parse_question_parse_error(__attribute__((unused))
 
 
4904
                                         test_fixture *fixture,
 
 
4905
                                         __attribute__((unused))
 
 
4906
                                         gconstpointer user_data){
 
 
4907
  __attribute__((cleanup(cleanup_close)))
 
 
4908
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4909
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4910
  __attribute__((cleanup(string_set_clear)))
 
 
4911
    string_set cancelled_filenames = {};
 
 
4912
  __attribute__((cleanup(cleanup_queue)))
 
 
4913
    task_queue *queue = create_queue();
 
 
4914
  g_assert_nonnull(queue);
 
 
4916
  __attribute__((cleanup(cleanup_string)))
 
 
4917
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4918
  g_assert_nonnull(tempfilename);
 
 
4919
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4920
  g_assert_cmpint(tempfile, >, 0);
 
 
4921
  const char bad_data[] = "this is bad syntax\n";
 
 
4922
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
 
4923
                  ==, sizeof(bad_data));
 
 
4924
  g_assert_cmpint(close(tempfile), ==, 0);
 
 
4926
  char *const filename = strdup(tempfilename);
 
 
4927
  g_assert_nonnull(filename);
 
 
4928
  task_context task = {
 
 
4929
    .func=open_and_parse_question,
 
 
4930
    .question_filename=filename,
 
 
4932
    .password=(buffer[]){{}},
 
 
4934
    .cancelled_filenames=&cancelled_filenames,
 
 
4935
    .current_time=(mono_microsecs[]){0},
 
 
4936
    .mandos_client_exited=(bool[]){false},
 
 
4937
    .password_is_read=(bool[]){false},
 
 
4939
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4941
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4943
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4947
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
 
4948
                                           test_fixture *fixture,
 
 
4949
                                           __attribute__((unused))
 
 
4950
                                           gconstpointer user_data){
 
 
4951
  __attribute__((cleanup(cleanup_close)))
 
 
4952
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4953
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4954
  __attribute__((cleanup(string_set_clear)))
 
 
4955
    string_set cancelled_filenames = {};
 
 
4956
  __attribute__((cleanup(cleanup_queue)))
 
 
4957
    task_queue *queue = create_queue();
 
 
4958
  g_assert_nonnull(queue);
 
 
4960
  __attribute__((cleanup(cleanup_string)))
 
 
4961
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4962
  g_assert_nonnull(tempfilename);
 
 
4963
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4964
  g_assert_cmpint(questionfile, >, 0);
 
 
4965
  FILE *qf = fdopen(questionfile, "w");
 
 
4966
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
 
4967
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4969
  char *const filename = strdup(tempfilename);
 
 
4970
  g_assert_nonnull(filename);
 
 
4971
  task_context task = {
 
 
4972
    .func=open_and_parse_question,
 
 
4973
    .question_filename=filename,
 
 
4975
    .password=(buffer[]){{}},
 
 
4977
    .cancelled_filenames=&cancelled_filenames,
 
 
4978
    .current_time=(mono_microsecs[]){0},
 
 
4979
    .mandos_client_exited=(bool[]){false},
 
 
4980
    .password_is_read=(bool[]){false},
 
 
4982
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4983
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4985
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4989
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
 
4990
                                            test_fixture *fixture,
 
 
4991
                                            __attribute__((unused))
 
 
4992
                                            gconstpointer user_data){
 
 
4993
  __attribute__((cleanup(cleanup_close)))
 
 
4994
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4995
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4996
  __attribute__((cleanup(string_set_clear)))
 
 
4997
    string_set cancelled_filenames = {};
 
 
4998
  __attribute__((cleanup(cleanup_queue)))
 
 
4999
    task_queue *queue = create_queue();
 
 
5000
  g_assert_nonnull(queue);
 
 
5002
  __attribute__((cleanup(cleanup_string)))
 
 
5003
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5004
  g_assert_nonnull(tempfilename);
 
 
5005
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5006
  g_assert_cmpint(questionfile, >, 0);
 
 
5007
  FILE *qf = fdopen(questionfile, "w");
 
 
5008
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
 
5009
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5011
  char *const filename = strdup(tempfilename);
 
 
5012
  g_assert_nonnull(filename);
 
 
5013
  task_context task = {
 
 
5014
    .func=open_and_parse_question,
 
 
5015
    .question_filename=filename,
 
 
5017
    .password=(buffer[]){{}},
 
 
5019
    .cancelled_filenames=&cancelled_filenames,
 
 
5020
    .current_time=(mono_microsecs[]){0},
 
 
5021
    .mandos_client_exited=(bool[]){false},
 
 
5022
    .password_is_read=(bool[]){false},
 
 
5024
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5025
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5027
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5031
void test_open_and_parse_question_nopid(__attribute__((unused))
 
 
5032
                                        test_fixture *fixture,
 
 
5033
                                        __attribute__((unused))
 
 
5034
                                        gconstpointer user_data){
 
 
5035
  __attribute__((cleanup(cleanup_close)))
 
 
5036
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5037
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5038
  __attribute__((cleanup(string_set_clear)))
 
 
5039
    string_set cancelled_filenames = {};
 
 
5040
  __attribute__((cleanup(cleanup_queue)))
 
 
5041
    task_queue *queue = create_queue();
 
 
5042
  g_assert_nonnull(queue);
 
 
5044
  __attribute__((cleanup(cleanup_string)))
 
 
5045
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5046
  g_assert_nonnull(tempfilename);
 
 
5047
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5048
  g_assert_cmpint(questionfile, >, 0);
 
 
5049
  FILE *qf = fdopen(questionfile, "w");
 
 
5050
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
 
5051
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5053
  char *const filename = strdup(tempfilename);
 
 
5054
  g_assert_nonnull(filename);
 
 
5055
  task_context task = {
 
 
5056
    .func=open_and_parse_question,
 
 
5057
    .question_filename=filename,
 
 
5059
    .password=(buffer[]){{}},
 
 
5061
    .cancelled_filenames=&cancelled_filenames,
 
 
5062
    .current_time=(mono_microsecs[]){0},
 
 
5063
    .mandos_client_exited=(bool[]){false},
 
 
5064
    .password_is_read=(bool[]){false},
 
 
5066
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5067
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5069
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5073
void test_open_and_parse_question_badpid(__attribute__((unused))
 
 
5074
                                         test_fixture *fixture,
 
 
5075
                                         __attribute__((unused))
 
 
5076
                                         gconstpointer user_data){
 
 
5077
  __attribute__((cleanup(cleanup_close)))
 
 
5078
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5079
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5080
  __attribute__((cleanup(string_set_clear)))
 
 
5081
    string_set cancelled_filenames = {};
 
 
5082
  __attribute__((cleanup(cleanup_queue)))
 
 
5083
    task_queue *queue = create_queue();
 
 
5084
  g_assert_nonnull(queue);
 
 
5086
  __attribute__((cleanup(cleanup_string)))
 
 
5087
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5088
  g_assert_nonnull(tempfilename);
 
 
5089
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5090
  g_assert_cmpint(questionfile, >, 0);
 
 
5091
  FILE *qf = fdopen(questionfile, "w");
 
 
5092
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
 
5094
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5096
  char *const filename = strdup(tempfilename);
 
 
5097
  g_assert_nonnull(filename);
 
 
5098
  task_context task = {
 
 
5099
    .func=open_and_parse_question,
 
 
5100
    .question_filename=filename,
 
 
5102
    .password=(buffer[]){{}},
 
 
5104
    .cancelled_filenames=&cancelled_filenames,
 
 
5105
    .current_time=(mono_microsecs[]){0},
 
 
5106
    .mandos_client_exited=(bool[]){false},
 
 
5107
    .password_is_read=(bool[]){false},
 
 
5109
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5110
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5112
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5116
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
 
5117
                                         test_fixture *fixture,
 
 
5118
                                         __attribute__((unused))
 
 
5119
                                         gconstpointer user_data){
 
 
5120
  __attribute__((cleanup(cleanup_close)))
 
 
5121
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5122
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5123
  __attribute__((cleanup(string_set_clear)))
 
 
5124
    string_set cancelled_filenames = {};
 
 
5125
  buffer password = {};
 
 
5126
  bool mandos_client_exited = false;
 
 
5127
  bool password_is_read = false;
 
 
5128
  __attribute__((cleanup(cleanup_queue)))
 
 
5129
    task_queue *queue = create_queue();
 
 
5130
  g_assert_nonnull(queue);
 
 
5131
  const mono_microsecs current_time = 0;
 
 
5133
  /* Find value of sysctl kernel.pid_max */
 
 
5134
  uintmax_t pid_max = 0;
 
 
5135
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
 
5136
  g_assert_nonnull(sysctl_pid_max);
 
 
5137
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
 
5139
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
 
5141
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
 
5142
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
 
5144
  __attribute__((cleanup(cleanup_string)))
 
 
5145
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5146
  g_assert_nonnull(tempfilename);
 
 
5147
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5148
  g_assert_cmpint(questionfile, >, 0);
 
 
5149
  FILE *qf = fdopen(questionfile, "w");
 
 
5150
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5151
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
 
5153
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5155
  char *const question_filename = strdup(tempfilename);
 
 
5156
  g_assert_nonnull(question_filename);
 
 
5157
  task_context task = {
 
 
5158
    .func=open_and_parse_question,
 
 
5159
    .question_filename=question_filename,
 
 
5161
    .password=&password,
 
 
5162
    .filename=question_filename,
 
 
5163
    .cancelled_filenames=&cancelled_filenames,
 
 
5164
    .current_time=¤t_time,
 
 
5165
    .mandos_client_exited=&mandos_client_exited,
 
 
5166
    .password_is_read=&password_is_read,
 
 
5168
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5169
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5171
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5175
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
 
5176
                                         test_fixture *fixture,
 
 
5177
                                         __attribute__((unused))
 
 
5178
                                         gconstpointer user_data){
 
 
5179
  __attribute__((cleanup(cleanup_close)))
 
 
5180
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5181
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5182
  __attribute__((cleanup(string_set_clear)))
 
 
5183
    string_set cancelled_filenames = {};
 
 
5184
  buffer password = {};
 
 
5185
  bool mandos_client_exited = false;
 
 
5186
  bool password_is_read = false;
 
 
5187
  __attribute__((cleanup(cleanup_queue)))
 
 
5188
    task_queue *queue = create_queue();
 
 
5189
  g_assert_nonnull(queue);
 
 
5190
  const mono_microsecs current_time = 0;
 
 
5192
  __attribute__((cleanup(cleanup_string)))
 
 
5193
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5194
  g_assert_nonnull(tempfilename);
 
 
5195
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5196
  g_assert_cmpint(questionfile, >, 0);
 
 
5197
  FILE *qf = fdopen(questionfile, "w");
 
 
5198
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5199
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
 
5200
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5202
  char *const filename = strdup(tempfilename);
 
 
5203
  g_assert_nonnull(filename);
 
 
5204
  task_context task = {
 
 
5205
    .func=open_and_parse_question,
 
 
5206
    .question_filename=filename,
 
 
5208
    .password=&password,
 
 
5210
    .cancelled_filenames=&cancelled_filenames,
 
 
5211
    .current_time=¤t_time,
 
 
5212
    .mandos_client_exited=&mandos_client_exited,
 
 
5213
    .password_is_read=&password_is_read,
 
 
5215
  task.func(task, queue);
 
 
5216
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5218
  __attribute__((cleanup(cleanup_string)))
 
 
5219
    char *socket_filename = strdup("/nonexistent");
 
 
5220
  g_assert_nonnull(socket_filename);
 
 
5221
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5222
        .func=connect_question_socket,
 
 
5223
        .question_filename=tempfilename,
 
 
5224
        .filename=socket_filename,
 
 
5226
        .password=&password,
 
 
5227
        .current_time=¤t_time,
 
 
5228
        .mandos_client_exited=&mandos_client_exited,
 
 
5229
        .password_is_read=&password_is_read,
 
 
5232
  g_assert_true(queue->next_run != 0);
 
 
5234
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5238
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
 
5239
                                          test_fixture *fixture,
 
 
5240
                                          __attribute__((unused))
 
 
5241
                                          gconstpointer user_data){
 
 
5242
  __attribute__((cleanup(cleanup_close)))
 
 
5243
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5244
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5245
  __attribute__((cleanup(string_set_clear)))
 
 
5246
    string_set cancelled_filenames = {};
 
 
5247
  buffer password = {};
 
 
5248
  bool mandos_client_exited = false;
 
 
5249
  bool password_is_read = false;
 
 
5250
  __attribute__((cleanup(cleanup_queue)))
 
 
5251
    task_queue *queue = create_queue();
 
 
5252
  g_assert_nonnull(queue);
 
 
5253
  const mono_microsecs current_time = 0;
 
 
5255
  __attribute__((cleanup(cleanup_string)))
 
 
5256
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5257
  g_assert_nonnull(tempfilename);
 
 
5258
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5259
  g_assert_cmpint(questionfile, >, 0);
 
 
5260
  FILE *qf = fdopen(questionfile, "w");
 
 
5261
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5262
                          PRIuMAX "\nNotAfter=\n",
 
 
5263
                          (uintmax_t)getpid()), >, 0);
 
 
5264
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5266
  char *const filename = strdup(tempfilename);
 
 
5267
  g_assert_nonnull(filename);
 
 
5268
  task_context task = {
 
 
5269
    .func=open_and_parse_question,
 
 
5270
    .question_filename=filename,
 
 
5272
    .password=&password,
 
 
5274
    .cancelled_filenames=&cancelled_filenames,
 
 
5275
    .current_time=¤t_time,
 
 
5276
    .mandos_client_exited=&mandos_client_exited,
 
 
5277
    .password_is_read=&password_is_read,
 
 
5279
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5280
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5282
  __attribute__((cleanup(cleanup_string)))
 
 
5283
    char *socket_filename = strdup("/nonexistent");
 
 
5284
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5285
        .func=connect_question_socket,
 
 
5286
        .question_filename=tempfilename,
 
 
5287
        .filename=socket_filename,
 
 
5289
        .password=&password,
 
 
5290
        .current_time=¤t_time,
 
 
5291
        .mandos_client_exited=&mandos_client_exited,
 
 
5292
        .password_is_read=&password_is_read,
 
 
5294
  g_assert_true(queue->next_run != 0);
 
 
5296
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5300
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
 
5302
                                                  const mono_microsecs
 
 
5304
                                                  const mono_microsecs
 
 
5306
  __attribute__((cleanup(cleanup_close)))
 
 
5307
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5308
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5309
  __attribute__((cleanup(string_set_clear)))
 
 
5310
    string_set cancelled_filenames = {};
 
 
5311
  buffer password = {};
 
 
5312
  bool mandos_client_exited = false;
 
 
5313
  bool password_is_read = false;
 
 
5314
  __attribute__((cleanup(cleanup_queue)))
 
 
5315
    task_queue *queue = create_queue();
 
 
5316
  g_assert_nonnull(queue);
 
 
5317
  queue->next_run = next_queue_run;
 
 
5319
  __attribute__((cleanup(cleanup_string)))
 
 
5320
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5321
  g_assert_nonnull(tempfilename);
 
 
5322
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5323
  g_assert_cmpint(questionfile, >, 0);
 
 
5324
  FILE *qf = fdopen(questionfile, "w");
 
 
5325
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5326
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
 
5327
                          (uintmax_t)getpid(), notafter), >, 0);
 
 
5328
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5330
  char *const filename = strdup(tempfilename);
 
 
5331
  g_assert_nonnull(filename);
 
 
5332
  task_context task = {
 
 
5333
    .func=open_and_parse_question,
 
 
5334
    .question_filename=filename,
 
 
5336
    .password=&password,
 
 
5338
    .cancelled_filenames=&cancelled_filenames,
 
 
5339
    .current_time=¤t_time,
 
 
5340
    .mandos_client_exited=&mandos_client_exited,
 
 
5341
    .password_is_read=&password_is_read,
 
 
5343
  task.func(task, queue);
 
 
5345
  if(queue->length >= 1){
 
 
5346
    __attribute__((cleanup(cleanup_string)))
 
 
5347
      char *socket_filename = strdup("/nonexistent");
 
 
5348
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5349
          .func=connect_question_socket,
 
 
5350
          .filename=socket_filename,
 
 
5352
          .password=&password,
 
 
5353
          .current_time=¤t_time,
 
 
5354
          .cancelled_filenames=&cancelled_filenames,
 
 
5355
          .mandos_client_exited=&mandos_client_exited,
 
 
5356
          .password_is_read=&password_is_read,
 
 
5358
    g_assert_true(queue->next_run != 0);
 
 
5362
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5363
  } else if(current_time >= notafter) {
 
 
5364
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5366
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5367
          .func=cancel_old_question,
 
 
5368
          .question_filename=tempfilename,
 
 
5369
          .filename=tempfilename,
 
 
5371
          .cancelled_filenames=&cancelled_filenames,
 
 
5372
          .current_time=¤t_time,
 
 
5375
  g_assert_true(queue->next_run == 1);
 
 
5377
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5381
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
 
5382
                                        test_fixture *fixture,
 
 
5383
                                        __attribute__((unused))
 
 
5384
                                        gconstpointer user_data){
 
 
5385
  /* current_time, notafter, next_queue_run */
 
 
5386
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
 
5390
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
 
5391
                                        test_fixture *fixture,
 
 
5392
                                        __attribute__((unused))
 
 
5393
                                        gconstpointer user_data){
 
 
5394
  /* current_time, notafter, next_queue_run */
 
 
5395
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
 
5399
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
 
5400
                                          test_fixture *fixture,
 
 
5401
                                          __attribute__((unused))
 
 
5402
                                          gconstpointer user_data){
 
 
5403
  /* current_time, notafter, next_queue_run */
 
 
5404
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
 
5408
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
 
5409
                                          test_fixture *fixture,
 
 
5410
                                          __attribute__((unused))
 
 
5411
                                          gconstpointer user_data){
 
 
5412
  /* current_time, notafter, next_queue_run */
 
 
5413
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
 
5417
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
 
5418
                                            test_fixture *fixture,
 
 
5419
                                            __attribute__((unused))
 
 
5420
                                            gconstpointer user_data){
 
 
5421
  /* current_time, notafter, next_queue_run */
 
 
5422
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
 
5426
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
 
5427
                                           test_fixture *fixture,
 
 
5428
                                           __attribute__((unused))
 
 
5429
                                           gconstpointer user_data){
 
 
5430
  /* current_time, notafter, next_queue_run */
 
 
5431
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
 
5434
static void assert_cancel_old_question_param(const mono_microsecs
 
 
5436
                                             const mono_microsecs
 
 
5438
                                             const mono_microsecs
 
 
5440
                                             const mono_microsecs
 
 
5442
  __attribute__((cleanup(string_set_clear)))
 
 
5443
    string_set cancelled_filenames = {};
 
 
5444
  __attribute__((cleanup(cleanup_queue)))
 
 
5445
    task_queue *queue = create_queue();
 
 
5446
  g_assert_nonnull(queue);
 
 
5447
  queue->next_run = next_queue_run;
 
 
5449
  char *const question_filename = strdup("/nonexistent");
 
 
5450
  g_assert_nonnull(question_filename);
 
 
5451
  task_context task = {
 
 
5452
    .func=cancel_old_question,
 
 
5453
    .question_filename=question_filename,
 
 
5454
    .filename=question_filename,
 
 
5456
    .cancelled_filenames=&cancelled_filenames,
 
 
5457
    .current_time=¤t_time,
 
 
5459
  task.func(task, queue);
 
 
5461
  if(current_time >= notafter){
 
 
5462
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5463
    g_assert_true(string_set_contains(cancelled_filenames,
 
 
5466
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5467
          .func=cancel_old_question,
 
 
5468
          .question_filename=question_filename,
 
 
5469
          .filename=question_filename,
 
 
5471
          .cancelled_filenames=&cancelled_filenames,
 
 
5472
          .current_time=¤t_time,
 
 
5475
    g_assert_false(string_set_contains(cancelled_filenames,
 
 
5476
                                       question_filename));
 
 
5478
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
 
5479
                   (unsigned int)next_set_to);
 
 
5482
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
 
5483
                                           test_fixture *fixture,
 
 
5484
                                           __attribute__((unused))
 
 
5485
                                           gconstpointer user_data){
 
 
5486
  /* next_queue_run unset,
 
 
5487
     cancellation should happen because time has come,
 
 
5488
     next_queue_run should be unchanged */
 
 
5489
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5490
  assert_cancel_old_question_param(0, 1, 2, 0);
 
 
5493
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
 
5494
                                           test_fixture *fixture,
 
 
5495
                                           __attribute__((unused))
 
 
5496
                                           gconstpointer user_data){
 
 
5497
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
 
5498
     and current_time is not yet notafter or greater,
 
 
5499
     update value of next_queue_run to value of notafter */
 
 
5500
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5501
  assert_cancel_old_question_param(0, 2, 1, 2);
 
 
5504
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
 
5505
                                           test_fixture *fixture,
 
 
5506
                                           __attribute__((unused))
 
 
5507
                                           gconstpointer user_data){
 
 
5508
  /* next_queue_run 1,
 
 
5509
     cancellation should happen because time has come,
 
 
5510
     next_queue_run should be unchanged */
 
 
5511
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5512
  assert_cancel_old_question_param(1, 2, 3, 1);
 
 
5515
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
 
5516
                                           test_fixture *fixture,
 
 
5517
                                           __attribute__((unused))
 
 
5518
                                           gconstpointer user_data){
 
 
5519
  /* If next_queue_run is set,
 
 
5520
     and current_time is not yet notafter or greater,
 
 
5521
     and notafter is larger than next_queue_run
 
 
5522
     next_queue_run should be unchanged */
 
 
5523
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5524
  assert_cancel_old_question_param(1, 3, 2, 1);
 
 
5527
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
 
5528
                                           test_fixture *fixture,
 
 
5529
                                           __attribute__((unused))
 
 
5530
                                           gconstpointer user_data){
 
 
5531
  /* next_queue_run 2,
 
 
5532
     cancellation should happen because time has come,
 
 
5533
     next_queue_run should be unchanged */
 
 
5534
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5535
  assert_cancel_old_question_param(2, 1, 3, 2);
 
 
5538
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
 
5539
                                           test_fixture *fixture,
 
 
5540
                                           __attribute__((unused))
 
 
5541
                                           gconstpointer user_data){
 
 
5542
  /* If next_queue_run is set,
 
 
5543
     and current_time is not yet notafter or greater,
 
 
5544
     and notafter is larger than next_queue_run
 
 
5545
     next_queue_run should be unchanged */
 
 
5546
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5547
  assert_cancel_old_question_param(2, 3, 1, 2);
 
 
5550
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
 
5551
                                           test_fixture *fixture,
 
 
5552
                                           __attribute__((unused))
 
 
5553
                                           gconstpointer user_data){
 
 
5554
  /* next_queue_run 3,
 
 
5555
     cancellation should happen because time has come,
 
 
5556
     next_queue_run should be unchanged */
 
 
5557
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5558
  assert_cancel_old_question_param(3, 1, 2, 3);
 
 
5561
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
 
5562
                                           test_fixture *fixture,
 
 
5563
                                           __attribute__((unused))
 
 
5564
                                           gconstpointer user_data){
 
 
5565
  /* If next_queue_run is set,
 
 
5566
     and current_time is not yet notafter or greater,
 
 
5567
     and notafter is smaller than next_queue_run
 
 
5568
     update value of next_queue_run to value of notafter */
 
 
5569
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5570
  assert_cancel_old_question_param(3, 2, 1, 2);
 
 
5574
test_connect_question_socket_name_too_long(__attribute__((unused))
 
 
5575
                                           test_fixture *fixture,
 
 
5576
                                           __attribute__((unused))
 
 
5577
                                           gconstpointer user_data){
 
 
5578
  __attribute__((cleanup(cleanup_close)))
 
 
5579
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5580
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5581
  const char question_filename[] = "/nonexistent/question";
 
 
5582
  __attribute__((cleanup(string_set_clear)))
 
 
5583
    string_set cancelled_filenames = {};
 
 
5584
  __attribute__((cleanup(cleanup_queue)))
 
 
5585
    task_queue *queue = create_queue();
 
 
5586
  g_assert_nonnull(queue);
 
 
5587
  __attribute__((cleanup(cleanup_string)))
 
 
5588
    char *tempdir = make_temporary_directory();
 
 
5589
  g_assert_nonnull(tempdir);
 
 
5590
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
 
5591
  char socket_name[sizeof(unix_socket.sun_path)];
 
 
5592
  memset(socket_name, 'x', sizeof(socket_name));
 
 
5593
  socket_name[sizeof(socket_name)-1] = '\0';
 
 
5594
  char *filename = NULL;
 
 
5595
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5597
  g_assert_nonnull(filename);
 
 
5599
  task_context task = {
 
 
5600
    .func=connect_question_socket,
 
 
5601
    .question_filename=strdup(question_filename),
 
 
5603
    .password=(buffer[]){{}},
 
 
5605
    .cancelled_filenames=&cancelled_filenames,
 
 
5606
    .mandos_client_exited=(bool[]){false},
 
 
5607
    .password_is_read=(bool[]){false},
 
 
5608
    .current_time=(mono_microsecs[]){0},
 
 
5610
  g_assert_nonnull(task.question_filename);
 
 
5611
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5613
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5614
                                    question_filename));
 
 
5615
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5616
  g_assert_true(queue->next_run == 0);
 
 
5618
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5622
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
 
5623
                                               test_fixture *fixture,
 
 
5624
                                               __attribute__((unused))
 
 
5627
  __attribute__((cleanup(cleanup_close)))
 
 
5628
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5629
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5630
  const char question_filename[] = "/nonexistent/question";
 
 
5631
  __attribute__((cleanup(string_set_clear)))
 
 
5632
    string_set cancelled_filenames = {};
 
 
5633
  const mono_microsecs current_time = 3;
 
 
5634
  __attribute__((cleanup(cleanup_queue)))
 
 
5635
    task_queue *queue = create_queue();
 
 
5636
  g_assert_nonnull(queue);
 
 
5637
  __attribute__((cleanup(cleanup_string)))
 
 
5638
    char *tempdir = make_temporary_directory();
 
 
5639
  g_assert_nonnull(tempdir);
 
 
5640
  char socket_name[] = "nonexistent";
 
 
5641
  char *filename = NULL;
 
 
5642
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5644
  g_assert_nonnull(filename);
 
 
5646
  task_context task = {
 
 
5647
    .func=connect_question_socket,
 
 
5648
    .question_filename=strdup(question_filename),
 
 
5650
    .password=(buffer[]){{}},
 
 
5652
    .cancelled_filenames=&cancelled_filenames,
 
 
5653
    .mandos_client_exited=(bool[]){false},
 
 
5654
    .password_is_read=(bool[]){false},
 
 
5655
    .current_time=¤t_time,
 
 
5657
  g_assert_nonnull(task.question_filename);
 
 
5658
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5660
  g_assert_nonnull(find_matching_task(queue, task));
 
 
5662
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
5663
                                     question_filename));
 
 
5664
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5665
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5667
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5671
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
 
5672
                                            test_fixture *fixture,
 
 
5673
                                            __attribute__((unused))
 
 
5674
                                            gconstpointer user_data){
 
 
5675
  __attribute__((cleanup(cleanup_close)))
 
 
5676
    const int epoll_fd = open("/dev/null",
 
 
5677
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
5678
  __attribute__((cleanup(cleanup_string)))
 
 
5679
    char *const question_filename = strdup("/nonexistent/question");
 
 
5680
  g_assert_nonnull(question_filename);
 
 
5681
  __attribute__((cleanup(string_set_clear)))
 
 
5682
    string_set cancelled_filenames = {};
 
 
5683
  const mono_microsecs current_time = 5;
 
 
5684
  __attribute__((cleanup(cleanup_queue)))
 
 
5685
    task_queue *queue = create_queue();
 
 
5686
  g_assert_nonnull(queue);
 
 
5687
  __attribute__((cleanup(cleanup_string)))
 
 
5688
    char *tempdir = make_temporary_directory();
 
 
5689
  g_assert_nonnull(tempdir);
 
 
5690
  __attribute__((cleanup(cleanup_close)))
 
 
5691
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5692
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5693
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5694
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5695
  const char socket_name[] = "socket_name";
 
 
5696
  __attribute__((cleanup(cleanup_string)))
 
 
5697
    char *filename = NULL;
 
 
5698
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5700
  g_assert_nonnull(filename);
 
 
5701
  g_assert_cmpint((int)strlen(filename), <,
 
 
5702
                  (int)sizeof(sock_name.sun_path));
 
 
5703
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5704
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5705
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5706
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5707
  task_context task = {
 
 
5708
    .func=connect_question_socket,
 
 
5709
    .question_filename=strdup(question_filename),
 
 
5711
    .password=(buffer[]){{}},
 
 
5712
    .filename=strdup(filename),
 
 
5713
    .cancelled_filenames=&cancelled_filenames,
 
 
5714
    .mandos_client_exited=(bool[]){false},
 
 
5715
    .password_is_read=(bool[]){false},
 
 
5716
    .current_time=¤t_time,
 
 
5718
  g_assert_nonnull(task.question_filename);
 
 
5719
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5721
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5722
  const task_context *const added_task
 
 
5723
    = find_matching_task(queue, task);
 
 
5724
  g_assert_nonnull(added_task);
 
 
5725
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5727
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5728
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5732
void test_connect_question_socket_usable(__attribute__((unused))
 
 
5733
                                         test_fixture *fixture,
 
 
5734
                                         __attribute__((unused))
 
 
5735
                                         gconstpointer user_data){
 
 
5736
  __attribute__((cleanup(cleanup_close)))
 
 
5737
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5738
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5739
  __attribute__((cleanup(cleanup_string)))
 
 
5740
    char *const question_filename = strdup("/nonexistent/question");
 
 
5741
  g_assert_nonnull(question_filename);
 
 
5742
  __attribute__((cleanup(string_set_clear)))
 
 
5743
    string_set cancelled_filenames = {};
 
 
5744
  buffer password = {};
 
 
5745
  bool mandos_client_exited = false;
 
 
5746
  bool password_is_read = false;
 
 
5747
  const mono_microsecs current_time = 0;
 
 
5748
  __attribute__((cleanup(cleanup_queue)))
 
 
5749
    task_queue *queue = create_queue();
 
 
5750
  g_assert_nonnull(queue);
 
 
5751
  __attribute__((cleanup(cleanup_string)))
 
 
5752
    char *tempdir = make_temporary_directory();
 
 
5753
  g_assert_nonnull(tempdir);
 
 
5754
  __attribute__((cleanup(cleanup_close)))
 
 
5755
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5756
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5757
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5758
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5759
  const char socket_name[] = "socket_name";
 
 
5760
  __attribute__((cleanup(cleanup_string)))
 
 
5761
    char *filename = NULL;
 
 
5762
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5764
  g_assert_nonnull(filename);
 
 
5765
  g_assert_cmpint((int)strlen(filename), <,
 
 
5766
                  (int)sizeof(sock_name.sun_path));
 
 
5767
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5768
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5769
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5770
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5771
  task_context task = {
 
 
5772
    .func=connect_question_socket,
 
 
5773
    .question_filename=strdup(question_filename),
 
 
5775
    .password=&password,
 
 
5776
    .filename=strdup(filename),
 
 
5777
    .cancelled_filenames=&cancelled_filenames,
 
 
5778
    .mandos_client_exited=&mandos_client_exited,
 
 
5779
    .password_is_read=&password_is_read,
 
 
5780
    .current_time=¤t_time,
 
 
5782
  g_assert_nonnull(task.question_filename);
 
 
5783
  task.func(task, queue);
 
 
5785
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5786
  const task_context *const added_task
 
 
5787
    = find_matching_task(queue, (task_context){
 
 
5788
        .func=send_password_to_socket,
 
 
5789
        .question_filename=question_filename,
 
 
5792
        .password=&password,
 
 
5793
        .cancelled_filenames=&cancelled_filenames,
 
 
5794
        .mandos_client_exited=&mandos_client_exited,
 
 
5795
        .password_is_read=&password_is_read,
 
 
5796
        .current_time=¤t_time,
 
 
5798
  g_assert_nonnull(added_task);
 
 
5799
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5801
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5804
  const int fd = added_task->fd;
 
 
5805
  g_assert_cmpint(fd, >, 0);
 
 
5806
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
 
5809
  char write_data[PIPE_BUF];
 
 
5811
    /* Construct test password buffer */
 
 
5812
    /* Start with + since that is what the real protocol uses */
 
 
5813
    write_data[0] = '+';
 
 
5814
    /* Set a special character at string end just to mark the end */
 
 
5815
    write_data[sizeof(write_data)-2] = 'y';
 
 
5816
    /* Set NUL at buffer end, as suggested by the protocol */
 
 
5817
    write_data[sizeof(write_data)-1] = '\0';
 
 
5818
    /* Fill rest of password with 'x' */
 
 
5819
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
 
5820
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
 
5821
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
 
5824
  /* read from sock_fd */
 
 
5825
  char read_data[sizeof(write_data)];
 
 
5826
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
 
5827
                  ==, sizeof(read_data));
 
 
5829
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
 
5832
  /* writing to sock_fd should fail */
 
 
5833
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
 
5834
                       MSG_NOSIGNAL), <, 0);
 
 
5836
  /* reading from fd should fail */
 
 
5837
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
 
5838
                            MSG_NOSIGNAL), <, 0);
 
 
5840
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5841
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5845
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
 
5846
                                               test_fixture *fixture,
 
 
5847
                                               __attribute__((unused))
 
 
5850
  __attribute__((cleanup(cleanup_close)))
 
 
5851
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5852
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5853
  __attribute__((cleanup(cleanup_string)))
 
 
5854
    char *const question_filename = strdup("/nonexistent/question");
 
 
5855
  g_assert_nonnull(question_filename);
 
 
5856
  __attribute__((cleanup(cleanup_string)))
 
 
5857
    char *const filename = strdup("/nonexistent/socket");
 
 
5858
  g_assert_nonnull(filename);
 
 
5859
  __attribute__((cleanup(string_set_clear)))
 
 
5860
    string_set cancelled_filenames = {};
 
 
5861
  buffer password = {};
 
 
5862
  bool password_is_read = true;
 
 
5863
  __attribute__((cleanup(cleanup_queue)))
 
 
5864
    task_queue *queue = create_queue();
 
 
5865
  g_assert_nonnull(queue);
 
 
5867
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5868
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5870
  __attribute__((cleanup(cleanup_close)))
 
 
5871
    const int read_socket = socketfds[0];
 
 
5872
  const int write_socket = socketfds[1];
 
 
5873
  task_context task = {
 
 
5874
    .func=send_password_to_socket,
 
 
5875
    .question_filename=strdup(question_filename),
 
 
5876
    .filename=strdup(filename),
 
 
5879
    .password=&password,
 
 
5880
    .cancelled_filenames=&cancelled_filenames,
 
 
5881
    .mandos_client_exited=(bool[]){false},
 
 
5882
    .password_is_read=&password_is_read,
 
 
5883
    .current_time=(mono_microsecs[]){0},
 
 
5885
  g_assert_nonnull(task.question_filename);
 
 
5887
  task.func(task, queue);
 
 
5889
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5891
  const task_context *const added_task
 
 
5892
    = find_matching_task(queue, task);
 
 
5893
  g_assert_nonnull(added_task);
 
 
5894
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5895
  g_assert_true(password_is_read);
 
 
5897
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5898
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5903
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
 
5904
                                               test_fixture *fixture,
 
 
5905
                                               __attribute__((unused))
 
 
5908
  __attribute__((cleanup(cleanup_close)))
 
 
5909
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5910
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5911
  __attribute__((cleanup(cleanup_string)))
 
 
5912
    char *const question_filename = strdup("/nonexistent/question");
 
 
5913
  g_assert_nonnull(question_filename);
 
 
5914
  __attribute__((cleanup(cleanup_string)))
 
 
5915
    char *const filename = strdup("/nonexistent/socket");
 
 
5916
  __attribute__((cleanup(string_set_clear)))
 
 
5917
    string_set cancelled_filenames = {};
 
 
5918
  buffer password = {};
 
 
5919
  __attribute__((cleanup(cleanup_queue)))
 
 
5920
    task_queue *queue = create_queue();
 
 
5921
  g_assert_nonnull(queue);
 
 
5923
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5924
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5926
  __attribute__((cleanup(cleanup_close)))
 
 
5927
    const int read_socket = socketfds[0];
 
 
5928
  const int write_socket = socketfds[1];
 
 
5929
  task_context task = {
 
 
5930
    .func=send_password_to_socket,
 
 
5931
    .question_filename=strdup(question_filename),
 
 
5932
    .filename=strdup(filename),
 
 
5935
    .password=&password,
 
 
5936
    .cancelled_filenames=&cancelled_filenames,
 
 
5937
    .mandos_client_exited=(bool[]){false},
 
 
5938
    .password_is_read=(bool[]){false},
 
 
5939
    .current_time=(mono_microsecs[]){0},
 
 
5941
  g_assert_nonnull(task.question_filename);
 
 
5943
  task.func(task, queue);
 
 
5945
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5947
  const task_context *const added_task = find_matching_task(queue,
 
 
5949
  g_assert_nonnull(added_task);
 
 
5950
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5951
  g_assert_true(queue->next_run == 0);
 
 
5953
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5954
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5959
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
 
5960
                                           test_fixture *fixture,
 
 
5961
                                           __attribute__((unused))
 
 
5962
                                           gconstpointer user_data){
 
 
5963
  __attribute__((cleanup(cleanup_close)))
 
 
5964
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5965
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5966
  const char question_filename[] = "/nonexistent/question";
 
 
5967
  char *const filename = strdup("/nonexistent/socket");
 
 
5968
  __attribute__((cleanup(string_set_clear)))
 
 
5969
    string_set cancelled_filenames = {};
 
 
5972
  /* Find a message size which triggers EMSGSIZE */
 
 
5973
  __attribute__((cleanup(cleanup_string)))
 
 
5974
    char *message_buffer = NULL;
 
 
5975
  size_t message_size = PIPE_BUF + 1;
 
 
5976
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
 
5977
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
 
5978
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
 
5981
    free(message_buffer);
 
 
5982
    message_buffer = malloc(message_size);
 
 
5983
    if(message_buffer == NULL){
 
 
5984
      g_test_skip("Skipping EMSGSIZE test");
 
 
5985
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
 
5986
                     (uintmax_t)message_size);
 
 
5989
    /* Fill buffer with 'x' */
 
 
5990
    memset(message_buffer, 'x', message_size);
 
 
5991
    /* Create a new socketpair for each message size to avoid having
 
 
5992
       to empty the pipe by reading the message to a separate buffer
 
 
5994
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5995
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5997
    ssret = send(socketfds[1], message_buffer, message_size,
 
 
5999
    error_t saved_errno = errno;
 
 
6000
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
 
6001
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
 
6004
      if(saved_errno != EMSGSIZE) {
 
 
6005
        g_test_skip("Skipping EMSGSIZE test");
 
 
6006
        g_test_message("Error on send(): %s", strerror(saved_errno));
 
 
6009
    } else if(ssret != (ssize_t)message_size){
 
 
6010
      g_test_skip("Skipping EMSGSIZE test");
 
 
6011
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
 
6012
                     " bytes", (uintmax_t)ssret,
 
 
6013
                     (intmax_t)message_size);
 
 
6017
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
 
6018
                 (intmax_t)message_size);
 
 
6021
    .data=message_buffer,
 
 
6022
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
 
6023
    .allocated=message_size,
 
 
6025
  if(mlock(password.data, password.allocated) != 0){
 
 
6026
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6029
  __attribute__((cleanup(cleanup_queue)))
 
 
6030
    task_queue *queue = create_queue();
 
 
6031
  g_assert_nonnull(queue);
 
 
6032
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6033
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6035
  __attribute__((cleanup(cleanup_close)))
 
 
6036
    const int read_socket = socketfds[0];
 
 
6037
  __attribute__((cleanup(cleanup_close)))
 
 
6038
    const int write_socket = socketfds[1];
 
 
6039
  task_context task = {
 
 
6040
    .func=send_password_to_socket,
 
 
6041
    .question_filename=strdup(question_filename),
 
 
6045
    .password=&password,
 
 
6046
    .cancelled_filenames=&cancelled_filenames,
 
 
6047
    .mandos_client_exited=(bool[]){true},
 
 
6048
    .password_is_read=(bool[]){true},
 
 
6049
    .current_time=(mono_microsecs[]){0},
 
 
6051
  g_assert_nonnull(task.question_filename);
 
 
6053
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6055
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6056
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
6057
                                    question_filename));
 
 
6060
static void test_send_password_to_socket_retry(__attribute__((unused))
 
 
6061
                                               test_fixture *fixture,
 
 
6062
                                               __attribute__((unused))
 
 
6065
  __attribute__((cleanup(cleanup_close)))
 
 
6066
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6067
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6068
  __attribute__((cleanup(cleanup_string)))
 
 
6069
    char *const question_filename = strdup("/nonexistent/question");
 
 
6070
  g_assert_nonnull(question_filename);
 
 
6071
  __attribute__((cleanup(cleanup_string)))
 
 
6072
    char *const filename = strdup("/nonexistent/socket");
 
 
6073
  g_assert_nonnull(filename);
 
 
6074
  __attribute__((cleanup(string_set_clear)))
 
 
6075
    string_set cancelled_filenames = {};
 
 
6076
  __attribute__((cleanup(cleanup_buffer)))
 
 
6077
    buffer password = {};
 
 
6079
  __attribute__((cleanup(cleanup_queue)))
 
 
6080
    task_queue *queue = create_queue();
 
 
6081
  g_assert_nonnull(queue);
 
 
6083
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6084
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6086
  __attribute__((cleanup(cleanup_close)))
 
 
6087
    const int read_socket = socketfds[0];
 
 
6088
  const int write_socket = socketfds[1];
 
 
6089
  /* Close the server side socket to force ECONNRESET on client */
 
 
6090
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6091
  task_context task = {
 
 
6092
    .func=send_password_to_socket,
 
 
6093
    .question_filename=strdup(question_filename),
 
 
6094
    .filename=strdup(filename),
 
 
6097
    .password=&password,
 
 
6098
    .cancelled_filenames=&cancelled_filenames,
 
 
6099
    .mandos_client_exited=(bool[]){true},
 
 
6100
    .password_is_read=(bool[]){true},
 
 
6101
    .current_time=(mono_microsecs[]){0},
 
 
6103
  g_assert_nonnull(task.question_filename);
 
 
6105
  task.func(task, queue);
 
 
6107
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6109
  const task_context *const added_task = find_matching_task(queue,
 
 
6111
  g_assert_nonnull(added_task);
 
 
6112
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6114
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
6119
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
 
6120
                                            test_fixture *fixture,
 
 
6121
                                            __attribute__((unused))
 
 
6122
                                            gconstpointer user_data){
 
 
6123
  __attribute__((cleanup(cleanup_close)))
 
 
6124
    const int epoll_fd = open("/dev/null",
 
 
6125
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6126
  __attribute__((cleanup(cleanup_string)))
 
 
6127
    char *const question_filename = strdup("/nonexistent/question");
 
 
6128
  g_assert_nonnull(question_filename);
 
 
6129
  __attribute__((cleanup(cleanup_string)))
 
 
6130
    char *const filename = strdup("/nonexistent/socket");
 
 
6131
  g_assert_nonnull(filename);
 
 
6132
  __attribute__((cleanup(string_set_clear)))
 
 
6133
    string_set cancelled_filenames = {};
 
 
6134
  __attribute__((cleanup(cleanup_buffer)))
 
 
6135
    buffer password = {};
 
 
6137
  const mono_microsecs current_time = 11;
 
 
6138
  __attribute__((cleanup(cleanup_queue)))
 
 
6139
    task_queue *queue = create_queue();
 
 
6140
  g_assert_nonnull(queue);
 
 
6142
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6143
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6145
  __attribute__((cleanup(cleanup_close)))
 
 
6146
    const int read_socket = socketfds[0];
 
 
6147
  const int write_socket = socketfds[1];
 
 
6148
  /* Close the server side socket to force ECONNRESET on client */
 
 
6149
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6150
  task_context task = {
 
 
6151
    .func=send_password_to_socket,
 
 
6152
    .question_filename=strdup(question_filename),
 
 
6153
    .filename=strdup(filename),
 
 
6156
    .password=&password,
 
 
6157
    .cancelled_filenames=&cancelled_filenames,
 
 
6158
    .mandos_client_exited=(bool[]){true},
 
 
6159
    .password_is_read=(bool[]){true},
 
 
6160
    .current_time=¤t_time,
 
 
6162
  g_assert_nonnull(task.question_filename);
 
 
6164
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6166
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6168
  const task_context *const added_task = find_matching_task(queue,
 
 
6170
  g_assert_nonnull(added_task);
 
 
6171
  g_assert_true(queue->next_run == current_time + 1000000);
 
 
6172
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6175
static void assert_send_password_to_socket_password(buffer password){
 
 
6176
  __attribute__((cleanup(cleanup_close)))
 
 
6177
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6178
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6179
  char *const question_filename = strdup("/nonexistent/question");
 
 
6180
  g_assert_nonnull(question_filename);
 
 
6181
  char *const filename = strdup("/nonexistent/socket");
 
 
6182
  g_assert_nonnull(filename);
 
 
6183
  __attribute__((cleanup(string_set_clear)))
 
 
6184
    string_set cancelled_filenames = {};
 
 
6186
  __attribute__((cleanup(cleanup_queue)))
 
 
6187
    task_queue *queue = create_queue();
 
 
6188
  g_assert_nonnull(queue);
 
 
6190
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6191
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6193
  __attribute__((cleanup(cleanup_close)))
 
 
6194
    const int read_socket = socketfds[0];
 
 
6195
  const int write_socket = socketfds[1];
 
 
6196
  task_context task = {
 
 
6197
    .func=send_password_to_socket,
 
 
6198
    .question_filename=question_filename,
 
 
6202
    .password=&password,
 
 
6203
    .cancelled_filenames=&cancelled_filenames,
 
 
6204
    .mandos_client_exited=(bool[]){true},
 
 
6205
    .password_is_read=(bool[]){true},
 
 
6206
    .current_time=(mono_microsecs[]){0},
 
 
6209
  char *expected_written_data = malloc(password.length + 2);
 
 
6210
  g_assert_nonnull(expected_written_data);
 
 
6211
  expected_written_data[0] = '+';
 
 
6212
  expected_written_data[password.length + 1] = '\0';
 
 
6213
  if(password.length > 0){
 
 
6214
    g_assert_nonnull(password.data);
 
 
6215
    memcpy(expected_written_data + 1, password.data, password.length);
 
 
6218
  task.func(task, queue);
 
 
6221
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
 
6222
                  (int)(password.length + 2));
 
 
6223
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6225
  g_assert_true(memcmp(expected_written_data, buf,
 
 
6226
                       password.length + 2) == 0);
 
 
6228
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
 
6230
  free(expected_written_data);
 
 
6234
test_send_password_to_socket_null_password(__attribute__((unused))
 
 
6235
                                           test_fixture *fixture,
 
 
6236
                                           __attribute__((unused))
 
 
6237
                                           gconstpointer user_data){
 
 
6238
  __attribute__((cleanup(cleanup_buffer)))
 
 
6239
    buffer password = {};
 
 
6240
  assert_send_password_to_socket_password(password);
 
 
6244
test_send_password_to_socket_empty_password(__attribute__((unused))
 
 
6245
                                            test_fixture *fixture,
 
 
6246
                                            __attribute__((unused))
 
 
6247
                                            gconstpointer user_data){
 
 
6248
  __attribute__((cleanup(cleanup_buffer)))
 
 
6250
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
 
6252
    .allocated=0,               /* deliberate lie */
 
 
6254
  g_assert_nonnull(password.data);
 
 
6255
  assert_send_password_to_socket_password(password);
 
 
6259
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
 
6260
                                            test_fixture *fixture,
 
 
6261
                                            __attribute__((unused))
 
 
6262
                                            gconstpointer user_data){
 
 
6263
  __attribute__((cleanup(cleanup_buffer)))
 
 
6269
  if(mlock(password.data, password.allocated) != 0){
 
 
6270
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6272
  assert_send_password_to_socket_password(password);
 
 
6276
test_send_password_to_socket_text_password(__attribute__((unused))
 
 
6277
                                           test_fixture *fixture,
 
 
6278
                                           __attribute__((unused))
 
 
6279
                                           gconstpointer user_data){
 
 
6280
  const char dummy_test_password[] = "dummy test password";
 
 
6281
  __attribute__((cleanup(cleanup_buffer)))
 
 
6283
    .data = strdup(dummy_test_password),
 
 
6284
    .length = strlen(dummy_test_password),
 
 
6285
    .allocated = sizeof(dummy_test_password),
 
 
6287
  if(mlock(password.data, password.allocated) != 0){
 
 
6288
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6290
  assert_send_password_to_socket_password(password);
 
 
6294
test_send_password_to_socket_binary_password(__attribute__((unused))
 
 
6295
                                             test_fixture *fixture,
 
 
6296
                                             __attribute__((unused))
 
 
6297
                                             gconstpointer user_data){
 
 
6298
  __attribute__((cleanup(cleanup_buffer)))
 
 
6304
  g_assert_nonnull(password.data);
 
 
6305
  if(mlock(password.data, password.allocated) != 0){
 
 
6306
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6308
  char c = 1;                   /* Start at 1, avoiding NUL */
 
 
6309
  for(int i=0; i < 255; i++){
 
 
6310
    password.data[i] = c++;
 
 
6312
  assert_send_password_to_socket_password(password);
 
 
6316
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
 
6317
                                              test_fixture *fixture,
 
 
6318
                                              __attribute__((unused))
 
 
6321
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
 
6322
  __attribute__((cleanup(cleanup_buffer)))
 
 
6324
    .data=malloc(sizeof(test_password)),
 
 
6325
    .length=sizeof(test_password),
 
 
6326
    .allocated=sizeof(test_password),
 
 
6328
  g_assert_nonnull(password.data);
 
 
6329
  if(mlock(password.data, password.allocated) !=0){
 
 
6330
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6332
  memcpy(password.data, test_password, password.allocated);
 
 
6333
  assert_send_password_to_socket_password(password);
 
 
6336
static bool assert_add_existing_questions_to_devnull(task_queue
 
 
6349
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
 
6350
                                               test_fixture *fixture,
 
 
6351
                                               __attribute__((unused))
 
 
6354
  __attribute__((cleanup(cleanup_queue)))
 
 
6355
    task_queue *queue = create_queue();
 
 
6356
  g_assert_nonnull(queue);
 
 
6357
  __attribute__((cleanup(cleanup_close)))
 
 
6358
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6359
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6360
  __attribute__((cleanup(string_set_clear)))
 
 
6361
    string_set cancelled_filenames = {};
 
 
6363
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6366
                  (buffer[]){{}}, /* password */
 
 
6367
                  &cancelled_filenames,
 
 
6368
                  (mono_microsecs[]){0}, /* current_time */
 
 
6369
                  (bool[]){false},       /* mandos_client_exited */
 
 
6370
                  (bool[]){false},       /* password_is_read */
 
 
6371
                  "/nonexistent"));      /* dirname */
 
 
6373
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6377
bool assert_add_existing_questions_to_devnull(task_queue
 
 
6384
                                              *cancelled_filenames,
 
 
6385
                                              const mono_microsecs
 
 
6386
                                              *const current_time,
 
 
6388
                                              mandos_client_exited,
 
 
6393
  __attribute__((cleanup(cleanup_close)))
 
 
6394
    const int devnull_fd = open("/dev/null",
 
 
6395
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6396
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
6397
  __attribute__((cleanup(cleanup_close)))
 
 
6398
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
6399
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
6400
  dup2(devnull_fd, STDERR_FILENO);
 
 
6401
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
 
6402
                                          cancelled_filenames,
 
 
6404
                                          mandos_client_exited,
 
 
6405
                                          password_is_read, dirname);
 
 
6406
  dup2(real_stderr_fd, STDERR_FILENO);
 
 
6411
void test_add_existing_questions_no_questions(__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(string_set_clear)))
 
 
6423
    string_set cancelled_filenames = {};
 
 
6424
  __attribute__((cleanup(cleanup_string)))
 
 
6425
    char *tempdir = make_temporary_directory();
 
 
6426
  g_assert_nonnull(tempdir);
 
 
6428
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6431
                  (buffer[]){{}}, /* password */
 
 
6432
                  &cancelled_filenames,
 
 
6433
                  (mono_microsecs[]){0}, /* current_time */
 
 
6434
                  (bool[]){false},       /* mandos_client_exited */
 
 
6435
                  (bool[]){false},       /* password_is_read */
 
 
6438
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6440
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6443
static char *make_question_file_in_directory(const char *const);
 
 
6446
void test_add_existing_questions_one_question(__attribute__((unused))
 
 
6447
                                              test_fixture *fixture,
 
 
6448
                                              __attribute__((unused))
 
 
6451
  __attribute__((cleanup(cleanup_queue)))
 
 
6452
    task_queue *queue = create_queue();
 
 
6453
  g_assert_nonnull(queue);
 
 
6454
  __attribute__((cleanup(cleanup_close)))
 
 
6455
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6456
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6457
  __attribute__((cleanup(cleanup_buffer)))
 
 
6458
    buffer password = {};
 
 
6459
  __attribute__((cleanup(string_set_clear)))
 
 
6460
    string_set cancelled_filenames = {};
 
 
6461
  const mono_microsecs current_time = 0;
 
 
6462
  bool mandos_client_exited = false;
 
 
6463
  bool password_is_read = false;
 
 
6464
  __attribute__((cleanup(cleanup_string)))
 
 
6465
    char *tempdir = make_temporary_directory();
 
 
6466
  g_assert_nonnull(tempdir);
 
 
6467
  __attribute__((cleanup(cleanup_string)))
 
 
6468
    char *question_filename
 
 
6469
    = make_question_file_in_directory(tempdir);
 
 
6470
  g_assert_nonnull(question_filename);
 
 
6472
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6476
                 &cancelled_filenames,
 
 
6478
                 &mandos_client_exited,
 
 
6482
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6484
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6485
        .func=open_and_parse_question,
 
 
6487
        .filename=question_filename,
 
 
6488
        .question_filename=question_filename,
 
 
6489
        .password=&password,
 
 
6490
        .cancelled_filenames=&cancelled_filenames,
 
 
6491
        .current_time=¤t_time,
 
 
6492
        .mandos_client_exited=&mandos_client_exited,
 
 
6493
        .password_is_read=&password_is_read,
 
 
6496
  g_assert_true(queue->next_run == 1);
 
 
6498
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6499
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6502
static char *make_question_file_in_directory(const char
 
 
6504
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
 
6508
void test_add_existing_questions_two_questions(__attribute__((unused))
 
 
6509
                                               test_fixture *fixture,
 
 
6510
                                               __attribute__((unused))
 
 
6513
  __attribute__((cleanup(cleanup_queue)))
 
 
6514
    task_queue *queue = create_queue();
 
 
6515
  g_assert_nonnull(queue);
 
 
6516
  __attribute__((cleanup(cleanup_close)))
 
 
6517
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6518
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6519
  __attribute__((cleanup(cleanup_buffer)))
 
 
6520
    buffer password = {};
 
 
6521
  __attribute__((cleanup(string_set_clear)))
 
 
6522
    string_set cancelled_filenames = {};
 
 
6523
  const mono_microsecs current_time = 0;
 
 
6524
  bool mandos_client_exited = false;
 
 
6525
  bool password_is_read = false;
 
 
6526
  __attribute__((cleanup(cleanup_string)))
 
 
6527
    char *tempdir = make_temporary_directory();
 
 
6528
  g_assert_nonnull(tempdir);
 
 
6529
  __attribute__((cleanup(cleanup_string)))
 
 
6530
    char *question_filename1
 
 
6531
    = make_question_file_in_directory(tempdir);
 
 
6532
  g_assert_nonnull(question_filename1);
 
 
6533
  __attribute__((cleanup(cleanup_string)))
 
 
6534
    char *question_filename2
 
 
6535
    = make_question_file_in_directory(tempdir);
 
 
6536
  g_assert_nonnull(question_filename2);
 
 
6538
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6542
                 &cancelled_filenames,
 
 
6544
                 &mandos_client_exited,
 
 
6548
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
6550
  g_assert_true(queue->next_run == 1);
 
 
6552
  __attribute__((cleanup(string_set_clear)))
 
 
6553
    string_set seen_questions = {};
 
 
6555
  bool queue_contains_question_opener(char *const question_filename){
 
 
6556
    return(find_matching_task(queue, (task_context){
 
 
6557
          .func=open_and_parse_question,
 
 
6559
          .question_filename=question_filename,
 
 
6560
          .password=&password,
 
 
6561
          .cancelled_filenames=&cancelled_filenames,
 
 
6562
          .current_time=¤t_time,
 
 
6563
          .mandos_client_exited=&mandos_client_exited,
 
 
6564
          .password_is_read=&password_is_read,
 
 
6568
  g_assert_true(queue_contains_question_opener(question_filename1));
 
 
6569
  g_assert_true(queue_contains_question_opener(question_filename2));
 
 
6571
  g_assert_true(queue->next_run == 1);
 
 
6573
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6574
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6575
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6579
test_add_existing_questions_non_questions(__attribute__((unused))
 
 
6580
                                          test_fixture *fixture,
 
 
6581
                                          __attribute__((unused))
 
 
6582
                                          gconstpointer user_data){
 
 
6583
  __attribute__((cleanup(cleanup_queue)))
 
 
6584
    task_queue *queue = create_queue();
 
 
6585
  g_assert_nonnull(queue);
 
 
6586
  __attribute__((cleanup(cleanup_close)))
 
 
6587
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6588
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6589
  __attribute__((cleanup(string_set_clear)))
 
 
6590
    string_set cancelled_filenames = {};
 
 
6591
  __attribute__((cleanup(cleanup_string)))
 
 
6592
    char *tempdir = make_temporary_directory();
 
 
6593
  g_assert_nonnull(tempdir);
 
 
6594
  __attribute__((cleanup(cleanup_string)))
 
 
6595
    char *question_filename1
 
 
6596
    = make_temporary_file_in_directory(tempdir);
 
 
6597
  g_assert_nonnull(question_filename1);
 
 
6598
  __attribute__((cleanup(cleanup_string)))
 
 
6599
    char *question_filename2
 
 
6600
    = make_temporary_file_in_directory(tempdir);
 
 
6601
  g_assert_nonnull(question_filename2);
 
 
6603
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6606
                  (buffer[]){{}}, /* password */
 
 
6607
                  &cancelled_filenames,
 
 
6608
                  (mono_microsecs[]){0}, /* current_time */
 
 
6609
                  (bool[]){false},       /* mandos_client_exited */
 
 
6610
                  (bool[]){false},       /* password_is_read */
 
 
6613
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6615
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6616
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6617
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6621
test_add_existing_questions_both_types(__attribute__((unused))
 
 
6622
                                       test_fixture *fixture,
 
 
6623
                                       __attribute__((unused))
 
 
6624
                                       gconstpointer user_data){
 
 
6625
  __attribute__((cleanup(cleanup_queue)))
 
 
6626
    task_queue *queue = create_queue();
 
 
6627
  g_assert_nonnull(queue);
 
 
6628
  __attribute__((cleanup(cleanup_close)))
 
 
6629
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6630
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6631
  __attribute__((cleanup(cleanup_buffer)))
 
 
6632
    buffer password = {};
 
 
6633
  __attribute__((cleanup(string_set_clear)))
 
 
6634
    string_set cancelled_filenames = {};
 
 
6635
  const mono_microsecs current_time = 0;
 
 
6636
  bool mandos_client_exited = false;
 
 
6637
  bool password_is_read = false;
 
 
6638
  __attribute__((cleanup(cleanup_string)))
 
 
6639
    char *tempdir = make_temporary_directory();
 
 
6640
  g_assert_nonnull(tempdir);
 
 
6641
  __attribute__((cleanup(cleanup_string)))
 
 
6642
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
 
6643
  g_assert_nonnull(tempfilename1);
 
 
6644
  __attribute__((cleanup(cleanup_string)))
 
 
6645
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
 
6646
  g_assert_nonnull(tempfilename2);
 
 
6647
  __attribute__((cleanup(cleanup_string)))
 
 
6648
    char *question_filename
 
 
6649
    = make_question_file_in_directory(tempdir);
 
 
6650
  g_assert_nonnull(question_filename);
 
 
6652
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6656
                 &cancelled_filenames,
 
 
6658
                 &mandos_client_exited,
 
 
6662
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6664
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6665
        .func=open_and_parse_question,
 
 
6667
        .filename=question_filename,
 
 
6668
        .question_filename=question_filename,
 
 
6669
        .password=&password,
 
 
6670
        .cancelled_filenames=&cancelled_filenames,
 
 
6671
        .current_time=¤t_time,
 
 
6672
        .mandos_client_exited=&mandos_client_exited,
 
 
6673
        .password_is_read=&password_is_read,
 
 
6676
  g_assert_true(queue->next_run == 1);
 
 
6678
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
 
6679
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
 
6680
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6681
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6684
static void test_wait_for_event_timeout(__attribute__((unused))
 
 
6685
                                        test_fixture *fixture,
 
 
6686
                                        __attribute__((unused))
 
 
6687
                                        gconstpointer user_data){
 
 
6688
  __attribute__((cleanup(cleanup_close)))
 
 
6689
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6690
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6692
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
 
6695
static void test_wait_for_event_event(__attribute__((unused))
 
 
6696
                                      test_fixture *fixture,
 
 
6697
                                      __attribute__((unused))
 
 
6698
                                      gconstpointer user_data){
 
 
6699
  __attribute__((cleanup(cleanup_close)))
 
 
6700
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6701
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6703
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6704
  __attribute__((cleanup(cleanup_close)))
 
 
6705
    const int read_pipe = pipefds[0];
 
 
6706
  __attribute__((cleanup(cleanup_close)))
 
 
6707
    const int write_pipe = pipefds[1];
 
 
6708
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
 
6709
                            &(struct epoll_event)
 
 
6710
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
 
6711
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
 
6713
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6716
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
 
6717
                                        __attribute__((unused))
 
 
6718
                                        gconstpointer user_data){
 
 
6719
  const pid_t pid = fork();
 
 
6720
  if(pid == 0){         /* Child */
 
 
6721
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
6722
      _exit(EXIT_FAILURE);
 
 
6724
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
6725
      _exit(EXIT_FAILURE);
 
 
6729
  g_assert_true(pid != -1);
 
 
6730
  __attribute__((cleanup(cleanup_close)))
 
 
6731
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6732
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6734
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6737
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
 
6738
  g_assert_true(WIFEXITED(status));
 
 
6739
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
 
6742
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
 
6743
                                           test_fixture *fixture,
 
 
6744
                                           __attribute__((unused))
 
 
6745
                                           gconstpointer user_data){
 
 
6746
  __attribute__((cleanup(cleanup_queue)))
 
 
6747
    task_queue *queue = create_queue();
 
 
6748
  g_assert_nonnull(queue);
 
 
6749
  queue->next_run = 1;
 
 
6750
  __attribute__((cleanup(cleanup_close)))
 
 
6751
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6752
  __attribute__((cleanup(string_set_clear)))
 
 
6753
    string_set cancelled_filenames = {};
 
 
6754
  bool quit_now = false;
 
 
6756
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6757
  g_assert_false(quit_now);
 
 
6758
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
 
6762
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
 
6763
                                               test_fixture *fixture,
 
 
6764
                                               __attribute__((unused))
 
 
6767
  __attribute__((cleanup(cleanup_queue)))
 
 
6768
    task_queue *queue = create_queue();
 
 
6769
  g_assert_nonnull(queue);
 
 
6770
  __attribute__((cleanup(string_set_clear)))
 
 
6771
    string_set cancelled_filenames = {};
 
 
6772
  bool quit_now = false;
 
 
6773
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6774
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6775
                               question_filename));
 
 
6777
  g_assert_true(add_to_queue(queue,
 
 
6778
                             (task_context){ .func=dummy_func }));
 
 
6780
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6781
  g_assert_false(quit_now);
 
 
6782
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6783
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
6784
                                     question_filename));
 
 
6788
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
 
6789
                                              test_fixture *fixture,
 
 
6790
                                              __attribute__((unused))
 
 
6793
  __attribute__((cleanup(cleanup_queue)))
 
 
6794
    task_queue *queue = create_queue();
 
 
6795
  g_assert_nonnull(queue);
 
 
6796
  __attribute__((cleanup(string_set_clear)))
 
 
6797
    string_set cancelled_filenames = {};
 
 
6798
  bool quit_now = false;
 
 
6800
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6801
  __attribute__((cleanup(cleanup_close)))
 
 
6802
    const int read_pipe = pipefds[0];
 
 
6803
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
6804
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6805
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6806
                               question_filename));
 
 
6807
  __attribute__((nonnull))
 
 
6808
    void quit_func(const task_context task,
 
 
6809
                   __attribute__((unused)) task_queue *const q){
 
 
6810
    g_assert_nonnull(task.quit_now);
 
 
6811
    *task.quit_now = true;
 
 
6813
  task_context task = {
 
 
6815
    .question_filename=strdup(question_filename),
 
 
6816
    .quit_now=&quit_now,
 
 
6819
  g_assert_nonnull(task.question_filename);
 
 
6821
  g_assert_true(add_to_queue(queue, task));
 
 
6823
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6824
  g_assert_false(quit_now);
 
 
6826
  /* read_pipe should be closed already */
 
 
6828
  bool read_pipe_closed = (close(read_pipe) == -1);
 
 
6829
  read_pipe_closed &= (errno == EBADF);
 
 
6830
  g_assert_true(read_pipe_closed);
 
 
6833
static void test_run_queue_one_task(__attribute__((unused))
 
 
6834
                                    test_fixture *fixture,
 
 
6835
                                    __attribute__((unused))
 
 
6836
                                    gconstpointer user_data){
 
 
6837
  __attribute__((cleanup(cleanup_queue)))
 
 
6838
    task_queue *queue = create_queue();
 
 
6839
  g_assert_nonnull(queue);
 
 
6840
  __attribute__((cleanup(string_set_clear)))
 
 
6841
    string_set cancelled_filenames = {};
 
 
6842
  bool quit_now = false;
 
 
6844
  __attribute__((nonnull))
 
 
6845
    void next_run_func(__attribute__((unused))
 
 
6846
                       const task_context task,
 
 
6847
                       task_queue *const q){
 
 
6851
  task_context task = {
 
 
6852
    .func=next_run_func,
 
 
6854
  g_assert_true(add_to_queue(queue, task));
 
 
6856
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6857
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6858
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6861
static void test_run_queue_two_tasks(__attribute__((unused))
 
 
6862
                                     test_fixture *fixture,
 
 
6863
                                     __attribute__((unused))
 
 
6864
                                     gconstpointer user_data){
 
 
6865
  __attribute__((cleanup(cleanup_queue)))
 
 
6866
    task_queue *queue = create_queue();
 
 
6867
  g_assert_nonnull(queue);
 
 
6868
  queue->next_run = 1;
 
 
6869
  __attribute__((cleanup(string_set_clear)))
 
 
6870
    string_set cancelled_filenames = {};
 
 
6871
  bool quit_now = false;
 
 
6872
  bool mandos_client_exited = false;
 
 
6874
  __attribute__((nonnull))
 
 
6875
    void next_run_func(__attribute__((unused))
 
 
6876
                       const task_context task,
 
 
6877
                       task_queue *const q){
 
 
6881
  __attribute__((nonnull))
 
 
6882
    void exited_func(const task_context task,
 
 
6883
                     __attribute__((unused)) task_queue *const q){
 
 
6884
    *task.mandos_client_exited = true;
 
 
6887
  task_context task1 = {
 
 
6888
    .func=next_run_func,
 
 
6890
  g_assert_true(add_to_queue(queue, task1));
 
 
6892
  task_context task2 = {
 
 
6894
    .mandos_client_exited=&mandos_client_exited,
 
 
6896
  g_assert_true(add_to_queue(queue, task2));
 
 
6898
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6899
  g_assert_false(quit_now);
 
 
6900
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6901
  g_assert_true(mandos_client_exited);
 
 
6902
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6905
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
 
6906
                                          test_fixture *fixture,
 
 
6907
                                          __attribute__((unused))
 
 
6908
                                          gconstpointer user_data){
 
 
6909
  __attribute__((cleanup(cleanup_queue)))
 
 
6910
    task_queue *queue = create_queue();
 
 
6911
  g_assert_nonnull(queue);
 
 
6912
  __attribute__((cleanup(string_set_clear)))
 
 
6913
    string_set cancelled_filenames = {};
 
 
6914
  bool quit_now = false;
 
 
6915
  bool mandos_client_exited = false;
 
 
6916
  bool password_is_read = false;
 
 
6918
  __attribute__((nonnull))
 
 
6919
    void set_exited_func(const task_context task,
 
 
6920
                         __attribute__((unused)) task_queue *const q){
 
 
6921
    *task.mandos_client_exited = true;
 
 
6922
    *task.quit_now = true;
 
 
6924
  task_context task1 = {
 
 
6925
    .func=set_exited_func,
 
 
6926
    .quit_now=&quit_now,
 
 
6927
    .mandos_client_exited=&mandos_client_exited,
 
 
6929
  g_assert_true(add_to_queue(queue, task1));
 
 
6931
  __attribute__((nonnull))
 
 
6932
    void set_read_func(const task_context task,
 
 
6933
                       __attribute__((unused)) task_queue *const q){
 
 
6934
    *task.quit_now = true;
 
 
6935
    *task.password_is_read = true;
 
 
6937
  task_context task2 = {
 
 
6938
    .func=set_read_func,
 
 
6939
    .quit_now=&quit_now,
 
 
6940
    .password_is_read=&password_is_read,
 
 
6942
  g_assert_true(add_to_queue(queue, task2));
 
 
6944
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6945
  g_assert_true(quit_now);
 
 
6946
  g_assert_true(mandos_client_exited xor password_is_read);
 
 
6947
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6950
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
 
6951
                                             test_fixture *fixture,
 
 
6952
                                             __attribute__((unused))
 
 
6953
                                             gconstpointer user_data){
 
 
6954
  __attribute__((cleanup(cleanup_queue)))
 
 
6955
    task_queue *queue = create_queue();
 
 
6956
  g_assert_nonnull(queue);
 
 
6957
  __attribute__((cleanup(string_set_clear)))
 
 
6958
    string_set cancelled_filenames = {};
 
 
6960
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6961
  __attribute__((cleanup(cleanup_close)))
 
 
6962
    const int read_pipe = pipefds[0];
 
 
6963
  __attribute__((cleanup(cleanup_close)))
 
 
6964
    const int write_pipe = pipefds[1];
 
 
6965
  bool quit_now = false;
 
 
6967
  __attribute__((nonnull))
 
 
6968
    void read_func(const task_context task,
 
 
6969
                   __attribute__((unused)) task_queue *const q){
 
 
6970
    *task.quit_now = true;
 
 
6972
  task_context task1 = {
 
 
6974
    .quit_now=&quit_now,
 
 
6977
  g_assert_true(add_to_queue(queue, task1));
 
 
6979
  __attribute__((nonnull))
 
 
6980
    void write_func(const task_context task,
 
 
6981
                    __attribute__((unused)) task_queue *const q){
 
 
6982
    *task.quit_now = true;
 
 
6984
  task_context task2 = {
 
 
6986
    .quit_now=&quit_now,
 
 
6989
  g_assert_true(add_to_queue(queue, task2));
 
 
6991
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6992
  g_assert_true(quit_now);
 
 
6994
  /* Either read_pipe or write_pipe should be closed already */
 
 
6996
  bool close_read_pipe = (close(read_pipe) == -1);
 
 
6997
  close_read_pipe &= (errno == EBADF);
 
 
6999
  bool close_write_pipe = (close(write_pipe) == -1);
 
 
7000
  close_write_pipe &= (errno == EBADF);
 
 
7001
  g_assert_true(close_read_pipe xor close_write_pipe);
 
 
7002
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
7005
static void test_setup_signal_handler(__attribute__((unused))
 
 
7006
                                      test_fixture *fixture,
 
 
7007
                                      __attribute__((unused))
 
 
7008
                                      gconstpointer user_data){
 
 
7009
  /* Save current SIGCHLD action, whatever it is */
 
 
7010
  struct sigaction expected_sigchld_action;
 
 
7011
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7014
  /* Act; i.e. run the setup_signal_handler() function */
 
 
7015
  struct sigaction actual_old_sigchld_action;
 
 
7016
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
 
7018
  /* Check that the function correctly set "actual_old_sigchld_action"
 
 
7019
     to the same values as the previously saved
 
 
7020
     "expected_sigchld_action" */
 
 
7021
  /* Check member sa_handler */
 
 
7022
  g_assert_true(actual_old_sigchld_action.sa_handler
 
 
7023
                == expected_sigchld_action.sa_handler);
 
 
7024
  /* Check member sa_mask */
 
 
7025
  for(int signum = 1; signum < NSIG; signum++){
 
 
7026
    const int expected_old_block_state
 
 
7027
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7028
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7029
    const int actual_old_block_state
 
 
7030
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
 
7031
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7032
    g_assert_cmpint(actual_old_block_state,
 
 
7033
                    ==, expected_old_block_state);
 
 
7035
  /* Check member sa_flags */
 
 
7036
  g_assert_true((actual_old_sigchld_action.sa_flags
 
 
7037
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7038
                == (expected_sigchld_action.sa_flags
 
 
7039
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7041
  /* Retrieve the current signal handler for SIGCHLD as set by
 
 
7042
     setup_signal_handler() */
 
 
7043
  struct sigaction actual_new_sigchld_action;
 
 
7044
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7045
                            &actual_new_sigchld_action), ==, 0);
 
 
7046
  /* Check that the signal handler (member sa_handler) is correctly
 
 
7047
     set to the "handle_sigchld" function */
 
 
7048
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
 
7049
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
 
7050
  g_assert_true(actual_new_sigchld_action.sa_handler
 
 
7052
  /* Check (in member sa_mask) that at least a handful of signals are
 
 
7053
     actually blocked during the signal handler */
 
 
7054
  for(int signum = 1; signum < NSIG; signum++){
 
 
7055
    int actual_new_block_state;
 
 
7061
      actual_new_block_state
 
 
7062
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
 
7063
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
 
7065
    case SIGKILL:               /* non-blockable */
 
 
7066
    case SIGSTOP:               /* non-blockable */
 
 
7067
    case SIGCHLD:               /* always blocked */
 
 
7072
  /* Check member sa_flags */
 
 
7073
  g_assert_true((actual_new_sigchld_action.sa_flags
 
 
7074
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7075
                == (SA_NOCLDSTOP | SA_RESTART));
 
 
7077
  /* Restore signal handler */
 
 
7078
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
 
7082
static void test_restore_signal_handler(__attribute__((unused))
 
 
7083
                                        test_fixture *fixture,
 
 
7084
                                        __attribute__((unused))
 
 
7085
                                        gconstpointer user_data){
 
 
7086
  /* Save current SIGCHLD action, whatever it is */
 
 
7087
  struct sigaction expected_sigchld_action;
 
 
7088
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7090
  /* Since we haven't established a signal handler yet, there should
 
 
7091
     not be one established.  But another test may have relied on
 
 
7092
     restore_signal_handler() to restore the signal handler, and if
 
 
7093
     restore_signal_handler() is buggy (which we should be prepared
 
 
7094
     for in this test) the signal handler may not have been restored
 
 
7095
     properly; check for this: */
 
 
7096
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
 
7098
  /* Establish a signal handler */
 
 
7099
  struct sigaction sigchld_action = {
 
 
7100
    .sa_handler=handle_sigchld,
 
 
7101
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
7103
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
 
7104
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
 
7106
  /* Act; i.e. run the restore_signal_handler() function */
 
 
7107
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
 
7109
  /* Retrieve the restored signal handler data */
 
 
7110
  struct sigaction actual_restored_sigchld_action;
 
 
7111
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7112
                            &actual_restored_sigchld_action), ==, 0);
 
 
7114
  /* Check that the function correctly restored the signal action, as
 
 
7115
     saved in "actual_restored_sigchld_action", to the same values as
 
 
7116
     the previously saved "expected_sigchld_action" */
 
 
7117
  /* Check member sa_handler */
 
 
7118
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
 
7119
                == expected_sigchld_action.sa_handler);
 
 
7120
  /* Check member sa_mask */
 
 
7121
  for(int signum = 1; signum < NSIG; signum++){
 
 
7122
    const int expected_old_block_state
 
 
7123
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7124
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7125
    const int actual_restored_block_state
 
 
7126
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
 
7127
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
 
7128
    g_assert_cmpint(actual_restored_block_state,
 
 
7129
                    ==, expected_old_block_state);
 
 
7131
  /* Check member sa_flags */
 
 
7132
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
 
7133
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7134
                == (expected_sigchld_action.sa_flags
 
 
7135
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7138
static void test_block_sigchld(__attribute__((unused))
 
 
7139
                               test_fixture *fixture,
 
 
7140
                               __attribute__((unused))
 
 
7141
                               gconstpointer user_data){
 
 
7142
  /* Save original signal mask */
 
 
7143
  sigset_t expected_sigmask;
 
 
7144
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
 
7147
  /* Make sure SIGCHLD is unblocked for this test */
 
 
7148
  sigset_t sigchld_sigmask;
 
 
7149
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7150
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7151
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
 
7154
  /* Act; i.e. run the block_sigchld() function */
 
 
7155
  sigset_t actual_old_sigmask;
 
 
7156
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
 
7158
  /* Check the actual_old_sigmask; it should be the same as the
 
 
7159
     previously saved signal mask "expected_sigmask". */
 
 
7160
  for(int signum = 1; signum < NSIG; signum++){
 
 
7161
    const int expected_old_block_state
 
 
7162
      = sigismember(&expected_sigmask, signum);
 
 
7163
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7164
    const int actual_old_block_state
 
 
7165
      = sigismember(&actual_old_sigmask, signum);
 
 
7166
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7167
    g_assert_cmpint(actual_old_block_state,
 
 
7168
                    ==, expected_old_block_state);
 
 
7171
  /* Retrieve the newly set signal mask */
 
 
7172
  sigset_t actual_sigmask;
 
 
7173
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
 
7175
  /* SIGCHLD should be blocked */
 
 
7176
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
 
7178
  /* Restore signal mask */
 
 
7179
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
 
7183
static void test_restore_sigmask(__attribute__((unused))
 
 
7184
                                 test_fixture *fixture,
 
 
7185
                                 __attribute__((unused))
 
 
7186
                                 gconstpointer user_data){
 
 
7187
  /* Save original signal mask */
 
 
7188
  sigset_t orig_sigmask;
 
 
7189
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
 
7191
  /* Make sure SIGCHLD is blocked for this test */
 
 
7192
  sigset_t sigchld_sigmask;
 
 
7193
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7194
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7195
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
 
7198
  /* Act; i.e. run the restore_sigmask() function */
 
 
7199
  g_assert_true(restore_sigmask(&orig_sigmask));
 
 
7201
  /* Retrieve the newly restored signal mask */
 
 
7202
  sigset_t restored_sigmask;
 
 
7203
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
 
7206
  /* Check the restored_sigmask; it should be the same as the
 
 
7207
     previously saved signal mask "orig_sigmask". */
 
 
7208
  for(int signum = 1; signum < NSIG; signum++){
 
 
7209
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
 
7210
    g_assert_cmpint(orig_block_state, >=, 0);
 
 
7211
    const int restored_block_state = sigismember(&restored_sigmask,
 
 
7213
    g_assert_cmpint(restored_block_state, >=, 0);
 
 
7214
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
 
7217
  /* Restore signal mask */
 
 
7218
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
 
7222
static void test_parse_arguments_noargs(__attribute__((unused))
 
 
7223
                                        test_fixture *fixture,
 
 
7224
                                        __attribute__((unused))
 
 
7225
                                        gconstpointer user_data){
 
 
7229
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7231
  char *agent_directory = NULL;
 
 
7232
  char *helper_directory = NULL;
 
 
7235
  char *mandos_argz = NULL;
 
 
7236
  size_t mandos_argz_length = 0;
 
 
7238
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7239
                                &helper_directory, &user, &group,
 
 
7240
                                &mandos_argz, &mandos_argz_length));
 
 
7241
  g_assert_null(agent_directory);
 
 
7242
  g_assert_null(helper_directory);
 
 
7243
  g_assert_true(user == 0);
 
 
7244
  g_assert_true(group == 0);
 
 
7245
  g_assert_null(mandos_argz);
 
 
7246
  g_assert_true(mandos_argz_length == 0);
 
 
7248
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7253
__attribute__((nonnull))
 
 
7254
static bool parse_arguments_devnull(int argc, char *argv[],
 
 
7255
                                    const bool exit_failure,
 
 
7256
                                    char **agent_directory,
 
 
7257
                                    char **helper_directory,
 
 
7261
                                    size_t *mandos_argz_length){
 
 
7263
  FILE *real_stderr = stderr;
 
 
7264
  FILE *devnull = fopen("/dev/null", "we");
 
 
7265
  g_assert_nonnull(devnull);
 
 
7268
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
 
7270
                                   helper_directory, user, group,
 
 
7271
                                   mandos_argz, mandos_argz_length);
 
 
7272
  const error_t saved_errno = errno;
 
 
7274
  stderr = real_stderr;
 
 
7275
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
7277
  errno = saved_errno;
 
 
7282
static void test_parse_arguments_invalid(__attribute__((unused))
 
 
7283
                                         test_fixture *fixture,
 
 
7284
                                         __attribute__((unused))
 
 
7285
                                         gconstpointer user_data){
 
 
7288
    strdup("--invalid"),
 
 
7290
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7292
  char *agent_directory = NULL;
 
 
7293
  char *helper_directory = NULL;
 
 
7296
  char *mandos_argz = NULL;
 
 
7297
  size_t mandos_argz_length = 0;
 
 
7299
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7301
                                         &helper_directory, &user,
 
 
7302
                                         &group, &mandos_argz,
 
 
7303
                                         &mandos_argz_length));
 
 
7305
  g_assert_true(errno == EINVAL);
 
 
7306
  g_assert_null(agent_directory);
 
 
7307
  g_assert_null(helper_directory);
 
 
7308
  g_assert_null(mandos_argz);
 
 
7309
  g_assert_true(mandos_argz_length == 0);
 
 
7311
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7316
static void test_parse_arguments_long_dir(__attribute__((unused))
 
 
7317
                                          test_fixture *fixture,
 
 
7318
                                          __attribute__((unused))
 
 
7319
                                          gconstpointer user_data){
 
 
7322
    strdup("--agent-directory"),
 
 
7325
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7327
  __attribute__((cleanup(cleanup_string)))
 
 
7328
    char *agent_directory = NULL;
 
 
7329
  char *helper_directory = NULL;
 
 
7332
  __attribute__((cleanup(cleanup_string)))
 
 
7333
    char *mandos_argz = NULL;
 
 
7334
  size_t mandos_argz_length = 0;
 
 
7336
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7337
                                &helper_directory, &user, &group,
 
 
7338
                                &mandos_argz, &mandos_argz_length));
 
 
7340
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7341
  g_assert_null(helper_directory);
 
 
7342
  g_assert_true(user == 0);
 
 
7343
  g_assert_true(group == 0);
 
 
7344
  g_assert_null(mandos_argz);
 
 
7345
  g_assert_true(mandos_argz_length == 0);
 
 
7347
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7352
static void test_parse_arguments_short_dir(__attribute__((unused))
 
 
7353
                                           test_fixture *fixture,
 
 
7354
                                           __attribute__((unused))
 
 
7355
                                           gconstpointer user_data){
 
 
7361
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7363
  __attribute__((cleanup(cleanup_string)))
 
 
7364
    char *agent_directory = NULL;
 
 
7365
  char *helper_directory = NULL;
 
 
7368
  __attribute__((cleanup(cleanup_string)))
 
 
7369
    char *mandos_argz = NULL;
 
 
7370
  size_t mandos_argz_length = 0;
 
 
7372
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7373
                                &helper_directory, &user, &group,
 
 
7374
                                &mandos_argz, &mandos_argz_length));
 
 
7376
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7377
  g_assert_null(helper_directory);
 
 
7378
  g_assert_true(user == 0);
 
 
7379
  g_assert_true(group == 0);
 
 
7380
  g_assert_null(mandos_argz);
 
 
7381
  g_assert_true(mandos_argz_length == 0);
 
 
7383
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7389
void test_parse_arguments_helper_directory(__attribute__((unused))
 
 
7390
                                           test_fixture *fixture,
 
 
7391
                                           __attribute__((unused))
 
 
7392
                                           gconstpointer user_data){
 
 
7395
    strdup("--helper-directory"),
 
 
7398
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7400
  char *agent_directory = NULL;
 
 
7401
  __attribute__((cleanup(cleanup_string)))
 
 
7402
    char *helper_directory = NULL;
 
 
7405
  __attribute__((cleanup(cleanup_string)))
 
 
7406
    char *mandos_argz = NULL;
 
 
7407
  size_t mandos_argz_length = 0;
 
 
7409
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7410
                                &helper_directory, &user, &group,
 
 
7411
                                &mandos_argz, &mandos_argz_length));
 
 
7413
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7414
  g_assert_null(agent_directory);
 
 
7415
  g_assert_true(user == 0);
 
 
7416
  g_assert_true(group == 0);
 
 
7417
  g_assert_null(mandos_argz);
 
 
7418
  g_assert_true(mandos_argz_length == 0);
 
 
7420
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7426
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
 
7427
                                            test_fixture *fixture,
 
 
7428
                                            __attribute__((unused))
 
 
7429
                                            gconstpointer user_data){
 
 
7432
    strdup("--plugin-helper-dir"),
 
 
7435
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7437
  char *agent_directory = NULL;
 
 
7438
  __attribute__((cleanup(cleanup_string)))
 
 
7439
    char *helper_directory = NULL;
 
 
7442
  __attribute__((cleanup(cleanup_string)))
 
 
7443
    char *mandos_argz = NULL;
 
 
7444
  size_t mandos_argz_length = 0;
 
 
7446
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7447
                                &helper_directory, &user, &group,
 
 
7448
                                &mandos_argz, &mandos_argz_length));
 
 
7450
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7451
  g_assert_null(agent_directory);
 
 
7452
  g_assert_true(user == 0);
 
 
7453
  g_assert_true(group == 0);
 
 
7454
  g_assert_null(mandos_argz);
 
 
7455
  g_assert_true(mandos_argz_length == 0);
 
 
7457
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7462
static void test_parse_arguments_user(__attribute__((unused))
 
 
7463
                                      test_fixture *fixture,
 
 
7464
                                      __attribute__((unused))
 
 
7465
                                      gconstpointer user_data){
 
 
7471
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7473
  char *agent_directory = NULL;
 
 
7474
  __attribute__((cleanup(cleanup_string)))
 
 
7475
    char *helper_directory = NULL;
 
 
7478
  __attribute__((cleanup(cleanup_string)))
 
 
7479
    char *mandos_argz = NULL;
 
 
7480
  size_t mandos_argz_length = 0;
 
 
7482
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7483
                                &helper_directory, &user, &group,
 
 
7484
                                &mandos_argz, &mandos_argz_length));
 
 
7486
  g_assert_null(helper_directory);
 
 
7487
  g_assert_null(agent_directory);
 
 
7488
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
 
7489
  g_assert_true(group == 0);
 
 
7490
  g_assert_null(mandos_argz);
 
 
7491
  g_assert_true(mandos_argz_length == 0);
 
 
7493
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7498
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
 
7499
                                              test_fixture *fixture,
 
 
7500
                                              __attribute__((unused))
 
 
7508
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7510
  char *agent_directory = NULL;
 
 
7511
  __attribute__((cleanup(cleanup_string)))
 
 
7512
    char *helper_directory = NULL;
 
 
7515
  __attribute__((cleanup(cleanup_string)))
 
 
7516
    char *mandos_argz = NULL;
 
 
7517
  size_t mandos_argz_length = 0;
 
 
7519
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7521
                                         &helper_directory, &user,
 
 
7522
                                         &group, &mandos_argz,
 
 
7523
                                         &mandos_argz_length));
 
 
7525
  g_assert_null(helper_directory);
 
 
7526
  g_assert_null(agent_directory);
 
 
7527
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7528
  g_assert_true(group == 0);
 
 
7529
  g_assert_null(mandos_argz);
 
 
7530
  g_assert_true(mandos_argz_length == 0);
 
 
7532
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7538
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
 
7539
                                            test_fixture *fixture,
 
 
7540
                                            __attribute__((unused))
 
 
7541
                                            gconstpointer user_data){
 
 
7547
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7549
  char *agent_directory = NULL;
 
 
7550
  __attribute__((cleanup(cleanup_string)))
 
 
7551
    char *helper_directory = NULL;
 
 
7554
  __attribute__((cleanup(cleanup_string)))
 
 
7555
    char *mandos_argz = NULL;
 
 
7556
  size_t mandos_argz_length = 0;
 
 
7558
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7560
                                         &helper_directory, &user,
 
 
7561
                                         &group, &mandos_argz,
 
 
7562
                                         &mandos_argz_length));
 
 
7564
  g_assert_null(helper_directory);
 
 
7565
  g_assert_null(agent_directory);
 
 
7566
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7567
  g_assert_true(group == 0);
 
 
7568
  g_assert_null(mandos_argz);
 
 
7569
  g_assert_true(mandos_argz_length == 0);
 
 
7571
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7576
static void test_parse_arguments_group(__attribute__((unused))
 
 
7577
                                       test_fixture *fixture,
 
 
7578
                                       __attribute__((unused))
 
 
7579
                                       gconstpointer user_data){
 
 
7585
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7587
  char *agent_directory = NULL;
 
 
7588
  __attribute__((cleanup(cleanup_string)))
 
 
7589
    char *helper_directory = NULL;
 
 
7592
  __attribute__((cleanup(cleanup_string)))
 
 
7593
    char *mandos_argz = NULL;
 
 
7594
  size_t mandos_argz_length = 0;
 
 
7596
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7597
                                &helper_directory, &user, &group,
 
 
7598
                                &mandos_argz, &mandos_argz_length));
 
 
7600
  g_assert_null(helper_directory);
 
 
7601
  g_assert_null(agent_directory);
 
 
7602
  g_assert_true(user == 0);
 
 
7603
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
 
7604
  g_assert_null(mandos_argz);
 
 
7605
  g_assert_true(mandos_argz_length == 0);
 
 
7607
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7612
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
 
7613
                                               test_fixture *fixture,
 
 
7614
                                               __attribute__((unused))
 
 
7622
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7624
  char *agent_directory = NULL;
 
 
7625
  __attribute__((cleanup(cleanup_string)))
 
 
7626
    char *helper_directory = NULL;
 
 
7629
  __attribute__((cleanup(cleanup_string)))
 
 
7630
    char *mandos_argz = NULL;
 
 
7631
  size_t mandos_argz_length = 0;
 
 
7633
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7635
                                         &helper_directory, &user,
 
 
7636
                                         &group, &mandos_argz,
 
 
7637
                                         &mandos_argz_length));
 
 
7639
  g_assert_null(helper_directory);
 
 
7640
  g_assert_null(agent_directory);
 
 
7641
  g_assert_true(user == 0);
 
 
7642
  g_assert_true(group == 0);
 
 
7643
  g_assert_null(mandos_argz);
 
 
7644
  g_assert_true(mandos_argz_length == 0);
 
 
7646
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7652
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
 
7653
                                             test_fixture *fixture,
 
 
7654
                                             __attribute__((unused))
 
 
7655
                                             gconstpointer user_data){
 
 
7661
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7663
  char *agent_directory = NULL;
 
 
7664
  __attribute__((cleanup(cleanup_string)))
 
 
7665
    char *helper_directory = NULL;
 
 
7668
  __attribute__((cleanup(cleanup_string)))
 
 
7669
    char *mandos_argz = NULL;
 
 
7670
  size_t mandos_argz_length = 0;
 
 
7672
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7674
                                         &helper_directory, &user,
 
 
7675
                                         &group, &mandos_argz,
 
 
7676
                                         &mandos_argz_length));
 
 
7678
  g_assert_null(helper_directory);
 
 
7679
  g_assert_null(agent_directory);
 
 
7680
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
 
7681
  g_assert_true(group == 0);
 
 
7682
  g_assert_null(mandos_argz);
 
 
7683
  g_assert_true(mandos_argz_length == 0);
 
 
7685
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7690
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
 
7691
                                               test_fixture *fixture,
 
 
7692
                                               __attribute__((unused))
 
 
7697
    strdup("mandos-client"),
 
 
7699
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7701
  __attribute__((cleanup(cleanup_string)))
 
 
7702
    char *agent_directory = NULL;
 
 
7703
  __attribute__((cleanup(cleanup_string)))
 
 
7704
    char *helper_directory = NULL;
 
 
7707
  __attribute__((cleanup(cleanup_string)))
 
 
7708
    char *mandos_argz = NULL;
 
 
7709
  size_t mandos_argz_length = 0;
 
 
7711
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7712
                                &helper_directory, &user, &group,
 
 
7713
                                &mandos_argz, &mandos_argz_length));
 
 
7715
  g_assert_null(agent_directory);
 
 
7716
  g_assert_null(helper_directory);
 
 
7717
  g_assert_true(user == 0);
 
 
7718
  g_assert_true(group == 0);
 
 
7719
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
 
7720
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7721
                                            mandos_argz_length),
 
 
7724
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7729
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
 
7730
                                             test_fixture *fixture,
 
 
7731
                                             __attribute__((unused))
 
 
7732
                                             gconstpointer user_data){
 
 
7735
    strdup("mandos-client"),
 
 
7740
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7742
  __attribute__((cleanup(cleanup_string)))
 
 
7743
    char *agent_directory = NULL;
 
 
7744
  __attribute__((cleanup(cleanup_string)))
 
 
7745
    char *helper_directory = NULL;
 
 
7748
  __attribute__((cleanup(cleanup_string)))
 
 
7749
    char *mandos_argz = NULL;
 
 
7750
  size_t mandos_argz_length = 0;
 
 
7752
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7753
                                &helper_directory, &user, &group,
 
 
7754
                                &mandos_argz, &mandos_argz_length));
 
 
7756
  g_assert_null(agent_directory);
 
 
7757
  g_assert_null(helper_directory);
 
 
7758
  g_assert_true(user == 0);
 
 
7759
  g_assert_true(group == 0);
 
 
7760
  char *marg = mandos_argz;
 
 
7761
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7762
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7763
  g_assert_cmpstr(marg, ==, "one");
 
 
7764
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7765
  g_assert_cmpstr(marg, ==, "two");
 
 
7766
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7767
  g_assert_cmpstr(marg, ==, "three");
 
 
7768
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7769
                                            mandos_argz_length),
 
 
7772
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7777
static void test_parse_arguments_all_args(__attribute__((unused))
 
 
7778
                                          test_fixture *fixture,
 
 
7779
                                          __attribute__((unused))
 
 
7780
                                          gconstpointer user_data){
 
 
7783
    strdup("--agent-directory"),
 
 
7785
    strdup("--helper-directory"),
 
 
7791
    strdup("mandos-client"),
 
 
7796
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7798
  __attribute__((cleanup(cleanup_string)))
 
 
7799
    char *agent_directory = NULL;
 
 
7800
  __attribute__((cleanup(cleanup_string)))
 
 
7801
    char *helper_directory = NULL;
 
 
7804
  __attribute__((cleanup(cleanup_string)))
 
 
7805
    char *mandos_argz = NULL;
 
 
7806
  size_t mandos_argz_length = 0;
 
 
7808
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7809
                                &helper_directory, &user, &group,
 
 
7810
                                &mandos_argz, &mandos_argz_length));
 
 
7812
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7813
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7814
  g_assert_true(user == 1);
 
 
7815
  g_assert_true(group == 2);
 
 
7816
  char *marg = mandos_argz;
 
 
7817
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7818
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7819
  g_assert_cmpstr(marg, ==, "one");
 
 
7820
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7821
  g_assert_cmpstr(marg, ==, "two");
 
 
7822
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7823
  g_assert_cmpstr(marg, ==, "three");
 
 
7824
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7825
                                            mandos_argz_length),
 
 
7828
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7833
static void test_parse_arguments_mixed(__attribute__((unused))
 
 
7834
                                       test_fixture *fixture,
 
 
7835
                                       __attribute__((unused))
 
 
7836
                                       gconstpointer user_data){
 
 
7839
    strdup("mandos-client"),
 
 
7843
    strdup("--agent-directory"),
 
 
7847
    strdup("--helper-directory=/var/tmp"),
 
 
7849
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7851
  __attribute__((cleanup(cleanup_string)))
 
 
7852
    char *agent_directory = NULL;
 
 
7853
  __attribute__((cleanup(cleanup_string)))
 
 
7854
    char *helper_directory = NULL;
 
 
7857
  __attribute__((cleanup(cleanup_string)))
 
 
7858
    char *mandos_argz = NULL;
 
 
7859
  size_t mandos_argz_length = 0;
 
 
7861
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7862
                                &helper_directory, &user, &group,
 
 
7863
                                &mandos_argz, &mandos_argz_length));
 
 
7865
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7866
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7867
  g_assert_true(user == 1);
 
 
7868
  g_assert_true(group == 0);
 
 
7869
  char *marg = mandos_argz;
 
 
7870
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7871
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7872
  g_assert_cmpstr(marg, ==, "one");
 
 
7873
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7874
  g_assert_cmpstr(marg, ==, "two");
 
 
7875
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7876
  g_assert_cmpstr(marg, ==, "three");
 
 
7877
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7878
                                            mandos_argz_length),
 
 
7881
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7886
/* End of tests section */
 
 
7888
/* Test boilerplate section; New tests should be added to the test
 
 
7889
   suite definition here, in the "run_tests" function.
 
 
7891
   Finally, this section also contains the should_only_run_tests()
 
 
7892
   function used by main() for deciding if tests should be run or to
 
 
7895
__attribute__((cold))
 
 
7896
static bool run_tests(int argc, char *argv[]){
 
 
7897
  g_test_init(&argc, &argv, NULL);
 
 
7899
  /* A macro to add a test with no setup or teardown functions */
 
 
7900
#define test_add(testpath, testfunc)                    \
 
 
7902
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
 
7903
               (testfunc), NULL);                       \
 
 
7906
  /* Test the signal-related functions first, since some other tests
 
 
7907
     depend on these functions in their setups and teardowns */
 
 
7908
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
 
7909
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
 
7910
  test_add("/signal-handling/block", test_block_sigchld);
 
 
7911
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
 
7913
  /* Regular non-signal-related tests; these use no setups or
 
 
7915
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
 
7916
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
 
7917
  test_add("/parse_arguments/long-dir",
 
 
7918
           test_parse_arguments_long_dir);
 
 
7919
  test_add("/parse_arguments/short-dir",
 
 
7920
           test_parse_arguments_short_dir);
 
 
7921
  test_add("/parse_arguments/helper-directory",
 
 
7922
           test_parse_arguments_helper_directory);
 
 
7923
  test_add("/parse_arguments/plugin-helper-dir",
 
 
7924
           test_parse_arguments_plugin_helper_dir);
 
 
7925
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
 
7926
  test_add("/parse_arguments/user-invalid",
 
 
7927
           test_parse_arguments_user_invalid);
 
 
7928
  test_add("/parse_arguments/user-zero-invalid",
 
 
7929
           test_parse_arguments_user_zero_invalid);
 
 
7930
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
 
7931
  test_add("/parse_arguments/group-invalid",
 
 
7932
           test_parse_arguments_group_invalid);
 
 
7933
  test_add("/parse_arguments/group-zero-invalid",
 
 
7934
           test_parse_arguments_group_zero_invalid);
 
 
7935
  test_add("/parse_arguments/mandos-noargs",
 
 
7936
           test_parse_arguments_mandos_noargs);
 
 
7937
  test_add("/parse_arguments/mandos-args",
 
 
7938
           test_parse_arguments_mandos_args);
 
 
7939
  test_add("/parse_arguments/all-args",
 
 
7940
           test_parse_arguments_all_args);
 
 
7941
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
 
7942
  test_add("/queue/create", test_create_queue);
 
 
7943
  test_add("/queue/add", test_add_to_queue);
 
 
7944
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
 
7945
  test_add("/queue/has_question/empty",
 
 
7946
           test_queue_has_question_empty);
 
 
7947
  test_add("/queue/has_question/false",
 
 
7948
           test_queue_has_question_false);
 
 
7949
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
 
7950
  test_add("/queue/has_question/false2",
 
 
7951
           test_queue_has_question_false2);
 
 
7952
  test_add("/queue/has_question/true2",
 
 
7953
           test_queue_has_question_true2);
 
 
7954
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
 
7955
  test_add("/string_set/net-set-contains-nothing",
 
 
7956
           test_string_set_new_set_contains_nothing);
 
 
7957
  test_add("/string_set/with-added-string-contains-it",
 
 
7958
           test_string_set_with_added_string_contains_it);
 
 
7959
  test_add("/string_set/cleared-does-not-contain-string",
 
 
7960
           test_string_set_cleared_does_not_contain_str);
 
 
7961
  test_add("/string_set/swap/one-with-empty",
 
 
7962
           test_string_set_swap_one_with_empty);
 
 
7963
  test_add("/string_set/swap/empty-with-one",
 
 
7964
           test_string_set_swap_empty_with_one);
 
 
7965
  test_add("/string_set/swap/one-with-one",
 
 
7966
           test_string_set_swap_one_with_one);
 
 
7968
  /* A macro to add a test using the setup and teardown functions */
 
 
7969
#define test_add_st(path, func)                                 \
 
 
7971
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
 
7975
  /* Signal-related tests; these use setups and teardowns which
 
 
7976
     establish, during each test run, a signal handler for, and a
 
 
7977
     signal mask blocking, the SIGCHLD signal, just like main() */
 
 
7978
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
 
7979
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
 
7980
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
 
7981
  test_add_st("/run_queue/zeroes-next-run",
 
 
7982
              test_run_queue_zeroes_next_run);
 
 
7983
  test_add_st("/run_queue/clears-cancelled_filenames",
 
 
7984
              test_run_queue_clears_cancelled_filenames);
 
 
7985
  test_add_st("/run_queue/skips-cancelled-filenames",
 
 
7986
              test_run_queue_skips_cancelled_filenames);
 
 
7987
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
 
7988
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
 
7989
  test_add_st("/run_queue/two-tasks/quit",
 
 
7990
              test_run_queue_two_tasks_quit);
 
 
7991
  test_add_st("/run_queue/two-tasks-cleanup",
 
 
7992
              test_run_queue_two_tasks_cleanup);
 
 
7993
  test_add_st("/task-creators/start_mandos_client",
 
 
7994
              test_start_mandos_client);
 
 
7995
  test_add_st("/task-creators/start_mandos_client/execv",
 
 
7996
              test_start_mandos_client_execv);
 
 
7997
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
 
7998
              test_start_mandos_client_suid_euid);
 
 
7999
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
 
8000
              test_start_mandos_client_suid_egid);
 
 
8001
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
 
8002
              test_start_mandos_client_suid_ruid);
 
 
8003
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
 
8004
              test_start_mandos_client_suid_rgid);
 
 
8005
  test_add_st("/task-creators/start_mandos_client/read",
 
 
8006
              test_start_mandos_client_read);
 
 
8007
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
 
8008
              test_start_mandos_client_helper_directory);
 
 
8009
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
 
8010
              test_start_mandos_client_sigmask);
 
 
8011
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
 
8012
              test_wait_for_mandos_client_exit_badpid);
 
 
8013
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
 
8014
              test_wait_for_mandos_client_exit_noexit);
 
 
8015
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
 
8016
              test_wait_for_mandos_client_exit_success);
 
 
8017
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
 
8018
              test_wait_for_mandos_client_exit_failure);
 
 
8019
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
 
8020
              test_wait_for_mandos_client_exit_killed);
 
 
8021
  test_add_st("/task/read_mandos_client_output/readerror",
 
 
8022
              test_read_mandos_client_output_readerror);
 
 
8023
  test_add_st("/task/read_mandos_client_output/nodata",
 
 
8024
              test_read_mandos_client_output_nodata);
 
 
8025
  test_add_st("/task/read_mandos_client_output/eof",
 
 
8026
              test_read_mandos_client_output_eof);
 
 
8027
  test_add_st("/task/read_mandos_client_output/once",
 
 
8028
              test_read_mandos_client_output_once);
 
 
8029
  test_add_st("/task/read_mandos_client_output/malloc",
 
 
8030
              test_read_mandos_client_output_malloc);
 
 
8031
  test_add_st("/task/read_mandos_client_output/append",
 
 
8032
              test_read_mandos_client_output_append);
 
 
8033
  test_add_st("/task-creators/add_inotify_dir_watch",
 
 
8034
              test_add_inotify_dir_watch);
 
 
8035
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
 
8036
              test_add_inotify_dir_watch_fail);
 
 
8037
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
 
8038
              test_add_inotify_dir_watch_nondir);
 
 
8039
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
 
8040
              test_add_inotify_dir_watch_EAGAIN);
 
 
8041
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
 
8042
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
 
8043
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
 
8044
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
 
8045
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
 
8046
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
 
8047
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
 
8048
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
 
8049
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
 
8050
              test_add_inotify_dir_watch_IN_DELETE);
 
 
8051
  test_add_st("/task/read_inotify_event/readerror",
 
 
8052
              test_read_inotify_event_readerror);
 
 
8053
  test_add_st("/task/read_inotify_event/bad-epoll",
 
 
8054
              test_read_inotify_event_bad_epoll);
 
 
8055
  test_add_st("/task/read_inotify_event/nodata",
 
 
8056
              test_read_inotify_event_nodata);
 
 
8057
  test_add_st("/task/read_inotify_event/eof",
 
 
8058
              test_read_inotify_event_eof);
 
 
8059
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
 
8060
              test_read_inotify_event_IN_CLOSE_WRITE);
 
 
8061
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
 
8062
              test_read_inotify_event_IN_MOVED_TO);
 
 
8063
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
 
8064
              test_read_inotify_event_IN_MOVED_FROM);
 
 
8065
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
 
8066
              test_read_inotify_event_IN_DELETE);
 
 
8067
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
 
8068
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
 
8069
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
 
8070
              test_read_inotify_event_IN_MOVED_TO_badname);
 
 
8071
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
 
8072
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
 
8073
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
 
8074
              test_read_inotify_event_IN_DELETE_badname);
 
 
8075
  test_add_st("/task/open_and_parse_question/ENOENT",
 
 
8076
              test_open_and_parse_question_ENOENT);
 
 
8077
  test_add_st("/task/open_and_parse_question/EIO",
 
 
8078
              test_open_and_parse_question_EIO);
 
 
8079
  test_add_st("/task/open_and_parse_question/parse-error",
 
 
8080
              test_open_and_parse_question_parse_error);
 
 
8081
  test_add_st("/task/open_and_parse_question/nosocket",
 
 
8082
              test_open_and_parse_question_nosocket);
 
 
8083
  test_add_st("/task/open_and_parse_question/badsocket",
 
 
8084
              test_open_and_parse_question_badsocket);
 
 
8085
  test_add_st("/task/open_and_parse_question/nopid",
 
 
8086
              test_open_and_parse_question_nopid);
 
 
8087
  test_add_st("/task/open_and_parse_question/badpid",
 
 
8088
              test_open_and_parse_question_badpid);
 
 
8089
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
 
8090
              test_open_and_parse_question_noexist_pid);
 
 
8091
  test_add_st("/task/open_and_parse_question/no-notafter",
 
 
8092
              test_open_and_parse_question_no_notafter);
 
 
8093
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
 
8094
              test_open_and_parse_question_bad_notafter);
 
 
8095
  test_add_st("/task/open_and_parse_question/notafter-0",
 
 
8096
              test_open_and_parse_question_notafter_0);
 
 
8097
  test_add_st("/task/open_and_parse_question/notafter-1",
 
 
8098
              test_open_and_parse_question_notafter_1);
 
 
8099
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
 
8100
              test_open_and_parse_question_notafter_1_1);
 
 
8101
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
 
8102
              test_open_and_parse_question_notafter_1_2);
 
 
8103
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
 
8104
              test_open_and_parse_question_equal_notafter);
 
 
8105
  test_add_st("/task/open_and_parse_question/late-notafter",
 
 
8106
              test_open_and_parse_question_late_notafter);
 
 
8107
  test_add_st("/task/cancel_old_question/0-1-2",
 
 
8108
              test_cancel_old_question_0_1_2);
 
 
8109
  test_add_st("/task/cancel_old_question/0-2-1",
 
 
8110
              test_cancel_old_question_0_2_1);
 
 
8111
  test_add_st("/task/cancel_old_question/1-2-3",
 
 
8112
              test_cancel_old_question_1_2_3);
 
 
8113
  test_add_st("/task/cancel_old_question/1-3-2",
 
 
8114
              test_cancel_old_question_1_3_2);
 
 
8115
  test_add_st("/task/cancel_old_question/2-1-3",
 
 
8116
              test_cancel_old_question_2_1_3);
 
 
8117
  test_add_st("/task/cancel_old_question/2-3-1",
 
 
8118
              test_cancel_old_question_2_3_1);
 
 
8119
  test_add_st("/task/cancel_old_question/3-1-2",
 
 
8120
              test_cancel_old_question_3_1_2);
 
 
8121
  test_add_st("/task/cancel_old_question/3-2-1",
 
 
8122
              test_cancel_old_question_3_2_1);
 
 
8123
  test_add_st("/task/connect_question_socket/name-too-long",
 
 
8124
              test_connect_question_socket_name_too_long);
 
 
8125
  test_add_st("/task/connect_question_socket/connect-fail",
 
 
8126
              test_connect_question_socket_connect_fail);
 
 
8127
  test_add_st("/task/connect_question_socket/bad-epoll",
 
 
8128
              test_connect_question_socket_bad_epoll);
 
 
8129
  test_add_st("/task/connect_question_socket/usable",
 
 
8130
              test_connect_question_socket_usable);
 
 
8131
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
 
8132
              test_send_password_to_socket_client_not_exited);
 
 
8133
  test_add_st("/task/send_password_to_socket/password-not-read",
 
 
8134
              test_send_password_to_socket_password_not_read);
 
 
8135
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
 
8136
              test_send_password_to_socket_EMSGSIZE);
 
 
8137
  test_add_st("/task/send_password_to_socket/retry",
 
 
8138
              test_send_password_to_socket_retry);
 
 
8139
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
 
8140
              test_send_password_to_socket_bad_epoll);
 
 
8141
  test_add_st("/task/send_password_to_socket/null-password",
 
 
8142
              test_send_password_to_socket_null_password);
 
 
8143
  test_add_st("/task/send_password_to_socket/empty-password",
 
 
8144
              test_send_password_to_socket_empty_password);
 
 
8145
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
 
8146
              test_send_password_to_socket_empty_str_pass);
 
 
8147
  test_add_st("/task/send_password_to_socket/text-password",
 
 
8148
              test_send_password_to_socket_text_password);
 
 
8149
  test_add_st("/task/send_password_to_socket/binary-password",
 
 
8150
              test_send_password_to_socket_binary_password);
 
 
8151
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
 
8152
              test_send_password_to_socket_nuls_in_password);
 
 
8153
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
 
8154
              test_add_existing_questions_ENOENT);
 
 
8155
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
 
8156
              test_add_existing_questions_no_questions);
 
 
8157
  test_add_st("/task-creators/add_existing_questions/one-question",
 
 
8158
              test_add_existing_questions_one_question);
 
 
8159
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
 
8160
              test_add_existing_questions_two_questions);
 
 
8161
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
 
8162
              test_add_existing_questions_non_questions);
 
 
8163
  test_add_st("/task-creators/add_existing_questions/both-types",
 
 
8164
              test_add_existing_questions_both_types);
 
 
8166
  return g_test_run() == 0;
 
 
8169
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
 
8170
  GOptionContext *context = g_option_context_new("");
 
 
8172
  g_option_context_set_help_enabled(context, FALSE);
 
 
8173
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
 
8175
  gboolean should_run_tests = FALSE;
 
 
8176
  GOptionEntry entries[] = {
 
 
8177
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
 
8178
      &should_run_tests, "Run tests", NULL },
 
 
8181
  g_option_context_add_main_entries(context, entries, NULL);
 
 
8183
  GError *error = NULL;
 
 
8185
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
 
8186
    g_option_context_free(context);
 
 
8187
    g_error("Failed to parse options: %s", error->message);
 
 
8190
  g_option_context_free(context);
 
 
8191
  return should_run_tests != FALSE;