/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to dracut-module/password-agent.c

  • Committer: Teddy Hogeborn
  • Date: 2015-06-28 16:35:27 UTC
  • mto: This revision was merged to the branch mainline in revision 759.
  • Revision ID: teddy@recompile.se-20150628163527-cky0ec59zew7teua
Add a plugin helper directory, available to all plugins.

* Makefile (PLUGIN_HELPERS): New; list of plugin helpers.
  (CPROGS): Appended "$(PLUGIN_HELPERS)".
* initramfs-tools-hook: Create new plugin helper directory, and copy
                        plugin helpers provided by the system and/or
                        by the local administrator.
  (PLUGINHELPERDIR): New.
* plugin-runner.c: Take new --plugin-helper-dir option and provide
                   environment variable to all plugins.
  (PHDIR): New; set to "/lib/mandos/plugin-helpers".
  (main/pluginhelperdir): New.
  (main/options): New option "--plugin-helper-dir".
  (main/parse_opt, main/parse_opt_config_file): Accept new option.
  (main): Use new option to set MANDOSPLUGINHELPERDIR environment
          variable as if using --global-env MANDOSPLUGINHELPERDIR=...
* plugin-runner.xml: Document new --plugin-helper-dir option.
  (SYNOPSIS, OPTIONS): Add "--plugin-helper-dir" option.
  (PLUGINS/WRITING PLUGINS): Document new environment variable
                             available to plugins.
  (ENVIRONMENT): Document new environment variable
                 "MANDOSPLUGINHELPERDIR" affected by the new
                 --plugin-helper-dir option.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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)))))); -*- */
2
 
/*
3
 
 * Mandos password agent - Simple password agent to run Mandos client
4
 
 *
5
 
 * Copyright © 2019 Teddy Hogeborn
6
 
 * Copyright © 2019 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
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.
14
 
 * 
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.
19
 
 * 
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/>.
22
 
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
28
 
                                   intmax_t, uint32_t, SCNx32,
29
 
                                   SCNuMAX, SCNxMAX */
30
 
#include <stddef.h>             /* size_t */
31
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
32
 
                                   getpid() */
33
 
#include <stdbool.h>            /* bool, true, false */
34
 
#include <signal.h>             /* struct sigaction, sigset_t,
35
 
                                   sigemptyset(), sigaddset(),
36
 
                                   SIGCHLD, pthread_sigmask(),
37
 
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
 
                                   SA_NOCLDSTOP, sigfillset(), kill(),
39
 
                                   SIGTERM, sigdelset(), SIGKILL,
40
 
                                   NSIG, sigismember(), SA_ONSTACK,
41
 
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
 
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
44
 
                                   malloc(), free(), strtoumax(),
45
 
                                   realloc(), setenv(), calloc(),
46
 
                                   mkdtemp(), mkostemp() */
47
 
#include <iso646.h>             /* not, or, and, xor */
48
 
#include <error.h>              /* error() */
49
 
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
 
#include <errno.h>              /* errno, error_t, EACCES,
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
54
 
                                   ECONNREFUSED, ECONNRESET,
55
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
 
                                   EINVAL */
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(),
63
 
                                   argz_add() */
64
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
65
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
66
 
                                   struct epoll_event, EPOLLIN,
67
 
                                   EPOLLRDHUP, EPOLLOUT,
68
 
                                   epoll_pwait() */
69
 
#include <time.h>               /* struct timespec, clock_gettime(),
70
 
                                   CLOCK_MONOTONIC */
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(),
75
 
                                   ARGP_NO_EXIT */
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
 
                                   fork(), _exit(), dup2(),
78
 
                                   STDOUT_FILENO, setresgid(),
79
 
                                   setresuid(), execv(), ssize_t,
80
 
                                   read(), dup3(), getuid(), dup(),
81
 
                                   STDERR_FILENO, pause(), write(),
82
 
                                   rmdir(), unlink(), getpid() */
83
 
#include <sys/mman.h>           /* munlock(), mlock() */
84
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_NOCTTY,
87
 
                                   O_RDONLY */
88
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
89
 
                                   WEXITSTATUS() */
90
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
91
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
92
 
                                   IN_CLOEXEC, inotify_add_watch(),
93
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
94
 
                                   IN_DELETE, struct inotify_event */
95
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
96
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
97
 
                                   getline(), sscanf(), feof(),
98
 
                                   ferror(), fclose(), stderr,
99
 
                                   rename(), fdopen(), fprintf(),
100
 
                                   fscanf() */
101
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
102
 
                        GError, g_key_file_load_from_file(),
103
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
104
 
                        g_key_file_get_string(), guint64,
105
 
                        g_key_file_get_uint64(),
106
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
107
 
                        g_assert_true(), g_assert_nonnull(),
108
 
                        g_assert_null(), g_assert_false(),
109
 
                        g_assert_cmpint(), g_assert_cmpuint(),
110
 
                        g_test_skip(), g_assert_cmpstr(),
111
 
                        g_test_init(), g_test_add(), g_test_run(),
112
 
                        GOptionContext, g_option_context_new(),
113
 
                        g_option_context_set_help_enabled(), FALSE,
114
 
                        g_option_context_set_ignore_unknown_options(),
115
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
116
 
                        g_option_context_add_main_entries(),
117
 
                        g_option_context_parse(),
118
 
                        g_option_context_free(), g_error() */
119
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
120
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
121
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
122
 
                                   SOCK_CLOEXEC, connect(),
123
 
                                   struct sockaddr, socklen_t,
124
 
                                   shutdown(), SHUT_RD, send(),
125
 
                                   MSG_NOSIGNAL, bind(), recv(),
126
 
                                   socketpair() */
127
 
#include <glob.h>               /* globfree(), glob_t, glob(),
128
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
129
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
130
 
                                   GLOB_NOSPACE */
131
 
 
132
 
/* End of includes */
133
 
 
134
 
/* Start of declarations of private types and functions */
135
 
 
136
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
137
 
typedef uintmax_t mono_microsecs;
138
 
 
139
 
/* "task_queue" - A queue of tasks to be run */
140
 
typedef struct {
141
 
  struct task_struct *tasks;    /* Tasks in this queue */
142
 
  size_t length;                /* Number of tasks */
143
 
  /* Memory allocated for "tasks", in bytes */
144
 
  size_t allocated;             
145
 
  /* Time when this queue should be run, at the latest */
146
 
  mono_microsecs next_run;
147
 
} __attribute__((designated_init)) task_queue;
148
 
 
149
 
/* "func_type" - A function type for task functions
150
 
 
151
 
   I.e. functions for the code which runs when a task is run, all have
152
 
   this type */
153
 
typedef void (task_func) (const struct task_struct,
154
 
                          task_queue *const)
155
 
  __attribute__((nonnull));
156
 
 
157
 
/* "buffer" - A data buffer for a growing array of bytes
158
 
 
159
 
   Used for the "password" variable */
160
 
typedef struct {
161
 
  char *data;
162
 
  size_t length;
163
 
  size_t allocated;
164
 
} __attribute__((designated_init)) buffer;
165
 
 
166
 
/* "string_set" - A set type which can contain strings
167
 
 
168
 
   Used by the "cancelled_filenames" variable */
169
 
typedef struct {
170
 
  char *argz;                   /* Do not access these except in */
171
 
  size_t argz_len;              /* the string_set_* functions */
172
 
} __attribute__((designated_init)) string_set;
173
 
 
174
 
/* "task_context" - local variables for tasks
175
 
 
176
 
   This data structure distinguishes between different tasks which are
177
 
   using the same function.  This data structure is passed to every
178
 
   task function when each task is run.
179
 
 
180
 
   Note that not every task uses every struct member. */
181
 
typedef struct task_struct {
182
 
  task_func *const func;         /* The function run by this task */
183
 
  char *const question_filename; /* The question file */
184
 
  const pid_t pid;               /* Mandos client process ID */
185
 
  const int epoll_fd;            /* The epoll set file descriptor */
186
 
  bool *const quit_now;          /* Set to true on fatal errors */
187
 
  const int fd;                  /* General purpose file descriptor */
188
 
  bool *const mandos_client_exited; /* Set true when client exits */
189
 
  buffer *const password;           /* As read from client process */
190
 
  bool *const password_is_read;     /* "password" is done growing */
191
 
  char *filename;                   /* General purpose file name */
192
 
  /* A set of strings of all the file names of questions which have
193
 
     been cancelled for any reason; tasks pertaining to these question
194
 
     files should not be run */
195
 
  string_set *const cancelled_filenames;
196
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
197
 
  /* Updated before each queue run; is compared with queue.next_run */
198
 
  const mono_microsecs *const current_time;
199
 
} __attribute__((designated_init)) task_context;
200
 
 
201
 
/* Declare all our functions here so we can define them in any order
202
 
   below.  Note: test functions are *not* declared here, they are
203
 
   declared in the test section. */
204
 
__attribute__((warn_unused_result))
205
 
static bool should_only_run_tests(int *, char **[]);
206
 
__attribute__((warn_unused_result, cold))
207
 
static bool run_tests(int, char *[]);
208
 
static void handle_sigchld(__attribute__((unused)) int sig){}
209
 
__attribute__((warn_unused_result, malloc))
210
 
task_queue *create_queue(void);
211
 
__attribute__((nonnull, warn_unused_result))
212
 
bool add_to_queue(task_queue *const, const task_context);
213
 
__attribute__((nonnull))
214
 
void cleanup_task(const task_context *const);
215
 
__attribute__((nonnull))
216
 
void cleanup_queue(task_queue *const *const);
217
 
__attribute__((pure, nonnull, warn_unused_result))
218
 
bool queue_has_question(const task_queue *const);
219
 
__attribute__((nonnull))
220
 
void cleanup_close(const int *const);
221
 
__attribute__((nonnull))
222
 
void cleanup_string(char *const *const);
223
 
__attribute__((nonnull))
224
 
void cleanup_buffer(buffer *const);
225
 
__attribute__((pure, nonnull, warn_unused_result))
226
 
bool string_set_contains(const string_set, const char *const);
227
 
__attribute__((nonnull, warn_unused_result))
228
 
bool string_set_add(string_set *const, const char *const);
229
 
__attribute__((nonnull))
230
 
void string_set_clear(string_set *);
231
 
void string_set_swap(string_set *const, string_set *const);
232
 
__attribute__((nonnull, warn_unused_result))
233
 
bool start_mandos_client(task_queue *const, const int, bool *const,
234
 
                         bool *const, buffer *const, bool *const,
235
 
                         const struct sigaction *const,
236
 
                         const sigset_t, const char *const,
237
 
                         const uid_t, const gid_t,
238
 
                         const char *const *const);
239
 
__attribute__((nonnull))
240
 
task_func wait_for_mandos_client_exit;
241
 
__attribute__((nonnull))
242
 
task_func read_mandos_client_output;
243
 
__attribute__((warn_unused_result))
244
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
245
 
                           buffer *const, const char *const,
246
 
                           string_set *, const mono_microsecs *const,
247
 
                           bool *const, bool *const);
248
 
__attribute__((nonnull))
249
 
task_func read_inotify_event;
250
 
__attribute__((nonnull))
251
 
task_func open_and_parse_question;
252
 
__attribute__((nonnull))
253
 
task_func cancel_old_question;
254
 
__attribute__((nonnull))
255
 
task_func connect_question_socket;
256
 
__attribute__((nonnull))
257
 
task_func send_password_to_socket;
258
 
__attribute__((warn_unused_result))
259
 
bool add_existing_questions(task_queue *const, const int,
260
 
                            buffer *const, string_set *,
261
 
                            const mono_microsecs *const,
262
 
                            bool *const, bool *const,
263
 
                            const char *const);
264
 
__attribute__((nonnull, warn_unused_result))
265
 
bool wait_for_event(const int, const mono_microsecs,
266
 
                    const mono_microsecs);
267
 
bool run_queue(task_queue **const, string_set *const, bool *const);
268
 
bool clear_all_fds_from_epoll_set(const int);
269
 
mono_microsecs get_current_time(void);
270
 
__attribute__((nonnull, warn_unused_result))
271
 
bool setup_signal_handler(struct sigaction *const);
272
 
__attribute__((nonnull))
273
 
bool restore_signal_handler(const struct sigaction *const);
274
 
__attribute__((nonnull, warn_unused_result))
275
 
bool block_sigchld(sigset_t *const);
276
 
__attribute__((nonnull))
277
 
bool restore_sigmask(const sigset_t *const);
278
 
__attribute__((nonnull))
279
 
bool parse_arguments(int, char *[], const bool, char **, char **,
280
 
                     uid_t *const , gid_t *const, char **, size_t *);
281
 
 
282
 
/* End of declarations of private types and functions */
283
 
 
284
 
/* Start of "main" section; this section LACKS TESTS!
285
 
 
286
 
   Code here should be as simple as possible. */
287
 
 
288
 
/* These are required to be global by Argp */
289
 
const char *argp_program_version = "password-agent " VERSION;
290
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
291
 
 
292
 
int main(int argc, char *argv[]){
293
 
 
294
 
  /* If the --test option is passed, skip all normal operations and
295
 
     instead only run the run_tests() function, which also does all
296
 
     its own option parsing, so we don't have to do anything here. */
297
 
  if(should_only_run_tests(&argc, &argv)){
298
 
    if(run_tests(argc, argv)){
299
 
      return EXIT_SUCCESS;      /* All tests successful */
300
 
    }
301
 
    return EXIT_FAILURE;        /* Some test(s) failed */
302
 
  }
303
 
 
304
 
  __attribute__((cleanup(cleanup_string)))
305
 
    char *agent_directory = NULL;
306
 
 
307
 
  __attribute__((cleanup(cleanup_string)))
308
 
    char *helper_directory = NULL;
309
 
 
310
 
  uid_t user = 0;
311
 
  gid_t group = 0;
312
 
 
313
 
  __attribute__((cleanup(cleanup_string)))
314
 
    char *mandos_argz = NULL;
315
 
  size_t mandos_argz_length = 0;
316
 
 
317
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
318
 
                         &helper_directory, &user, &group,
319
 
                         &mandos_argz, &mandos_argz_length)){
320
 
    /* This should never happen, since "true" is passed as the third
321
 
       argument to parse_arguments() above, which should make
322
 
       argp_parse() call exit() if any parsing error occurs. */
323
 
    error(EX_USAGE, errno, "Failed to parse arguments");
324
 
  }
325
 
 
326
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
327
 
  const char default_helper_directory[]
328
 
    = "/lib/mandos/plugin-helpers";
329
 
  const char *const default_argv[]
330
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
331
 
 
332
 
  /* Set variables to default values if unset */
333
 
  if(agent_directory == NULL){
334
 
    agent_directory = strdup(default_agent_directory);
335
 
    if(agent_directory == NULL){
336
 
      error(EX_OSERR, errno, "Failed strdup()");
337
 
    }
338
 
  }
339
 
  if(helper_directory == NULL){
340
 
    helper_directory = strdup(default_helper_directory);
341
 
    if(helper_directory == NULL){
342
 
      error(EX_OSERR, errno, "Failed strdup()");
343
 
    }
344
 
  }
345
 
  if(user == 0){
346
 
    user = 65534;               /* nobody */
347
 
  }
348
 
  if(group == 0){
349
 
    group = 65534;              /* nogroup */
350
 
  }
351
 
  /* If parse_opt did not create an argz vector, create one with
352
 
     default values */
353
 
  if(mandos_argz == NULL){
354
 
#ifdef __GNUC__
355
 
#pragma GCC diagnostic push
356
 
    /* argz_create() takes a non-const argv for some unknown reason -
357
 
       argz_create() isn't modifying the strings, just copying them.
358
 
       Therefore, this cast to non-const should be safe. */
359
 
#pragma GCC diagnostic ignored "-Wcast-qual"
360
 
#endif
361
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
362
 
                        &mandos_argz_length);
363
 
#ifdef __GNUC__
364
 
#pragma GCC diagnostic pop
365
 
#endif
366
 
    if(errno != 0){
367
 
      error(EX_OSERR, errno, "Failed argz_create()");
368
 
    }
369
 
  }
370
 
  /* Use argz vector to create a normal argv, usable by execv() */
371
 
 
372
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
373
 
                                          mandos_argz_length)
374
 
                               + 1) * sizeof(char *));
375
 
  if(mandos_argv == NULL){
376
 
    error_t saved_errno = errno;
377
 
    free(mandos_argz);
378
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
379
 
  }
380
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
381
 
 
382
 
  sigset_t orig_sigmask;
383
 
  if(not block_sigchld(&orig_sigmask)){
384
 
    return EX_OSERR;
385
 
  }
386
 
 
387
 
  struct sigaction old_sigchld_action;
388
 
  if(not setup_signal_handler(&old_sigchld_action)){
389
 
    return EX_OSERR;
390
 
  }
391
 
 
392
 
  mono_microsecs current_time = 0;
393
 
 
394
 
  bool mandos_client_exited = false;
395
 
  bool quit_now = false;
396
 
  __attribute__((cleanup(cleanup_close)))
397
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
398
 
  if(epoll_fd < 0){
399
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
400
 
  }
401
 
  __attribute__((cleanup(cleanup_queue)))
402
 
    task_queue *queue = create_queue();
403
 
  if(queue == NULL){
404
 
    error(EX_OSERR, errno, "Failed to create task queue");
405
 
  }
406
 
 
407
 
  __attribute__((cleanup(cleanup_buffer)))
408
 
    buffer password = {};
409
 
  bool password_is_read = false;
410
 
 
411
 
  __attribute__((cleanup(string_set_clear)))
412
 
    string_set cancelled_filenames = {};
413
 
 
414
 
  /* Add tasks to queue */
415
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
416
 
                             &quit_now, &password, &password_is_read,
417
 
                             &old_sigchld_action, orig_sigmask,
418
 
                             helper_directory, user, group,
419
 
                             (const char *const *)mandos_argv)){
420
 
    return EX_OSERR;            /* Error has already been printed */
421
 
  }
422
 
  /* These variables were only for start_mandos_client() and are not
423
 
     needed anymore */
424
 
  free(mandos_argv);
425
 
  free(mandos_argz);
426
 
  mandos_argz = NULL;
427
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
428
 
                               agent_directory, &cancelled_filenames,
429
 
                               &current_time, &mandos_client_exited,
430
 
                               &password_is_read)){
431
 
    switch(errno){              /* Error has already been printed */
432
 
    case EACCES:
433
 
    case ENAMETOOLONG:
434
 
    case ENOENT:
435
 
    case ENOTDIR:
436
 
      return EX_OSFILE;
437
 
    default:
438
 
      return EX_OSERR;
439
 
    }
440
 
  }
441
 
  if(not add_existing_questions(queue, epoll_fd, &password,
442
 
                                &cancelled_filenames, &current_time,
443
 
                                &mandos_client_exited,
444
 
                                &password_is_read, agent_directory)){
445
 
    return EXIT_FAILURE;        /* Error has already been printed */
446
 
  }
447
 
 
448
 
  /* Run queue */
449
 
  do {
450
 
    current_time = get_current_time();
451
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
452
 
      const error_t saved_errno = errno;
453
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
454
 
            " events");
455
 
    }
456
 
 
457
 
    current_time = get_current_time();
458
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
459
 
      const error_t saved_errno = errno;
460
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
461
 
    }
462
 
 
463
 
    /*  When no tasks about questions are left in the queue, break out
464
 
        of the loop (and implicitly exit the program) */
465
 
  } while(queue_has_question(queue));
466
 
 
467
 
  restore_signal_handler(&old_sigchld_action);
468
 
  restore_sigmask(&orig_sigmask);
469
 
 
470
 
  return EXIT_SUCCESS;
471
 
}
472
 
 
473
 
__attribute__((warn_unused_result))
474
 
mono_microsecs get_current_time(void){
475
 
  struct timespec currtime;
476
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
477
 
    error(0, errno, "Failed to get current time");
478
 
    return 0;
479
 
  }
480
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
481
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
482
 
}
483
 
 
484
 
/* End of "main" section */
485
 
 
486
 
/* Start of regular code section; ALL this code has tests */
487
 
 
488
 
__attribute__((nonnull))
489
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
490
 
                     char **agent_directory, char **helper_directory,
491
 
                     uid_t *const user, gid_t *const group,
492
 
                     char **mandos_argz, size_t *mandos_argz_length){
493
 
 
494
 
  const struct argp_option options[] = {
495
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
496
 
      .doc="Systemd password agent directory" },
497
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
498
 
      .doc="Mandos Client password helper directory" },
499
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
500
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
501
 
    { .name="user", .key='u', .arg="USERID",
502
 
      .doc="User ID the Mandos Client will use as its unprivileged"
503
 
      " user" },
504
 
    { .name="userid", .key=130, /* From plugin--runner */
505
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
506
 
    { .name="group", .key='g', .arg="GROUPID",
507
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
508
 
      " group" },
509
 
    { .name="groupid", .key=131, /* From plugin--runner */
510
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
511
 
    { .name="test", .key=255, /* See should_only_run_tests() */
512
 
      .doc="Skip normal operation, and only run self-tests.  See"
513
 
      " --test --help.", .group=10, },
514
 
    { NULL },
515
 
  };
516
 
 
517
 
  __attribute__((nonnull(3)))
518
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
519
 
    errno = 0;
520
 
    switch(key){
521
 
    case 'd':                   /* --agent-directory */
522
 
      *agent_directory = strdup(arg);
523
 
      break;
524
 
    case 128:                   /* --helper-directory */
525
 
    case 129:                   /* --plugin-helper-dir */
526
 
      *helper_directory = strdup(arg);
527
 
      break;
528
 
    case 'u':                   /* --user */
529
 
    case 130:                   /* --userid */
530
 
      {
531
 
        char *tmp;
532
 
        uintmax_t tmp_id = 0;
533
 
        errno = 0;
534
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
535
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
536
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
537
 
          return ARGP_ERR_UNKNOWN;
538
 
        }
539
 
        *user = (uid_t)tmp_id;
540
 
        errno = 0;
541
 
        break;
542
 
      }
543
 
    case 'g':                   /* --group */
544
 
    case 131:                   /* --groupid */
545
 
      {
546
 
        char *tmp;
547
 
        uintmax_t tmp_id = 0;
548
 
        errno = 0;
549
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
550
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
551
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
552
 
          return ARGP_ERR_UNKNOWN;
553
 
        }
554
 
        *group = (gid_t)tmp_id;
555
 
        errno = 0;
556
 
        break;
557
 
      }
558
 
    case ARGP_KEY_ARGS:
559
 
      /* Copy arguments into argz vector */
560
 
      return argz_create(state->argv + state->next, mandos_argz,
561
 
                         mandos_argz_length);
562
 
    default:
563
 
      return ARGP_ERR_UNKNOWN;
564
 
    }
565
 
    return errno;
566
 
  }
567
 
 
568
 
  const struct argp argp = {
569
 
    .options=options,
570
 
    .parser=parse_opt,
571
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
572
 
    .doc = "Mandos password agent -- runs Mandos client as a"
573
 
    " systemd password agent",
574
 
  };
575
 
 
576
 
  errno = argp_parse(&argp, argc, argv,
577
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
578
 
 
579
 
  return errno == 0;
580
 
}
581
 
 
582
 
__attribute__((nonnull, warn_unused_result))
583
 
bool block_sigchld(sigset_t *const orig_sigmask){
584
 
  sigset_t sigchld_sigmask;
585
 
  if(sigemptyset(&sigchld_sigmask) < 0){
586
 
    error(0, errno, "Failed to empty signal set");
587
 
    return false;
588
 
  }
589
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
590
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
591
 
    return false;
592
 
  }
593
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
594
 
    error(0, errno, "Failed to block SIGCHLD signal");
595
 
    return false;
596
 
  }
597
 
  return true;
598
 
}
599
 
 
600
 
__attribute__((nonnull, warn_unused_result, const))
601
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
602
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
603
 
    error(0, errno, "Failed to restore blocked signals");
604
 
    return false;
605
 
  }
606
 
  return true;
607
 
}
608
 
 
609
 
__attribute__((nonnull, warn_unused_result))
610
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
611
 
  struct sigaction sigchld_action = {
612
 
    .sa_handler=handle_sigchld,
613
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
614
 
  };
615
 
  /* Set all signals in "sa_mask" struct member; this makes all
616
 
     signals automatically blocked during signal handler */
617
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
618
 
    error(0, errno, "Failed to do sigfillset()");
619
 
    return false;
620
 
  }
621
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
622
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
623
 
    return false;
624
 
  }
625
 
  return true;
626
 
}
627
 
 
628
 
__attribute__((nonnull, warn_unused_result))
629
 
bool restore_signal_handler(const struct sigaction *const
630
 
                            old_sigchld_action){
631
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
632
 
    error(0, errno, "Failed to restore signal handler");
633
 
    return false;
634
 
  }
635
 
  return true;
636
 
}
637
 
 
638
 
__attribute__((warn_unused_result, malloc))
639
 
task_queue *create_queue(void){
640
 
  task_queue *queue = malloc(sizeof(task_queue));
641
 
  if(queue){
642
 
    queue->tasks = NULL;
643
 
    queue->length = 0;
644
 
    queue->allocated = 0;
645
 
    queue->next_run = 0;
646
 
  }
647
 
  return queue;
648
 
}
649
 
 
650
 
__attribute__((nonnull, warn_unused_result))
651
 
bool add_to_queue(task_queue *const queue, const task_context task){
652
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
653
 
  if(needed_size > (queue->allocated)){
654
 
    task_context *const new_tasks = realloc(queue->tasks,
655
 
                                            needed_size);
656
 
    if(new_tasks == NULL){
657
 
      error(0, errno, "Failed to allocate %" PRIuMAX
658
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
659
 
      return false;
660
 
    }
661
 
    queue->tasks = new_tasks;
662
 
    queue->allocated = needed_size;
663
 
  }
664
 
  /* Using memcpy here is necessary because doing */
665
 
  /* queue->tasks[queue->length++] = task; */
666
 
  /* would violate const-ness of task members */
667
 
  memcpy(&(queue->tasks[queue->length++]), &task,
668
 
         sizeof(task_context));
669
 
  return true;
670
 
}
671
 
 
672
 
__attribute__((nonnull))
673
 
void cleanup_task(const task_context *const task){
674
 
  const error_t saved_errno = errno;
675
 
  /* free and close all task data */
676
 
  free(task->question_filename);
677
 
  if(task->filename != task->question_filename){
678
 
    free(task->filename);
679
 
  }
680
 
  if(task->pid > 0){
681
 
    kill(task->pid, SIGTERM);
682
 
  }
683
 
  if(task->fd > 0){
684
 
    close(task->fd);
685
 
  }
686
 
  errno = saved_errno;
687
 
}
688
 
 
689
 
__attribute__((nonnull))
690
 
void free_queue(task_queue *const queue){
691
 
  free(queue->tasks);
692
 
  free(queue);
693
 
}
694
 
 
695
 
__attribute__((nonnull))
696
 
void cleanup_queue(task_queue *const *const queue){
697
 
  if(*queue == NULL){
698
 
    return;
699
 
  }
700
 
  for(size_t i = 0; i < (*queue)->length; i++){
701
 
    const task_context *const task = ((*queue)->tasks)+i;
702
 
    cleanup_task(task);
703
 
  }
704
 
  free_queue(*queue);
705
 
}
706
 
 
707
 
__attribute__((pure, nonnull, warn_unused_result))
708
 
bool queue_has_question(const task_queue *const queue){
709
 
  for(size_t i=0; i < queue->length; i++){
710
 
    if(queue->tasks[i].question_filename != NULL){
711
 
      return true;
712
 
    }
713
 
  }
714
 
  return false;
715
 
}
716
 
 
717
 
__attribute__((nonnull))
718
 
void cleanup_close(const int *const fd){
719
 
  const error_t saved_errno = errno;
720
 
  close(*fd);
721
 
  errno = saved_errno;
722
 
}
723
 
 
724
 
__attribute__((nonnull))
725
 
void cleanup_string(char *const *const ptr){
726
 
  free(*ptr);
727
 
}
728
 
 
729
 
__attribute__((nonnull))
730
 
void cleanup_buffer(buffer *buf){
731
 
  if(buf->allocated > 0){
732
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
733
 
    explicit_bzero(buf->data, buf->allocated);
734
 
#else
735
 
    memset(buf->data, '\0', buf->allocated);
736
 
#endif
737
 
  }
738
 
  if(buf->data != NULL){
739
 
    if(munlock(buf->data, buf->allocated) != 0){
740
 
      error(0, errno, "Failed to unlock memory of old buffer");
741
 
    }
742
 
    free(buf->data);
743
 
    buf->data = NULL;
744
 
  }
745
 
  buf->length = 0;
746
 
  buf->allocated = 0;
747
 
}
748
 
 
749
 
__attribute__((pure, nonnull, warn_unused_result))
750
 
bool string_set_contains(const string_set set, const char *const str){
751
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
752
 
      s = argz_next(set.argz, set.argz_len, s)){
753
 
    if(strcmp(s, str) == 0){
754
 
      return true;
755
 
    }
756
 
  }
757
 
  return false;
758
 
}
759
 
 
760
 
__attribute__((nonnull, warn_unused_result))
761
 
bool string_set_add(string_set *const set, const char *const str){
762
 
  if(string_set_contains(*set, str)){
763
 
    return true;
764
 
  }
765
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
766
 
  if(error == 0){
767
 
    return true;
768
 
  }
769
 
  errno = error;
770
 
  return false;
771
 
}
772
 
 
773
 
__attribute__((nonnull))
774
 
void string_set_clear(string_set *set){
775
 
  free(set->argz);
776
 
  set->argz = NULL;
777
 
  set->argz_len = 0;
778
 
}
779
 
 
780
 
__attribute__((nonnull))
781
 
void string_set_swap(string_set *const set1, string_set *const set2){
782
 
  /* Swap contents of two string sets */
783
 
  {
784
 
    char *const tmp_argz = set1->argz;
785
 
    set1->argz = set2->argz;
786
 
    set2->argz = tmp_argz;
787
 
  }
788
 
  {
789
 
    const size_t tmp_argz_len = set1->argz_len;
790
 
    set1->argz_len = set2->argz_len;
791
 
    set2->argz_len = tmp_argz_len;
792
 
  }
793
 
}
794
 
 
795
 
__attribute__((nonnull, warn_unused_result))
796
 
bool start_mandos_client(task_queue *const queue,
797
 
                         const int epoll_fd,
798
 
                         bool *const mandos_client_exited,
799
 
                         bool *const quit_now, buffer *const password,
800
 
                         bool *const password_is_read,
801
 
                         const struct sigaction *const
802
 
                         old_sigchld_action, const sigset_t sigmask,
803
 
                         const char *const helper_directory,
804
 
                         const uid_t user, const gid_t group,
805
 
                         const char *const *const argv){
806
 
  int pipefds[2];
807
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
808
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
809
 
    return false;
810
 
  }
811
 
 
812
 
  const pid_t pid = fork();
813
 
  if(pid == 0){
814
 
    if(not restore_signal_handler(old_sigchld_action)){
815
 
      _exit(EXIT_FAILURE);
816
 
    }
817
 
    if(not restore_sigmask(&sigmask)){
818
 
      _exit(EXIT_FAILURE);
819
 
    }
820
 
    if(close(pipefds[0]) != 0){
821
 
      error(0, errno, "Failed to close() parent pipe fd");
822
 
      _exit(EXIT_FAILURE);
823
 
    }
824
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
825
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
826
 
      _exit(EXIT_FAILURE);
827
 
    }
828
 
    if(close(pipefds[1]) != 0){
829
 
      error(0, errno, "Failed to close() old child pipe fd");
830
 
      _exit(EXIT_FAILURE);
831
 
    }
832
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
833
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
834
 
            " \"%s\", 1)", helper_directory);
835
 
      _exit(EXIT_FAILURE);
836
 
    }
837
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
838
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
839
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
840
 
      _exit(EXIT_FAILURE);
841
 
    }
842
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
843
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
844
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
845
 
      _exit(EXIT_FAILURE);
846
 
    }
847
 
#ifdef __GNUC__
848
 
#pragma GCC diagnostic push
849
 
    /* For historical reasons, the "argv" argument to execv() is not
850
 
       const, but it is safe to override this. */
851
 
#pragma GCC diagnostic ignored "-Wcast-qual"
852
 
#endif
853
 
    execv(argv[0], (char **)argv);
854
 
#ifdef __GNUC__
855
 
#pragma GCC diagnostic pop
856
 
#endif
857
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
858
 
    _exit(EXIT_FAILURE);
859
 
  }
860
 
  close(pipefds[1]);
861
 
 
862
 
  if(not add_to_queue(queue, (task_context){
863
 
        .func=wait_for_mandos_client_exit,
864
 
        .pid=pid,
865
 
        .mandos_client_exited=mandos_client_exited,
866
 
        .quit_now=quit_now,
867
 
      })){
868
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
869
 
    close(pipefds[0]);
870
 
    return false;
871
 
  }
872
 
 
873
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
874
 
                            &(struct epoll_event)
875
 
                            { .events=EPOLLIN | EPOLLRDHUP });
876
 
  if(ret != 0 and errno != EEXIST){
877
 
    error(0, errno, "Failed to add file descriptor to epoll set");
878
 
    close(pipefds[0]);
879
 
    return false;
880
 
  }
881
 
 
882
 
  return add_to_queue(queue, (task_context){
883
 
      .func=read_mandos_client_output,
884
 
      .epoll_fd=epoll_fd,
885
 
      .fd=pipefds[0],
886
 
      .quit_now=quit_now,
887
 
      .password=password,
888
 
      .password_is_read=password_is_read,
889
 
    });
890
 
}
891
 
 
892
 
__attribute__((nonnull))
893
 
void wait_for_mandos_client_exit(const task_context task,
894
 
                                 task_queue *const queue){
895
 
  const pid_t pid = task.pid;
896
 
  bool *const mandos_client_exited = task.mandos_client_exited;
897
 
  bool *const quit_now = task.quit_now;
898
 
 
899
 
  int status;
900
 
  switch(waitpid(pid, &status, WNOHANG)){
901
 
  case 0:                       /* Not exited yet */
902
 
    if(not add_to_queue(queue, task)){
903
 
      error(0, errno, "Failed to add myself to queue");
904
 
      *quit_now = true;
905
 
    }
906
 
    break;
907
 
  case -1:                      /* Error */
908
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
909
 
    if(errno != ECHILD){
910
 
      kill(pid, SIGTERM);
911
 
    }
912
 
    *quit_now = true;
913
 
    break;
914
 
  default:                      /* Has exited */
915
 
    *mandos_client_exited = true;
916
 
    if((not WIFEXITED(status))
917
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
918
 
      error(0, 0, "Mandos client failed or was killed");
919
 
      *quit_now = true;
920
 
    }
921
 
  }
922
 
}
923
 
 
924
 
__attribute__((nonnull))
925
 
void read_mandos_client_output(const task_context task,
926
 
                               task_queue *const queue){
927
 
  buffer *const password = task.password;
928
 
  bool *const quit_now = task.quit_now;
929
 
  bool *const password_is_read = task.password_is_read;
930
 
  const int fd = task.fd;
931
 
  const int epoll_fd = task.epoll_fd;
932
 
 
933
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
934
 
  if(password->allocated < new_potential_size){
935
 
    char *const new_buffer = calloc(new_potential_size, 1);
936
 
    if(new_buffer == NULL){
937
 
      error(0, errno, "Failed to allocate %" PRIuMAX
938
 
            " bytes for password", (uintmax_t)new_potential_size);
939
 
      *quit_now = true;
940
 
      close(fd);
941
 
      return;
942
 
    }
943
 
    if(mlock(new_buffer, new_potential_size) != 0){
944
 
      /* Warn but do not treat as fatal error */
945
 
      if(errno != EPERM and errno != ENOMEM){
946
 
        error(0, errno, "Failed to lock memory for password");
947
 
      }
948
 
    }
949
 
    if(password->length > 0){
950
 
      memcpy(new_buffer, password->data, password->length);
951
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
952
 
      explicit_bzero(password->data, password->allocated);
953
 
#else
954
 
      memset(password->data, '\0', password->allocated);
955
 
#endif
956
 
    }
957
 
    if(password->data != NULL){
958
 
      if(munlock(password->data, password->allocated) != 0){
959
 
        error(0, errno, "Failed to unlock memory of old buffer");
960
 
      }
961
 
      free(password->data);
962
 
    }
963
 
    password->data = new_buffer;
964
 
    password->allocated = new_potential_size;
965
 
  }
966
 
 
967
 
  const ssize_t read_length = read(fd, password->data
968
 
                                   + password->length, PIPE_BUF);
969
 
 
970
 
  if(read_length == 0){ /* EOF */
971
 
    *password_is_read = true;
972
 
    close(fd);
973
 
    return;
974
 
  }
975
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
976
 
    error(0, errno, "Failed to read password from Mandos client");
977
 
    *quit_now = true;
978
 
    close(fd);
979
 
    return;
980
 
  }
981
 
  if(read_length > 0){          /* Data has been read */
982
 
    password->length += (size_t)read_length;
983
 
  }
984
 
 
985
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
986
 
     available yet */
987
 
 
988
 
  /* Re-add the fd to the epoll set */
989
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
990
 
                            &(struct epoll_event)
991
 
                            { .events=EPOLLIN | EPOLLRDHUP });
992
 
  if(ret != 0 and errno != EEXIST){
993
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
994
 
    *quit_now = true;
995
 
    close(fd);
996
 
    return;
997
 
  }
998
 
 
999
 
  /* Re-add myself to the queue */
1000
 
  if(not add_to_queue(queue, task)){
1001
 
    error(0, errno, "Failed to add myself to queue");
1002
 
    *quit_now = true;
1003
 
    close(fd);
1004
 
  }
1005
 
}
1006
 
 
1007
 
__attribute__((nonnull, warn_unused_result))
1008
 
bool add_inotify_dir_watch(task_queue *const queue,
1009
 
                           const int epoll_fd, bool *const quit_now,
1010
 
                           buffer *const password,
1011
 
                           const char *const dir,
1012
 
                           string_set *cancelled_filenames,
1013
 
                           const mono_microsecs *const current_time,
1014
 
                           bool *const mandos_client_exited,
1015
 
                           bool *const password_is_read){
1016
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1017
 
  if(fd == -1){
1018
 
    error(0, errno, "Failed to create inotify instance");
1019
 
    return false;
1020
 
  }
1021
 
 
1022
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1023
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1024
 
                       | IN_ONLYDIR)
1025
 
     == -1){
1026
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
1027
 
    return false;
1028
 
  }
1029
 
 
1030
 
  /* Add the inotify fd to the epoll set */
1031
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1032
 
                            &(struct epoll_event)
1033
 
                            { .events=EPOLLIN | EPOLLRDHUP });
1034
 
  if(ret != 0 and errno != EEXIST){
1035
 
    error(0, errno, "Failed to add file descriptor to epoll set");
1036
 
    close(fd);
1037
 
    return false;
1038
 
  }
1039
 
 
1040
 
  const task_context read_inotify_event_task = {
1041
 
    .func=read_inotify_event,
1042
 
    .epoll_fd=epoll_fd,
1043
 
    .quit_now=quit_now,
1044
 
    .password=password,
1045
 
    .fd=fd,
1046
 
    .filename=strdup(dir),
1047
 
    .cancelled_filenames=cancelled_filenames,
1048
 
    .current_time=current_time,
1049
 
    .mandos_client_exited=mandos_client_exited,
1050
 
    .password_is_read=password_is_read,
1051
 
  };
1052
 
  if(read_inotify_event_task.filename == NULL){
1053
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
1054
 
    close(fd);
1055
 
    return false;
1056
 
  }
1057
 
 
1058
 
  return add_to_queue(queue, read_inotify_event_task);
1059
 
}
1060
 
 
1061
 
__attribute__((nonnull))
1062
 
void read_inotify_event(const task_context task,
1063
 
                        task_queue *const queue){
1064
 
  const int fd = task.fd;
1065
 
  const int epoll_fd = task.epoll_fd;
1066
 
  char *const filename = task.filename;
1067
 
  bool *quit_now = task.quit_now;
1068
 
  buffer *const password = task.password;
1069
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1070
 
  const mono_microsecs *const current_time = task.current_time;
1071
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1072
 
  bool *const password_is_read = task.password_is_read;
1073
 
 
1074
 
  /* "sufficient to read at least one event." - inotify(7) */
1075
 
  const size_t ievent_size = (sizeof(struct inotify_event)
1076
 
                              + NAME_MAX + 1);
1077
 
  struct {
1078
 
    struct inotify_event event;
1079
 
    char name_buffer[NAME_MAX + 1];
1080
 
  } ievent_buffer;
1081
 
  struct inotify_event *const ievent = &ievent_buffer.event;
1082
 
 
1083
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
1084
 
  if(read_length == 0){ /* EOF */
1085
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1086
 
    *quit_now = true;
1087
 
    cleanup_task(&task);
1088
 
    return;
1089
 
  }
1090
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1091
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
1092
 
          filename);
1093
 
    *quit_now = true;
1094
 
    cleanup_task(&task);
1095
 
    return;
1096
 
  }
1097
 
  if(read_length > 0            /* Data has been read */
1098
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1099
 
    char *question_filename = NULL;
1100
 
    const ssize_t question_filename_length
1101
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
1102
 
    if(question_filename_length < 0){
1103
 
      error(0, errno, "Failed to create file name from directory name"
1104
 
            " %s and file name %s", filename, ievent->name);
1105
 
    } else {
1106
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1107
 
        if(not add_to_queue(queue, (task_context){
1108
 
              .func=open_and_parse_question,
1109
 
              .epoll_fd=epoll_fd,
1110
 
              .question_filename=question_filename,
1111
 
              .filename=question_filename,
1112
 
              .password=password,
1113
 
              .cancelled_filenames=cancelled_filenames,
1114
 
              .current_time=current_time,
1115
 
              .mandos_client_exited=mandos_client_exited,
1116
 
              .password_is_read=password_is_read,
1117
 
            })){
1118
 
          error(0, errno, "Failed to add open_and_parse_question task"
1119
 
                " for file name %s to queue", filename);
1120
 
        } else {
1121
 
          /* Force the added task (open_and_parse_question) to run
1122
 
             immediately */
1123
 
          queue->next_run = 1;
1124
 
        }
1125
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1126
 
        if(not string_set_add(cancelled_filenames,
1127
 
                              question_filename)){
1128
 
          error(0, errno, "Could not add question %s to"
1129
 
                " cancelled_questions", question_filename);
1130
 
          *quit_now = true;
1131
 
          free(question_filename);
1132
 
          cleanup_task(&task);
1133
 
          return;
1134
 
        }
1135
 
        free(question_filename);
1136
 
      }
1137
 
    }
1138
 
  }
1139
 
 
1140
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
1141
 
     available yet */
1142
 
 
1143
 
  /* Re-add myself to the queue */
1144
 
  if(not add_to_queue(queue, task)){
1145
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1146
 
          " queue", filename);
1147
 
    *quit_now = true;
1148
 
    cleanup_task(&task);
1149
 
    return;
1150
 
  }
1151
 
 
1152
 
  /* Re-add the fd to the epoll set */
1153
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1154
 
                            &(struct epoll_event)
1155
 
                            { .events=EPOLLIN | EPOLLRDHUP });
1156
 
  if(ret != 0 and errno != EEXIST){
1157
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
1158
 
          " directory %s to epoll set", fd, filename);
1159
 
    /* Force the added task (read_inotify_event) to run again, at most
1160
 
       one second from now */
1161
 
    if((queue->next_run == 0)
1162
 
       or (queue->next_run > (*current_time + 1000000))){
1163
 
      queue->next_run = *current_time + 1000000;
1164
 
    }
1165
 
  }
1166
 
}
1167
 
 
1168
 
__attribute__((nonnull))
1169
 
void open_and_parse_question(const task_context task,
1170
 
                             task_queue *const queue){
1171
 
  __attribute__((cleanup(cleanup_string)))
1172
 
    char *question_filename = task.question_filename;
1173
 
  const int epoll_fd = task.epoll_fd;
1174
 
  buffer *const password = task.password;
1175
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1176
 
  const mono_microsecs *const current_time = task.current_time;
1177
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1178
 
  bool *const password_is_read = task.password_is_read;
1179
 
 
1180
 
  /* We use the GLib "Key-value file parser" functions to parse the
1181
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1182
 
     /systemd/PasswordAgents/> for specification of contents */
1183
 
  __attribute__((nonnull))
1184
 
    void cleanup_g_key_file(GKeyFile **key_file){
1185
 
    if(*key_file != NULL){
1186
 
      g_key_file_free(*key_file);
1187
 
    }
1188
 
  }
1189
 
 
1190
 
  __attribute__((cleanup(cleanup_g_key_file)))
1191
 
    GKeyFile *key_file = g_key_file_new();
1192
 
  if(key_file == NULL){
1193
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
1194
 
          question_filename);
1195
 
    return;
1196
 
  }
1197
 
  GError *glib_error = NULL;
1198
 
  if(g_key_file_load_from_file(key_file, question_filename,
1199
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
1200
 
    /* If a file was removed, we should ignore it, so */
1201
 
    /* only show error message if file actually existed */
1202
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
1203
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
1204
 
            question_filename, glib_error->message);
1205
 
    }
1206
 
    return;
1207
 
  }
1208
 
 
1209
 
  __attribute__((cleanup(cleanup_string)))
1210
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
1211
 
                                              "Socket",
1212
 
                                              &glib_error);
1213
 
  if(socket_name == NULL){
1214
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1215
 
          question_filename, glib_error->message);
1216
 
    return;
1217
 
  }
1218
 
 
1219
 
  if(strlen(socket_name) == 0){
1220
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1221
 
          question_filename);
1222
 
    return;
1223
 
  }
1224
 
 
1225
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1226
 
                                            &glib_error);
1227
 
  if(glib_error != NULL){
1228
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1229
 
          question_filename, glib_error->message);
1230
 
    return;
1231
 
  }
1232
 
 
1233
 
  if((pid != (guint64)((pid_t)pid))
1234
 
     or (kill((pid_t)pid, 0) != 0)){
1235
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1236
 
          " does not exist", (uintmax_t)pid, question_filename);
1237
 
    return;
1238
 
  }
1239
 
 
1240
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1241
 
                                           "NotAfter", &glib_error);
1242
 
  if(glib_error != NULL){
1243
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1244
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1245
 
            " %s", question_filename, glib_error->message);
1246
 
    }
1247
 
    notafter = 0;
1248
 
  }
1249
 
  if(notafter != 0){
1250
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
1251
 
      queue->next_run = notafter;
1252
 
    }
1253
 
    if(*current_time >= notafter){
1254
 
      return;
1255
 
    }
1256
 
  }
1257
 
 
1258
 
  const task_context connect_question_socket_task = {
1259
 
    .func=connect_question_socket,
1260
 
    .question_filename=strdup(question_filename),
1261
 
    .epoll_fd=epoll_fd,
1262
 
    .password=password,
1263
 
    .filename=strdup(socket_name),
1264
 
    .cancelled_filenames=task.cancelled_filenames,
1265
 
    .mandos_client_exited=mandos_client_exited,
1266
 
    .password_is_read=password_is_read,
1267
 
    .current_time=current_time,
1268
 
  };
1269
 
  if(connect_question_socket_task.question_filename == NULL
1270
 
     or connect_question_socket_task.filename == NULL
1271
 
     or not add_to_queue(queue, connect_question_socket_task)){
1272
 
    error(0, errno, "Failed to add connect_question_socket for socket"
1273
 
          " %s (from \"%s\") to queue", socket_name,
1274
 
          question_filename);
1275
 
    cleanup_task(&connect_question_socket_task);
1276
 
    return;
1277
 
  }
1278
 
  /* Force the added task (connect_question_socket) to run
1279
 
     immediately */
1280
 
  queue->next_run = 1;
1281
 
 
1282
 
  if(notafter > 0){
1283
 
    char *const dup_filename = strdup(question_filename);
1284
 
    const task_context cancel_old_question_task = {
1285
 
      .func=cancel_old_question,
1286
 
      .question_filename=dup_filename,
1287
 
      .notafter=notafter,
1288
 
      .filename=dup_filename,
1289
 
      .cancelled_filenames=cancelled_filenames,
1290
 
      .current_time=current_time,
1291
 
    };
1292
 
    if(cancel_old_question_task.question_filename == NULL
1293
 
       or not add_to_queue(queue, cancel_old_question_task)){
1294
 
      error(0, errno, "Failed to add cancel_old_question for file "
1295
 
            "\"%s\" to queue", question_filename);
1296
 
      cleanup_task(&cancel_old_question_task);
1297
 
      return;
1298
 
    }
1299
 
  }
1300
 
}
1301
 
 
1302
 
__attribute__((nonnull))
1303
 
void cancel_old_question(const task_context task,
1304
 
                         task_queue *const queue){
1305
 
  char *const question_filename = task.question_filename;
1306
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1307
 
  const mono_microsecs notafter = task.notafter;
1308
 
  const mono_microsecs *const current_time = task.current_time;
1309
 
 
1310
 
  if(*current_time >= notafter){
1311
 
    if(not string_set_add(cancelled_filenames, question_filename)){
1312
 
      error(0, errno, "Failed to cancel question for file %s",
1313
 
            question_filename);
1314
 
    }
1315
 
    cleanup_task(&task);
1316
 
    return;
1317
 
  }
1318
 
 
1319
 
  if(not add_to_queue(queue, task)){
1320
 
    error(0, errno, "Failed to add cancel_old_question for file "
1321
 
          "%s to queue", question_filename);
1322
 
    cleanup_task(&task);
1323
 
    return;
1324
 
  }
1325
 
 
1326
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
1327
 
    queue->next_run = notafter;
1328
 
  }
1329
 
}
1330
 
 
1331
 
__attribute__((nonnull))
1332
 
void connect_question_socket(const task_context task,
1333
 
                             task_queue *const queue){
1334
 
  char *const question_filename = task.question_filename;
1335
 
  char *const filename = task.filename;
1336
 
  const int epoll_fd = task.epoll_fd;
1337
 
  buffer *const password = task.password;
1338
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1339
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1340
 
  bool *const password_is_read = task.password_is_read;
1341
 
  const mono_microsecs *const current_time = task.current_time;
1342
 
 
1343
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1344
 
 
1345
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
1346
 
    error(0, 0, "Socket filename is larger than"
1347
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1348
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
1349
 
    if(not string_set_add(cancelled_filenames, question_filename)){
1350
 
      error(0, errno, "Failed to cancel question for file %s",
1351
 
            question_filename);
1352
 
    }
1353
 
    cleanup_task(&task);
1354
 
    return;
1355
 
  }
1356
 
 
1357
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
1358
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1359
 
  if(fd < 0){
1360
 
    error(0, errno,
1361
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1362
 
    if(not add_to_queue(queue, task)){
1363
 
      error(0, errno, "Failed to add connect_question_socket for file"
1364
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1365
 
            filename);
1366
 
      cleanup_task(&task);
1367
 
    } else {
1368
 
      /* Force the added task (connect_question_socket) to run
1369
 
         immediately */
1370
 
      queue->next_run = 1;
1371
 
    }
1372
 
    return;
1373
 
  }
1374
 
 
1375
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1376
 
  if(connect(fd, (struct sockaddr *)&sock_name,
1377
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
1378
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
1379
 
    if(not add_to_queue(queue, task)){
1380
 
      error(0, errno, "Failed to add connect_question_socket for file"
1381
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1382
 
            filename);
1383
 
      cleanup_task(&task);
1384
 
    } else {
1385
 
      /* Force the added task (connect_question_socket) to run again,
1386
 
         at most one second from now */
1387
 
      if((queue->next_run == 0)
1388
 
         or (queue->next_run > (*current_time + 1000000))){
1389
 
        queue->next_run = *current_time + 1000000;
1390
 
      }
1391
 
    }
1392
 
    return;
1393
 
  }
1394
 
 
1395
 
  /* Not necessary, but we can try, and merely warn on failure */
1396
 
  if(shutdown(fd, SHUT_RD) != 0){
1397
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1398
 
          filename);
1399
 
  }
1400
 
 
1401
 
  /* Add the fd to the epoll set */
1402
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1403
 
               &(struct epoll_event){ .events=EPOLLOUT })
1404
 
     != 0){
1405
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
1406
 
          " socket %s to epoll set", fd, filename);
1407
 
    if(not add_to_queue(queue, task)){
1408
 
      error(0, errno, "Failed to add connect_question_socket for file"
1409
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1410
 
            filename);
1411
 
      cleanup_task(&task);
1412
 
    } else {
1413
 
      /* Force the added task (connect_question_socket) to run again,
1414
 
         at most one second from now */
1415
 
      if((queue->next_run == 0)
1416
 
         or (queue->next_run > (*current_time + 1000000))){
1417
 
        queue->next_run = *current_time + 1000000;
1418
 
      }
1419
 
    }
1420
 
    return;
1421
 
  }
1422
 
 
1423
 
  /* add task send_password_to_socket to queue */
1424
 
  const task_context send_password_to_socket_task = {
1425
 
    .func=send_password_to_socket,
1426
 
    .question_filename=question_filename,
1427
 
    .filename=filename,
1428
 
    .epoll_fd=epoll_fd,
1429
 
    .fd=fd,
1430
 
    .password=password,
1431
 
    .cancelled_filenames=cancelled_filenames,
1432
 
    .mandos_client_exited=mandos_client_exited,
1433
 
    .password_is_read=password_is_read,
1434
 
    .current_time=current_time,
1435
 
  };
1436
 
 
1437
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
1438
 
    error(0, errno, "Failed to add send_password_to_socket for"
1439
 
          " file \"%s\" and socket \"%s\" to queue",
1440
 
          question_filename, filename);
1441
 
    cleanup_task(&send_password_to_socket_task);
1442
 
  }
1443
 
}
1444
 
 
1445
 
__attribute__((nonnull))
1446
 
void send_password_to_socket(const task_context task,
1447
 
                             task_queue *const queue){
1448
 
  char *const question_filename=task.question_filename;
1449
 
  char *const filename=task.filename;
1450
 
  const int epoll_fd=task.epoll_fd;
1451
 
  const int fd=task.fd;
1452
 
  buffer *const password=task.password;
1453
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
1454
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1455
 
  bool *const password_is_read = task.password_is_read;
1456
 
  const mono_microsecs *const current_time = task.current_time;
1457
 
 
1458
 
  if(*mandos_client_exited and *password_is_read){
1459
 
 
1460
 
    const size_t send_buffer_length = password->length + 2;
1461
 
    char *send_buffer = malloc(send_buffer_length);
1462
 
    if(send_buffer == NULL){
1463
 
      error(0, errno, "Failed to allocate send_buffer");
1464
 
    } else {
1465
 
      if(mlock(send_buffer, send_buffer_length) != 0){
1466
 
        /* Warn but do not treat as fatal error */
1467
 
        if(errno != EPERM and errno != ENOMEM){
1468
 
          error(0, errno, "Failed to lock memory for password"
1469
 
                " buffer");
1470
 
        }
1471
 
      }
1472
 
      /* “[…] send a single datagram to the socket consisting of the
1473
 
         password string either prefixed with "+" or with "-"
1474
 
         depending on whether the password entry was successful or
1475
 
         not. You may but don't have to include a final NUL byte in
1476
 
         your message.
1477
 
 
1478
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1479
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1480
 
      */
1481
 
      send_buffer[0] = '+';     /* Prefix with "+" */
1482
 
      /* Always add an extra NUL */
1483
 
      send_buffer[password->length + 1] = '\0';
1484
 
      if(password->length > 0){
1485
 
        memcpy(send_buffer + 1, password->data, password->length);
1486
 
      }
1487
 
      errno = 0;
1488
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1489
 
                           MSG_NOSIGNAL);
1490
 
      const error_t saved_errno = errno;
1491
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1492
 
      explicit_bzero(send_buffer, send_buffer_length);
1493
 
#else
1494
 
      memset(send_buffer, '\0', send_buffer_length);
1495
 
#endif
1496
 
      if(munlock(send_buffer, send_buffer_length) != 0){
1497
 
        error(0, errno, "Failed to unlock memory of send buffer");
1498
 
      }
1499
 
      free(send_buffer);
1500
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1501
 
        switch(saved_errno){
1502
 
        case EINTR:
1503
 
        case ENOBUFS:
1504
 
        case ENOMEM:
1505
 
        case EADDRINUSE:
1506
 
        case ECONNREFUSED:
1507
 
        case ECONNRESET:
1508
 
        case ENOENT:
1509
 
        case ETOOMANYREFS:
1510
 
        case EAGAIN:
1511
 
          /* Retry, below */
1512
 
          break;
1513
 
        case EMSGSIZE:
1514
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1515
 
                (uintmax_t)password->length);
1516
 
#if __GNUC__ < 7
1517
 
          /* FALLTHROUGH */
1518
 
#else
1519
 
          __attribute__((fallthrough));
1520
 
#endif
1521
 
        case 0:
1522
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1523
 
            error(0, 0, "Password only partially sent to socket");
1524
 
          }
1525
 
#if __GNUC__ < 7
1526
 
          /* FALLTHROUGH */
1527
 
#else
1528
 
          __attribute__((fallthrough));
1529
 
#endif
1530
 
        default:
1531
 
          error(0, saved_errno, "Failed to send() to socket %s",
1532
 
                filename);
1533
 
          if(not string_set_add(cancelled_filenames,
1534
 
                                question_filename)){
1535
 
            error(0, errno, "Failed to cancel question for file %s",
1536
 
                  question_filename);
1537
 
          }
1538
 
          cleanup_task(&task);
1539
 
          return;
1540
 
        }
1541
 
      } else {
1542
 
        /* Success */
1543
 
        cleanup_task(&task);
1544
 
        return;
1545
 
      }
1546
 
    }
1547
 
  }
1548
 
 
1549
 
  /* We failed or are not ready yet; retry later */
1550
 
 
1551
 
  if(not add_to_queue(queue, task)){
1552
 
    error(0, errno, "Failed to add send_password_to_socket for"
1553
 
          " file %s and socket %s to queue", question_filename,
1554
 
          filename);
1555
 
    cleanup_task(&task);
1556
 
  }
1557
 
 
1558
 
  /* Add the fd to the epoll set */
1559
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1560
 
               &(struct epoll_event){ .events=EPOLLOUT })
1561
 
     != 0){
1562
 
    error(0, errno, "Failed to add socket file descriptor %d for"
1563
 
          " socket %s to epoll set", fd, filename);
1564
 
    /* Force the added task (send_password_to_socket) to run again, at
1565
 
       most one second from now */
1566
 
    if((queue->next_run == 0)
1567
 
       or (queue->next_run > (*current_time + 1000000))){
1568
 
      queue->next_run = *current_time + 1000000;
1569
 
    }
1570
 
  }
1571
 
}
1572
 
 
1573
 
__attribute__((warn_unused_result))
1574
 
bool add_existing_questions(task_queue *const queue,
1575
 
                            const int epoll_fd,
1576
 
                            buffer *const password,
1577
 
                            string_set *cancelled_filenames,
1578
 
                            const mono_microsecs *const current_time,
1579
 
                            bool *const mandos_client_exited,
1580
 
                            bool *const password_is_read,
1581
 
                            const char *const dirname){
1582
 
  __attribute__((cleanup(cleanup_string)))
1583
 
    char *dir_pattern = NULL;
1584
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1585
 
  if(ret < 0 or dir_pattern == NULL){
1586
 
    error(0, errno, "Could not create glob pattern for directory %s",
1587
 
          dirname);
1588
 
    return false;
1589
 
  }
1590
 
  __attribute__((cleanup(globfree)))
1591
 
    glob_t question_filenames = {};
1592
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1593
 
              NULL, &question_filenames)){
1594
 
  case GLOB_ABORTED:
1595
 
  default:
1596
 
    error(0, errno, "Failed to open directory %s", dirname);
1597
 
    return false;
1598
 
  case GLOB_NOMATCH:
1599
 
    error(0, errno, "There are no question files in %s", dirname);
1600
 
    return false;
1601
 
  case GLOB_NOSPACE:
1602
 
    error(0, errno, "Could not allocate memory for question file"
1603
 
          " names in %s", dirname);
1604
 
#if __GNUC__ < 7
1605
 
    /* FALLTHROUGH */
1606
 
#else
1607
 
    __attribute__((fallthrough));
1608
 
#endif
1609
 
  case 0:
1610
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1611
 
      char *const question_filename = strdup(question_filenames
1612
 
                                             .gl_pathv[i]);
1613
 
      const task_context task = {
1614
 
        .func=open_and_parse_question,
1615
 
        .epoll_fd=epoll_fd,
1616
 
        .question_filename=question_filename,
1617
 
        .filename=question_filename,
1618
 
        .password=password,
1619
 
        .cancelled_filenames=cancelled_filenames,
1620
 
        .current_time=current_time,
1621
 
        .mandos_client_exited=mandos_client_exited,
1622
 
        .password_is_read=password_is_read,
1623
 
      };
1624
 
 
1625
 
      if(question_filename == NULL
1626
 
         or not add_to_queue(queue, task)){
1627
 
        error(0, errno, "Failed to add open_and_parse_question for"
1628
 
              " file %s to queue",
1629
 
              question_filenames.gl_pathv[i]);
1630
 
        free(question_filename);
1631
 
      } else {
1632
 
        queue->next_run = 1;
1633
 
      }
1634
 
    }
1635
 
    return true;
1636
 
  }
1637
 
}
1638
 
 
1639
 
__attribute__((nonnull, warn_unused_result))
1640
 
bool wait_for_event(const int epoll_fd,
1641
 
                    const mono_microsecs queue_next_run,
1642
 
                    const mono_microsecs current_time){
1643
 
  __attribute__((const))
1644
 
    int milliseconds_to_wait(const mono_microsecs currtime,
1645
 
                             const mono_microsecs nextrun){
1646
 
    if(currtime >= nextrun){
1647
 
      return 0;
1648
 
    }
1649
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1650
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
1651
 
      return INT_MAX;
1652
 
    }
1653
 
    return (int)wait_time_ms;
1654
 
  }
1655
 
 
1656
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
1657
 
                                                queue_next_run);
1658
 
 
1659
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
1660
 
  sigset_t temporary_unblocked_sigmask;
1661
 
  /* Get current signal mask */
1662
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1663
 
    return false;
1664
 
  }
1665
 
  /* Remove SIGCHLD from the signal mask */
1666
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1667
 
    return false;
1668
 
  }
1669
 
  struct epoll_event events[8]; /* Ignored */
1670
 
  int ret = epoll_pwait(epoll_fd, events,
1671
 
                        sizeof(events) / sizeof(struct epoll_event),
1672
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
1673
 
                        &temporary_unblocked_sigmask);
1674
 
  if(ret < 0 and errno != EINTR){
1675
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1676
 
          " ...", epoll_fd,
1677
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
1678
 
    return false;
1679
 
  }
1680
 
  return clear_all_fds_from_epoll_set(epoll_fd);
1681
 
}
1682
 
 
1683
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1684
 
  /* Create a new empty epoll set */
1685
 
  __attribute__((cleanup(cleanup_close)))
1686
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1687
 
  if(new_epoll_fd < 0){
1688
 
    return false;
1689
 
  }
1690
 
  /* dup3() the new epoll set fd over the old one, replacing it */
1691
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1692
 
    return false;
1693
 
  }
1694
 
  return true;
1695
 
}
1696
 
 
1697
 
__attribute__((nonnull, warn_unused_result))
1698
 
bool run_queue(task_queue **const queue,
1699
 
               string_set *const cancelled_filenames,
1700
 
               bool *const quit_now){
1701
 
 
1702
 
  task_queue *new_queue = create_queue();
1703
 
  if(new_queue == NULL){
1704
 
    return false;
1705
 
  }
1706
 
 
1707
 
  __attribute__((cleanup(string_set_clear)))
1708
 
    string_set old_cancelled_filenames = {};
1709
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1710
 
 
1711
 
  /* Declare i outside the for loop, since we might need i after the
1712
 
     loop in case we aborted in the middle */
1713
 
  size_t i;
1714
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
1715
 
    task_context *const task = &((*queue)->tasks[i]);
1716
 
    const char *const question_filename = task->question_filename;
1717
 
    /* Skip any task referencing a cancelled question filename */
1718
 
    if(question_filename != NULL
1719
 
       and string_set_contains(old_cancelled_filenames,
1720
 
                               question_filename)){
1721
 
      cleanup_task(task);
1722
 
      continue;
1723
 
    }
1724
 
    task->func(*task, new_queue);
1725
 
  }
1726
 
 
1727
 
  if(*quit_now){
1728
 
    /* we might be in the middle of the queue, so clean up any
1729
 
       remaining tasks in the current queue */
1730
 
    for(; i < (*queue)->length; i++){
1731
 
      cleanup_task(&((*queue)->tasks[i]));
1732
 
    }
1733
 
    free_queue(*queue);
1734
 
    *queue = new_queue;
1735
 
    new_queue = NULL;
1736
 
    return false;
1737
 
  }
1738
 
  free_queue(*queue);
1739
 
  *queue = new_queue;
1740
 
  new_queue = NULL;
1741
 
 
1742
 
  return true;
1743
 
}
1744
 
 
1745
 
/* End of regular code section */
1746
 
 
1747
 
/* Start of tests section; here are the tests for the above code */
1748
 
 
1749
 
/* This "fixture" data structure is used by the test setup and
1750
 
   teardown functions */
1751
 
typedef struct {
1752
 
  struct sigaction orig_sigaction;
1753
 
  sigset_t orig_sigmask;
1754
 
} test_fixture;
1755
 
 
1756
 
static void test_setup(test_fixture *fixture,
1757
 
                       __attribute__((unused))
1758
 
                       gconstpointer user_data){
1759
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1760
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
1761
 
}
1762
 
 
1763
 
static void test_teardown(test_fixture *fixture,
1764
 
                          __attribute__((unused))
1765
 
                          gconstpointer user_data){
1766
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1767
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1768
 
}
1769
 
 
1770
 
/* Utility function used by tests to search queue for matching task */
1771
 
__attribute__((pure, nonnull, warn_unused_result))
1772
 
static task_context *find_matching_task(const task_queue *const queue,
1773
 
                                        const task_context task){
1774
 
  /* The argument "task" structure is a pattern to match; 0 in any
1775
 
     member means any value matches, otherwise the value must match.
1776
 
     The filename strings are compared by strcmp(), not by pointer. */
1777
 
  for(size_t i = 0; i < queue->length; i++){
1778
 
    task_context *const current_task = queue->tasks+i;
1779
 
    /* Check all members of task_context, if set to a non-zero value.
1780
 
       If a member does not match, continue to next task in queue */
1781
 
 
1782
 
    /* task_func *const func */
1783
 
    if(task.func != NULL and current_task->func != task.func){
1784
 
      continue;
1785
 
    }
1786
 
    /* char *const question_filename; */
1787
 
    if(task.question_filename != NULL
1788
 
       and (current_task->question_filename == NULL
1789
 
            or strcmp(current_task->question_filename,
1790
 
                      task.question_filename) != 0)){
1791
 
      continue;
1792
 
    }
1793
 
    /* const pid_t pid; */
1794
 
    if(task.pid != 0 and current_task->pid != task.pid){
1795
 
      continue;
1796
 
    }
1797
 
    /* const int epoll_fd; */
1798
 
    if(task.epoll_fd != 0
1799
 
       and current_task->epoll_fd != task.epoll_fd){
1800
 
      continue;
1801
 
    }
1802
 
    /* bool *const quit_now; */
1803
 
    if(task.quit_now != NULL
1804
 
       and current_task->quit_now != task.quit_now){
1805
 
      continue;
1806
 
    }
1807
 
    /* const int fd; */
1808
 
    if(task.fd != 0 and current_task->fd != task.fd){
1809
 
      continue;
1810
 
    }
1811
 
    /* bool *const mandos_client_exited; */
1812
 
    if(task.mandos_client_exited != NULL
1813
 
       and current_task->mandos_client_exited
1814
 
       != task.mandos_client_exited){
1815
 
      continue;
1816
 
    }
1817
 
    /* buffer *const password; */
1818
 
    if(task.password != NULL
1819
 
       and current_task->password != task.password){
1820
 
      continue;
1821
 
    }
1822
 
    /* bool *const password_is_read; */
1823
 
    if(task.password_is_read != NULL
1824
 
       and current_task->password_is_read != task.password_is_read){
1825
 
      continue;
1826
 
    }
1827
 
    /* char *filename; */
1828
 
    if(task.filename != NULL
1829
 
       and (current_task->filename == NULL
1830
 
            or strcmp(current_task->filename, task.filename) != 0)){
1831
 
      continue;
1832
 
    }
1833
 
    /* string_set *const cancelled_filenames; */
1834
 
    if(task.cancelled_filenames != NULL
1835
 
       and current_task->cancelled_filenames
1836
 
       != task.cancelled_filenames){
1837
 
      continue;
1838
 
    }
1839
 
    /* const mono_microsecs notafter; */
1840
 
    if(task.notafter != 0
1841
 
       and current_task->notafter != task.notafter){
1842
 
      continue;
1843
 
    }
1844
 
    /* const mono_microsecs *const current_time; */
1845
 
    if(task.current_time != NULL
1846
 
       and current_task->current_time != task.current_time){
1847
 
      continue;
1848
 
    }
1849
 
    /* Current task matches all members; return it */
1850
 
    return current_task;
1851
 
  }
1852
 
  /* No task in queue matches passed pattern task */
1853
 
  return NULL;
1854
 
}
1855
 
 
1856
 
static void test_create_queue(__attribute__((unused))
1857
 
                              test_fixture *fixture,
1858
 
                              __attribute__((unused))
1859
 
                              gconstpointer user_data){
1860
 
  __attribute__((cleanup(cleanup_queue)))
1861
 
    task_queue *const queue = create_queue();
1862
 
  g_assert_nonnull(queue);
1863
 
  g_assert_null(queue->tasks);
1864
 
  g_assert_true(queue->length == 0);
1865
 
  g_assert_true(queue->next_run == 0);
1866
 
}
1867
 
 
1868
 
static task_func dummy_func;
1869
 
 
1870
 
static void test_add_to_queue(__attribute__((unused))
1871
 
                              test_fixture *fixture,
1872
 
                              __attribute__((unused))
1873
 
                              gconstpointer user_data){
1874
 
  __attribute__((cleanup(cleanup_queue)))
1875
 
    task_queue *queue = create_queue();
1876
 
  g_assert_nonnull(queue);
1877
 
 
1878
 
  g_assert_true(add_to_queue(queue,
1879
 
                             (task_context){ .func=dummy_func }));
1880
 
  g_assert_true(queue->length == 1);
1881
 
  g_assert_nonnull(queue->tasks);
1882
 
  g_assert_true(queue->tasks[0].func == dummy_func);
1883
 
}
1884
 
 
1885
 
static void dummy_func(__attribute__((unused))
1886
 
                       const task_context task,
1887
 
                       __attribute__((unused))
1888
 
                       task_queue *const queue){
1889
 
}
1890
 
 
1891
 
static void test_queue_has_question_empty(__attribute__((unused))
1892
 
                                          test_fixture *fixture,
1893
 
                                          __attribute__((unused))
1894
 
                                          gconstpointer user_data){
1895
 
  __attribute__((cleanup(cleanup_queue)))
1896
 
    task_queue *queue = create_queue();
1897
 
  g_assert_nonnull(queue);
1898
 
  g_assert_false(queue_has_question(queue));
1899
 
}
1900
 
 
1901
 
static void test_queue_has_question_false(__attribute__((unused))
1902
 
                                          test_fixture *fixture,
1903
 
                                          __attribute__((unused))
1904
 
                                          gconstpointer user_data){
1905
 
  __attribute__((cleanup(cleanup_queue)))
1906
 
    task_queue *queue = create_queue();
1907
 
  g_assert_nonnull(queue);
1908
 
  g_assert_true(add_to_queue(queue,
1909
 
                             (task_context){ .func=dummy_func }));
1910
 
  g_assert_false(queue_has_question(queue));
1911
 
}
1912
 
 
1913
 
static void test_queue_has_question_true(__attribute__((unused))
1914
 
                                         test_fixture *fixture,
1915
 
                                         __attribute__((unused))
1916
 
                                         gconstpointer user_data){
1917
 
  __attribute__((cleanup(cleanup_queue)))
1918
 
    task_queue *queue = create_queue();
1919
 
  g_assert_nonnull(queue);
1920
 
  char *const question_filename
1921
 
    = strdup("/nonexistent/question_filename");
1922
 
  g_assert_nonnull(question_filename);
1923
 
  task_context task = {
1924
 
    .func=dummy_func,
1925
 
    .question_filename=question_filename,
1926
 
  };
1927
 
  g_assert_true(add_to_queue(queue, task));
1928
 
  g_assert_true(queue_has_question(queue));
1929
 
}
1930
 
 
1931
 
static void test_queue_has_question_false2(__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
 
  task_context task = { .func=dummy_func };
1939
 
  g_assert_true(add_to_queue(queue, task));
1940
 
  g_assert_true(add_to_queue(queue, task));
1941
 
  g_assert_cmpint((int)queue->length, ==, 2);
1942
 
  g_assert_false(queue_has_question(queue));
1943
 
}
1944
 
 
1945
 
static void test_queue_has_question_true2(__attribute__((unused))
1946
 
                                          test_fixture *fixture,
1947
 
                                          __attribute__((unused))
1948
 
                                          gconstpointer user_data){
1949
 
  __attribute__((cleanup(cleanup_queue)))
1950
 
    task_queue *queue = create_queue();
1951
 
  g_assert_nonnull(queue);
1952
 
  task_context task1 = { .func=dummy_func };
1953
 
  g_assert_true(add_to_queue(queue, task1));
1954
 
  char *const question_filename
1955
 
    = strdup("/nonexistent/question_filename");
1956
 
  g_assert_nonnull(question_filename);
1957
 
  task_context task2 = {
1958
 
    .func=dummy_func,
1959
 
    .question_filename=question_filename,
1960
 
  };
1961
 
  g_assert_true(add_to_queue(queue, task2));
1962
 
  g_assert_cmpint((int)queue->length, ==, 2);
1963
 
  g_assert_true(queue_has_question(queue));
1964
 
}
1965
 
 
1966
 
static void test_cleanup_buffer(__attribute__((unused))
1967
 
                                test_fixture *fixture,
1968
 
                                __attribute__((unused))
1969
 
                                gconstpointer user_data){
1970
 
  buffer buf = {};
1971
 
 
1972
 
  const size_t buffersize = 10;
1973
 
 
1974
 
  buf.data = malloc(buffersize);
1975
 
  g_assert_nonnull(buf.data);
1976
 
  if(mlock(buf.data, buffersize) != 0){
1977
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
1978
 
  }
1979
 
 
1980
 
  cleanup_buffer(&buf);
1981
 
  g_assert_null(buf.data);
1982
 
}
1983
 
 
1984
 
static
1985
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1986
 
                                              test_fixture *fixture,
1987
 
                                              __attribute__((unused))
1988
 
                                              gconstpointer
1989
 
                                              user_data){
1990
 
  __attribute__((cleanup(string_set_clear)))
1991
 
    string_set set = {};
1992
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
1993
 
  g_assert_false(string_set_contains(set, "test_string"));
1994
 
}
1995
 
 
1996
 
static void
1997
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
1998
 
                                              test_fixture *fixture,
1999
 
                                              __attribute__((unused))
2000
 
                                              gconstpointer
2001
 
                                              user_data){
2002
 
  __attribute__((cleanup(string_set_clear)))
2003
 
    string_set set = {};
2004
 
  g_assert_true(string_set_add(&set, "test_string"));
2005
 
  g_assert_true(string_set_contains(set, "test_string"));
2006
 
}
2007
 
 
2008
 
static void
2009
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2010
 
                                             test_fixture *fixture,
2011
 
                                             __attribute__((unused))
2012
 
                                             gconstpointer user_data){
2013
 
  __attribute__((cleanup(string_set_clear)))
2014
 
    string_set set = {};
2015
 
  g_assert_true(string_set_add(&set, "test_string"));
2016
 
  string_set_clear(&set);
2017
 
  g_assert_false(string_set_contains(set, "test_string"));
2018
 
}
2019
 
 
2020
 
static
2021
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
2022
 
                                         test_fixture *fixture,
2023
 
                                         __attribute__((unused))
2024
 
                                         gconstpointer user_data){
2025
 
  __attribute__((cleanup(string_set_clear)))
2026
 
    string_set set1 = {};
2027
 
  __attribute__((cleanup(string_set_clear)))
2028
 
    string_set set2 = {};
2029
 
  g_assert_true(string_set_add(&set1, "test_string1"));
2030
 
  string_set_swap(&set1, &set2);
2031
 
  g_assert_false(string_set_contains(set1, "test_string1"));
2032
 
  g_assert_true(string_set_contains(set2, "test_string1"));
2033
 
}
2034
 
 
2035
 
static
2036
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
2037
 
                                         test_fixture *fixture,
2038
 
                                         __attribute__((unused))
2039
 
                                         gconstpointer user_data){
2040
 
  __attribute__((cleanup(string_set_clear)))
2041
 
    string_set set1 = {};
2042
 
  __attribute__((cleanup(string_set_clear)))
2043
 
    string_set set2 = {};
2044
 
  g_assert_true(string_set_add(&set2, "test_string2"));
2045
 
  string_set_swap(&set1, &set2);
2046
 
  g_assert_true(string_set_contains(set1, "test_string2"));
2047
 
  g_assert_false(string_set_contains(set2, "test_string2"));
2048
 
}
2049
 
 
2050
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
2051
 
                                              test_fixture *fixture,
2052
 
                                              __attribute__((unused))
2053
 
                                              gconstpointer
2054
 
                                              user_data){
2055
 
  __attribute__((cleanup(string_set_clear)))
2056
 
    string_set set1 = {};
2057
 
  __attribute__((cleanup(string_set_clear)))
2058
 
    string_set set2 = {};
2059
 
  g_assert_true(string_set_add(&set1, "test_string1"));
2060
 
  g_assert_true(string_set_add(&set2, "test_string2"));
2061
 
  string_set_swap(&set1, &set2);
2062
 
  g_assert_false(string_set_contains(set1, "test_string1"));
2063
 
  g_assert_true(string_set_contains(set1, "test_string2"));
2064
 
  g_assert_false(string_set_contains(set2, "test_string2"));
2065
 
  g_assert_true(string_set_contains(set2, "test_string1"));
2066
 
}
2067
 
 
2068
 
static bool fd_has_cloexec_and_nonblock(const int);
2069
 
 
2070
 
static bool epoll_set_contains(int, int, uint32_t);
2071
 
 
2072
 
static void test_start_mandos_client(test_fixture *fixture,
2073
 
                                     __attribute__((unused))
2074
 
                                     gconstpointer user_data){
2075
 
 
2076
 
  bool mandos_client_exited = false;
2077
 
  bool quit_now = false;
2078
 
  __attribute__((cleanup(cleanup_close)))
2079
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2080
 
  g_assert_cmpint(epoll_fd, >=, 0);
2081
 
  __attribute__((cleanup(cleanup_queue)))
2082
 
    task_queue *queue = create_queue();
2083
 
  g_assert_nonnull(queue);
2084
 
  buffer password = {};
2085
 
  bool password_is_read = false;
2086
 
  const char helper_directory[] = "/nonexistent";
2087
 
  const char *const argv[] = { "/bin/true", NULL };
2088
 
 
2089
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
2090
 
                                    &mandos_client_exited, &quit_now,
2091
 
                                    &password, &password_is_read,
2092
 
                                    &fixture->orig_sigaction,
2093
 
                                    fixture->orig_sigmask,
2094
 
                                    helper_directory, 0, 0, argv));
2095
 
 
2096
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2097
 
 
2098
 
  const task_context *const added_wait_task
2099
 
    = find_matching_task(queue, (task_context){
2100
 
        .func=wait_for_mandos_client_exit,
2101
 
        .mandos_client_exited=&mandos_client_exited,
2102
 
        .quit_now=&quit_now,
2103
 
      });
2104
 
  g_assert_nonnull(added_wait_task);
2105
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
2106
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2107
 
  waitpid(added_wait_task->pid, NULL, 0);
2108
 
 
2109
 
  const task_context *const added_read_task
2110
 
    = find_matching_task(queue, (task_context){
2111
 
        .func=read_mandos_client_output,
2112
 
        .epoll_fd=epoll_fd,
2113
 
        .password=&password,
2114
 
        .password_is_read=&password_is_read,
2115
 
        .quit_now=&quit_now,
2116
 
      });
2117
 
  g_assert_nonnull(added_read_task);
2118
 
  g_assert_cmpint(added_read_task->fd, >, 2);
2119
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2120
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2121
 
                                   EPOLLIN | EPOLLRDHUP));
2122
 
}
2123
 
 
2124
 
static bool fd_has_cloexec_and_nonblock(const int fd){
2125
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2126
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2127
 
  return ((socket_fd_flags >= 0)
2128
 
          and (socket_fd_flags & FD_CLOEXEC)
2129
 
          and (socket_file_flags >= 0)
2130
 
          and (socket_file_flags & O_NONBLOCK));
2131
 
}
2132
 
 
2133
 
__attribute__((const))
2134
 
bool is_privileged(void){
2135
 
  uid_t user = getuid() + 1;
2136
 
  if(user == 0){                /* Overflow check */
2137
 
    user++;
2138
 
  }
2139
 
  gid_t group = getuid() + 1;
2140
 
  if(group == 0){               /* Overflow check */
2141
 
    group++;
2142
 
  }
2143
 
  const pid_t pid = fork();
2144
 
  if(pid == 0){                 /* Child */
2145
 
    if(setresgid((uid_t)-1, group, group) == -1){
2146
 
      if(errno != EPERM){
2147
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2148
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2149
 
      }
2150
 
      exit(EXIT_FAILURE);
2151
 
    }
2152
 
    if(setresuid((uid_t)-1, user, user) == -1){
2153
 
      if(errno != EPERM){
2154
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2155
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2156
 
      }
2157
 
      exit(EXIT_FAILURE);
2158
 
    }
2159
 
    exit(EXIT_SUCCESS);
2160
 
  }
2161
 
  int status;
2162
 
  waitpid(pid, &status, 0);
2163
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2164
 
    return true;
2165
 
  }
2166
 
  return false;
2167
 
}
2168
 
 
2169
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2170
 
  /* Only scan for events in this eventmask */
2171
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2172
 
  __attribute__((cleanup(cleanup_string)))
2173
 
    char *fdinfo_name = NULL;
2174
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2175
 
  g_assert_cmpint(ret, >, 0);
2176
 
  g_assert_nonnull(fdinfo_name);
2177
 
 
2178
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
2179
 
  g_assert_nonnull(fdinfo);
2180
 
  uint32_t reported_events;
2181
 
  buffer line = {};
2182
 
  int found_fd = -1;
2183
 
 
2184
 
  do {
2185
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
2186
 
      break;
2187
 
    }
2188
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2189
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2190
 
              &found_fd, &reported_events) == 2){
2191
 
      if(found_fd == fd){
2192
 
        break;
2193
 
      }
2194
 
    }
2195
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
2196
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
2197
 
  free(line.data);
2198
 
  if(found_fd != fd){
2199
 
    return false;
2200
 
  }
2201
 
 
2202
 
  if(events == 0){
2203
 
    /* Don't check events if none are given */
2204
 
    return true;
2205
 
  }
2206
 
  return (reported_events & eventmask) == (events & eventmask);
2207
 
}
2208
 
 
2209
 
static void test_start_mandos_client_execv(test_fixture *fixture,
2210
 
                                           __attribute__((unused))
2211
 
                                           gconstpointer user_data){
2212
 
  bool mandos_client_exited = false;
2213
 
  bool quit_now = false;
2214
 
  __attribute__((cleanup(cleanup_close)))
2215
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2216
 
  g_assert_cmpint(epoll_fd, >=, 0);
2217
 
  __attribute__((cleanup(cleanup_queue)))
2218
 
    task_queue *queue = create_queue();
2219
 
  g_assert_nonnull(queue);
2220
 
  __attribute__((cleanup(cleanup_buffer)))
2221
 
    buffer password = {};
2222
 
  const char helper_directory[] = "/nonexistent";
2223
 
  /* Can't execv("/", ...), so this should fail */
2224
 
  const char *const argv[] = { "/", NULL };
2225
 
 
2226
 
  {
2227
 
    __attribute__((cleanup(cleanup_close)))
2228
 
      const int devnull_fd = open("/dev/null",
2229
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
2230
 
    g_assert_cmpint(devnull_fd, >=, 0);
2231
 
    __attribute__((cleanup(cleanup_close)))
2232
 
      const int real_stderr_fd = dup(STDERR_FILENO);
2233
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
2234
 
    dup2(devnull_fd, STDERR_FILENO);
2235
 
 
2236
 
    const bool success = start_mandos_client(queue, epoll_fd,
2237
 
                                             &mandos_client_exited,
2238
 
                                             &quit_now,
2239
 
                                             &password,
2240
 
                                             (bool[]){false},
2241
 
                                             &fixture->orig_sigaction,
2242
 
                                             fixture->orig_sigmask,
2243
 
                                             helper_directory, 0, 0,
2244
 
                                             argv);
2245
 
    dup2(real_stderr_fd, STDERR_FILENO);
2246
 
    g_assert_true(success);
2247
 
  }
2248
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2249
 
 
2250
 
  struct timespec starttime, currtime;
2251
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2252
 
  do {
2253
 
    queue->next_run = 0;
2254
 
    string_set cancelled_filenames = {};
2255
 
 
2256
 
    {
2257
 
      __attribute__((cleanup(cleanup_close)))
2258
 
        const int devnull_fd = open("/dev/null",
2259
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
2260
 
      g_assert_cmpint(devnull_fd, >=, 0);
2261
 
      __attribute__((cleanup(cleanup_close)))
2262
 
        const int real_stderr_fd = dup(STDERR_FILENO);
2263
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
2264
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2265
 
      dup2(devnull_fd, STDERR_FILENO);
2266
 
      const bool success = run_queue(&queue, &cancelled_filenames,
2267
 
                                     &quit_now);
2268
 
      dup2(real_stderr_fd, STDERR_FILENO);
2269
 
      if(not success){
2270
 
        break;
2271
 
      }
2272
 
    }
2273
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2274
 
  } while(((queue->length) > 0)
2275
 
          and (not quit_now)
2276
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2277
 
 
2278
 
  g_assert_true(quit_now);
2279
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2280
 
  g_assert_true(mandos_client_exited);
2281
 
}
2282
 
 
2283
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2284
 
                                               __attribute__((unused))
2285
 
                                               gconstpointer
2286
 
                                               user_data){
2287
 
  if(not is_privileged()){
2288
 
    g_test_skip("Not privileged");
2289
 
    return;
2290
 
  }
2291
 
 
2292
 
  bool mandos_client_exited = false;
2293
 
  bool quit_now = false;
2294
 
  __attribute__((cleanup(cleanup_close)))
2295
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2296
 
  g_assert_cmpint(epoll_fd, >=, 0);
2297
 
  __attribute__((cleanup(cleanup_queue)))
2298
 
    task_queue *queue = create_queue();
2299
 
  g_assert_nonnull(queue);
2300
 
  __attribute__((cleanup(cleanup_buffer)))
2301
 
    buffer password = {};
2302
 
  bool password_is_read = false;
2303
 
  const char helper_directory[] = "/nonexistent";
2304
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2305
 
  uid_t user = 1000;
2306
 
  gid_t group = 1001;
2307
 
 
2308
 
  const bool success = start_mandos_client(queue, epoll_fd,
2309
 
                                           &mandos_client_exited,
2310
 
                                           &quit_now, &password,
2311
 
                                           &password_is_read,
2312
 
                                           &fixture->orig_sigaction,
2313
 
                                           fixture->orig_sigmask,
2314
 
                                           helper_directory, user,
2315
 
                                           group, argv);
2316
 
  g_assert_true(success);
2317
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2318
 
 
2319
 
  struct timespec starttime, currtime;
2320
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2321
 
  do {
2322
 
    queue->next_run = 0;
2323
 
    string_set cancelled_filenames = {};
2324
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2325
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2326
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2327
 
  } while(((queue->length) > 0)
2328
 
          and (not quit_now)
2329
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2330
 
 
2331
 
  g_assert_false(quit_now);
2332
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2333
 
  g_assert_true(mandos_client_exited);
2334
 
 
2335
 
  g_assert_true(password_is_read);
2336
 
  g_assert_nonnull(password.data);
2337
 
 
2338
 
  uintmax_t id;
2339
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2340
 
                  ==, 1);
2341
 
  g_assert_true((uid_t)id == id);
2342
 
 
2343
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2344
 
}
2345
 
 
2346
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2347
 
                                               __attribute__((unused))
2348
 
                                               gconstpointer
2349
 
                                               user_data){
2350
 
  if(not is_privileged()){
2351
 
    g_test_skip("Not privileged");
2352
 
    return;
2353
 
  }
2354
 
 
2355
 
  bool mandos_client_exited = false;
2356
 
  bool quit_now = false;
2357
 
  __attribute__((cleanup(cleanup_close)))
2358
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2359
 
  g_assert_cmpint(epoll_fd, >=, 0);
2360
 
  __attribute__((cleanup(cleanup_queue)))
2361
 
    task_queue *queue = create_queue();
2362
 
  g_assert_nonnull(queue);
2363
 
  __attribute__((cleanup(cleanup_buffer)))
2364
 
    buffer password = {};
2365
 
  bool password_is_read = false;
2366
 
  const char helper_directory[] = "/nonexistent";
2367
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2368
 
  uid_t user = 1000;
2369
 
  gid_t group = 1001;
2370
 
 
2371
 
  const bool success = start_mandos_client(queue, epoll_fd,
2372
 
                                           &mandos_client_exited,
2373
 
                                           &quit_now, &password,
2374
 
                                           &password_is_read,
2375
 
                                           &fixture->orig_sigaction,
2376
 
                                           fixture->orig_sigmask,
2377
 
                                           helper_directory, user,
2378
 
                                           group, argv);
2379
 
  g_assert_true(success);
2380
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2381
 
 
2382
 
  struct timespec starttime, currtime;
2383
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2384
 
  do {
2385
 
    queue->next_run = 0;
2386
 
    string_set cancelled_filenames = {};
2387
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2388
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2389
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2390
 
  } while(((queue->length) > 0)
2391
 
          and (not quit_now)
2392
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2393
 
 
2394
 
  g_assert_false(quit_now);
2395
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2396
 
  g_assert_true(mandos_client_exited);
2397
 
 
2398
 
  g_assert_true(password_is_read);
2399
 
  g_assert_nonnull(password.data);
2400
 
 
2401
 
  uintmax_t id;
2402
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2403
 
                  ==, 1);
2404
 
  g_assert_true((gid_t)id == id);
2405
 
 
2406
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2407
 
}
2408
 
 
2409
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2410
 
                                               __attribute__((unused))
2411
 
                                               gconstpointer
2412
 
                                               user_data){
2413
 
  if(not is_privileged()){
2414
 
    g_test_skip("Not privileged");
2415
 
    return;
2416
 
  }
2417
 
 
2418
 
  bool mandos_client_exited = false;
2419
 
  bool quit_now = false;
2420
 
  __attribute__((cleanup(cleanup_close)))
2421
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2422
 
  g_assert_cmpint(epoll_fd, >=, 0);
2423
 
  __attribute__((cleanup(cleanup_queue)))
2424
 
    task_queue *queue = create_queue();
2425
 
  g_assert_nonnull(queue);
2426
 
  __attribute__((cleanup(cleanup_buffer)))
2427
 
    buffer password = {};
2428
 
  bool password_is_read = false;
2429
 
  const char helper_directory[] = "/nonexistent";
2430
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2431
 
    NULL };
2432
 
  uid_t user = 1000;
2433
 
  gid_t group = 1001;
2434
 
 
2435
 
  const bool success = start_mandos_client(queue, epoll_fd,
2436
 
                                           &mandos_client_exited,
2437
 
                                           &quit_now, &password,
2438
 
                                           &password_is_read,
2439
 
                                           &fixture->orig_sigaction,
2440
 
                                           fixture->orig_sigmask,
2441
 
                                           helper_directory, user,
2442
 
                                           group, argv);
2443
 
  g_assert_true(success);
2444
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2445
 
 
2446
 
  struct timespec starttime, currtime;
2447
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2448
 
  do {
2449
 
    queue->next_run = 0;
2450
 
    string_set cancelled_filenames = {};
2451
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2452
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2453
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2454
 
  } while(((queue->length) > 0)
2455
 
          and (not quit_now)
2456
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2457
 
 
2458
 
  g_assert_false(quit_now);
2459
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2460
 
  g_assert_true(mandos_client_exited);
2461
 
 
2462
 
  g_assert_true(password_is_read);
2463
 
  g_assert_nonnull(password.data);
2464
 
 
2465
 
  uintmax_t id;
2466
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2467
 
                  ==, 1);
2468
 
  g_assert_true((uid_t)id == id);
2469
 
 
2470
 
  g_assert_cmpuint((unsigned int)id, ==, user);
2471
 
}
2472
 
 
2473
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2474
 
                                               __attribute__((unused))
2475
 
                                               gconstpointer
2476
 
                                               user_data){
2477
 
  if(not is_privileged()){
2478
 
    g_test_skip("Not privileged");
2479
 
    return;
2480
 
  }
2481
 
 
2482
 
  bool mandos_client_exited = false;
2483
 
  bool quit_now = false;
2484
 
  __attribute__((cleanup(cleanup_close)))
2485
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2486
 
  g_assert_cmpint(epoll_fd, >=, 0);
2487
 
  __attribute__((cleanup(cleanup_queue)))
2488
 
    task_queue *queue = create_queue();
2489
 
  g_assert_nonnull(queue);
2490
 
  __attribute__((cleanup(cleanup_buffer)))
2491
 
    buffer password = {};
2492
 
  bool password_is_read = false;
2493
 
  const char helper_directory[] = "/nonexistent";
2494
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2495
 
    NULL };
2496
 
  uid_t user = 1000;
2497
 
  gid_t group = 1001;
2498
 
 
2499
 
  const bool success = start_mandos_client(queue, epoll_fd,
2500
 
                                           &mandos_client_exited,
2501
 
                                           &quit_now, &password,
2502
 
                                           &password_is_read,
2503
 
                                           &fixture->orig_sigaction,
2504
 
                                           fixture->orig_sigmask,
2505
 
                                           helper_directory, user,
2506
 
                                           group, argv);
2507
 
  g_assert_true(success);
2508
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2509
 
 
2510
 
  struct timespec starttime, currtime;
2511
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2512
 
  do {
2513
 
    queue->next_run = 0;
2514
 
    string_set cancelled_filenames = {};
2515
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2516
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2517
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2518
 
  } while(((queue->length) > 0)
2519
 
          and (not quit_now)
2520
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2521
 
 
2522
 
  g_assert_false(quit_now);
2523
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2524
 
  g_assert_true(mandos_client_exited);
2525
 
 
2526
 
  g_assert_true(password_is_read);
2527
 
  g_assert_nonnull(password.data);
2528
 
 
2529
 
  uintmax_t id;
2530
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2531
 
                  ==, 1);
2532
 
  g_assert_true((gid_t)id == id);
2533
 
 
2534
 
  g_assert_cmpuint((unsigned int)id, ==, group);
2535
 
}
2536
 
 
2537
 
static void test_start_mandos_client_read(test_fixture *fixture,
2538
 
                                          __attribute__((unused))
2539
 
                                          gconstpointer user_data){
2540
 
  bool mandos_client_exited = false;
2541
 
  bool quit_now = false;
2542
 
  __attribute__((cleanup(cleanup_close)))
2543
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2544
 
  g_assert_cmpint(epoll_fd, >=, 0);
2545
 
  __attribute__((cleanup(cleanup_queue)))
2546
 
    task_queue *queue = create_queue();
2547
 
  g_assert_nonnull(queue);
2548
 
  __attribute__((cleanup(cleanup_buffer)))
2549
 
    buffer password = {};
2550
 
  bool password_is_read = false;
2551
 
  const char dummy_test_password[] = "dummy test password";
2552
 
  const char helper_directory[] = "/nonexistent";
2553
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2554
 
    NULL };
2555
 
 
2556
 
  const bool success = start_mandos_client(queue, epoll_fd,
2557
 
                                           &mandos_client_exited,
2558
 
                                           &quit_now, &password,
2559
 
                                           &password_is_read,
2560
 
                                           &fixture->orig_sigaction,
2561
 
                                           fixture->orig_sigmask,
2562
 
                                           helper_directory, 0, 0,
2563
 
                                           argv);
2564
 
  g_assert_true(success);
2565
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2566
 
 
2567
 
  struct timespec starttime, currtime;
2568
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2569
 
  do {
2570
 
    queue->next_run = 0;
2571
 
    string_set cancelled_filenames = {};
2572
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2573
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2574
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2575
 
  } while(((queue->length) > 0)
2576
 
          and (not quit_now)
2577
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2578
 
 
2579
 
  g_assert_false(quit_now);
2580
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2581
 
  g_assert_true(mandos_client_exited);
2582
 
 
2583
 
  g_assert_true(password_is_read);
2584
 
  g_assert_cmpint((int)password.length, ==,
2585
 
                  sizeof(dummy_test_password)-1);
2586
 
  g_assert_nonnull(password.data);
2587
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
2588
 
                         sizeof(dummy_test_password)-1), ==, 0);
2589
 
}
2590
 
 
2591
 
static
2592
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2593
 
                                               __attribute__((unused))
2594
 
                                               gconstpointer
2595
 
                                               user_data){
2596
 
  bool mandos_client_exited = false;
2597
 
  bool quit_now = false;
2598
 
  __attribute__((cleanup(cleanup_close)))
2599
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2600
 
  g_assert_cmpint(epoll_fd, >=, 0);
2601
 
  __attribute__((cleanup(cleanup_queue)))
2602
 
    task_queue *queue = create_queue();
2603
 
  g_assert_nonnull(queue);
2604
 
  __attribute__((cleanup(cleanup_buffer)))
2605
 
    buffer password = {};
2606
 
  bool password_is_read = false;
2607
 
  const char helper_directory[] = "/nonexistent";
2608
 
  const char *const argv[] = { "/bin/sh", "-c",
2609
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2610
 
 
2611
 
  const bool success = start_mandos_client(queue, epoll_fd,
2612
 
                                           &mandos_client_exited,
2613
 
                                           &quit_now, &password,
2614
 
                                           &password_is_read,
2615
 
                                           &fixture->orig_sigaction,
2616
 
                                           fixture->orig_sigmask,
2617
 
                                           helper_directory, 0, 0,
2618
 
                                           argv);
2619
 
  g_assert_true(success);
2620
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2621
 
 
2622
 
  struct timespec starttime, currtime;
2623
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2624
 
  do {
2625
 
    queue->next_run = 0;
2626
 
    string_set cancelled_filenames = {};
2627
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2628
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2629
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2630
 
  } while(((queue->length) > 0)
2631
 
          and (not quit_now)
2632
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2633
 
 
2634
 
  g_assert_false(quit_now);
2635
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2636
 
  g_assert_true(mandos_client_exited);
2637
 
 
2638
 
  g_assert_true(password_is_read);
2639
 
  g_assert_cmpint((int)password.length, ==,
2640
 
                  sizeof(helper_directory)-1);
2641
 
  g_assert_nonnull(password.data);
2642
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
2643
 
                         sizeof(helper_directory)-1), ==, 0);
2644
 
}
2645
 
 
2646
 
__attribute__((nonnull, warn_unused_result))
2647
 
static bool proc_status_sigblk_to_sigset(const char *const,
2648
 
                                         sigset_t *const);
2649
 
 
2650
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2651
 
                                             __attribute__((unused))
2652
 
                                             gconstpointer user_data){
2653
 
  bool mandos_client_exited = false;
2654
 
  bool quit_now = false;
2655
 
  __attribute__((cleanup(cleanup_close)))
2656
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2657
 
  g_assert_cmpint(epoll_fd, >=, 0);
2658
 
  __attribute__((cleanup(cleanup_queue)))
2659
 
    task_queue *queue = create_queue();
2660
 
  g_assert_nonnull(queue);
2661
 
  __attribute__((cleanup(cleanup_buffer)))
2662
 
    buffer password = {};
2663
 
  bool password_is_read = false;
2664
 
  const char helper_directory[] = "/nonexistent";
2665
 
  /* see proc(5) for format of /proc/self/status */
2666
 
  const char *const argv[] = { "/usr/bin/awk",
2667
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2668
 
 
2669
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
2670
 
                                    &mandos_client_exited, &quit_now,
2671
 
                                    &password, &password_is_read,
2672
 
                                    &fixture->orig_sigaction,
2673
 
                                    fixture->orig_sigmask,
2674
 
                                    helper_directory, 0, 0, argv));
2675
 
 
2676
 
  struct timespec starttime, currtime;
2677
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2678
 
  do {
2679
 
    queue->next_run = 0;
2680
 
    string_set cancelled_filenames = {};
2681
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2682
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2683
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2684
 
  } while((not (mandos_client_exited and password_is_read))
2685
 
          and (not quit_now)
2686
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2687
 
  g_assert_true(mandos_client_exited);
2688
 
  g_assert_true(password_is_read);
2689
 
 
2690
 
  sigset_t parsed_sigmask;
2691
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
2692
 
                                             &parsed_sigmask));
2693
 
 
2694
 
  for(int signum = 1; signum < NSIG; signum++){
2695
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
2696
 
    if(sigismember(&fixture->orig_sigmask, signum)){
2697
 
      g_assert_true(has_signal);
2698
 
    } else {
2699
 
      g_assert_false(has_signal);
2700
 
    }
2701
 
  }
2702
 
}
2703
 
 
2704
 
__attribute__((nonnull, warn_unused_result))
2705
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2706
 
                                         sigset_t *const sigmask){
2707
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2708
 
  uintmax_t scanned_sigmask;
2709
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2710
 
    return false;
2711
 
  }
2712
 
  if(sigemptyset(sigmask) != 0){
2713
 
    return false;
2714
 
  }
2715
 
  for(int signum = 1; signum < NSIG; signum++){
2716
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2717
 
      if(sigaddset(sigmask, signum) != 0){
2718
 
        return false;
2719
 
      }
2720
 
    }
2721
 
  }
2722
 
  return true;
2723
 
}
2724
 
 
2725
 
static void run_task_with_stderr_to_dev_null(const task_context task,
2726
 
                                             task_queue *const queue);
2727
 
 
2728
 
static
2729
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2730
 
                                             test_fixture *fixture,
2731
 
                                             __attribute__((unused))
2732
 
                                             gconstpointer user_data){
2733
 
 
2734
 
  bool mandos_client_exited = false;
2735
 
  bool quit_now = false;
2736
 
 
2737
 
  __attribute__((cleanup(cleanup_queue)))
2738
 
    task_queue *queue = create_queue();
2739
 
  g_assert_nonnull(queue);
2740
 
  const task_context task = {
2741
 
    .func=wait_for_mandos_client_exit,
2742
 
    .pid=1,
2743
 
    .mandos_client_exited=&mandos_client_exited,
2744
 
    .quit_now=&quit_now,
2745
 
  };
2746
 
  run_task_with_stderr_to_dev_null(task, queue);
2747
 
 
2748
 
  g_assert_false(mandos_client_exited);
2749
 
  g_assert_true(quit_now);
2750
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2751
 
}
2752
 
 
2753
 
static void run_task_with_stderr_to_dev_null(const task_context task,
2754
 
                                             task_queue *const queue){
2755
 
  FILE *real_stderr = stderr;
2756
 
  FILE *devnull = fopen("/dev/null", "we");
2757
 
  g_assert_nonnull(devnull);
2758
 
 
2759
 
  stderr = devnull;
2760
 
  task.func(task, queue);
2761
 
  stderr = real_stderr;
2762
 
 
2763
 
  g_assert_cmpint(fclose(devnull), ==, 0);
2764
 
}
2765
 
 
2766
 
static
2767
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2768
 
                                             __attribute__((unused))
2769
 
                                             gconstpointer user_data){
2770
 
  bool mandos_client_exited = false;
2771
 
  bool quit_now = false;
2772
 
 
2773
 
  pid_t create_eternal_process(void){
2774
 
    const pid_t pid = fork();
2775
 
    if(pid == 0){               /* Child */
2776
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2777
 
        _exit(EXIT_FAILURE);
2778
 
      }
2779
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2780
 
        _exit(EXIT_FAILURE);
2781
 
      }
2782
 
      while(true){
2783
 
        pause();
2784
 
      }
2785
 
    }
2786
 
    return pid;
2787
 
  }
2788
 
  pid_t pid = create_eternal_process();
2789
 
  g_assert_true(pid != -1);
2790
 
 
2791
 
  __attribute__((cleanup(cleanup_queue)))
2792
 
    task_queue *queue = create_queue();
2793
 
  g_assert_nonnull(queue);
2794
 
  const task_context task = {
2795
 
    .func=wait_for_mandos_client_exit,
2796
 
    .pid=pid,
2797
 
    .mandos_client_exited=&mandos_client_exited,
2798
 
    .quit_now=&quit_now,
2799
 
  };
2800
 
  task.func(task, queue);
2801
 
 
2802
 
  g_assert_false(mandos_client_exited);
2803
 
  g_assert_false(quit_now);
2804
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2805
 
 
2806
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
2807
 
        .func=wait_for_mandos_client_exit,
2808
 
        .pid=task.pid,
2809
 
        .mandos_client_exited=&mandos_client_exited,
2810
 
        .quit_now=&quit_now,
2811
 
      }));
2812
 
}
2813
 
 
2814
 
static
2815
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2816
 
                                              __attribute__((unused))
2817
 
                                              gconstpointer
2818
 
                                              user_data){
2819
 
  bool mandos_client_exited = false;
2820
 
  bool quit_now = false;
2821
 
 
2822
 
  pid_t create_successful_process(void){
2823
 
    const pid_t pid = fork();
2824
 
    if(pid == 0){               /* Child */
2825
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2826
 
        _exit(EXIT_FAILURE);
2827
 
      }
2828
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2829
 
        _exit(EXIT_FAILURE);
2830
 
      }
2831
 
      exit(EXIT_SUCCESS);
2832
 
    }
2833
 
    return pid;
2834
 
  }
2835
 
  const pid_t pid = create_successful_process();
2836
 
  g_assert_true(pid != -1);
2837
 
 
2838
 
  __attribute__((cleanup(cleanup_queue)))
2839
 
    task_queue *queue = create_queue();
2840
 
  g_assert_nonnull(queue);
2841
 
  const task_context initial_task = {
2842
 
    .func=wait_for_mandos_client_exit,
2843
 
    .pid=pid,
2844
 
    .mandos_client_exited=&mandos_client_exited,
2845
 
    .quit_now=&quit_now,
2846
 
  };
2847
 
  g_assert_true(add_to_queue(queue, initial_task));
2848
 
 
2849
 
  struct timespec starttime, currtime;
2850
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2851
 
  __attribute__((cleanup(cleanup_close)))
2852
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2853
 
  do {
2854
 
    queue->next_run = 0;
2855
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2856
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2857
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2858
 
  } while((not mandos_client_exited)
2859
 
          and (not quit_now)
2860
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2861
 
 
2862
 
  g_assert_true(mandos_client_exited);
2863
 
  g_assert_false(quit_now);
2864
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2865
 
}
2866
 
 
2867
 
static
2868
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2869
 
                                              __attribute__((unused))
2870
 
                                              gconstpointer
2871
 
                                              user_data){
2872
 
  bool mandos_client_exited = false;
2873
 
  bool quit_now = false;
2874
 
 
2875
 
  pid_t create_failing_process(void){
2876
 
    const pid_t pid = fork();
2877
 
    if(pid == 0){               /* Child */
2878
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2879
 
        _exit(EXIT_FAILURE);
2880
 
      }
2881
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2882
 
        _exit(EXIT_FAILURE);
2883
 
      }
2884
 
      exit(EXIT_FAILURE);
2885
 
    }
2886
 
    return pid;
2887
 
  }
2888
 
  const pid_t pid = create_failing_process();
2889
 
  g_assert_true(pid != -1);
2890
 
 
2891
 
  __attribute__((cleanup(string_set_clear)))
2892
 
    string_set cancelled_filenames = {};
2893
 
  __attribute__((cleanup(cleanup_close)))
2894
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2895
 
  g_assert_cmpint(epoll_fd, >=, 0);
2896
 
  __attribute__((cleanup(cleanup_queue)))
2897
 
    task_queue *queue = create_queue();
2898
 
  g_assert_nonnull(queue);
2899
 
  g_assert_true(add_to_queue(queue, (task_context){
2900
 
        .func=wait_for_mandos_client_exit,
2901
 
        .pid=pid,
2902
 
        .mandos_client_exited=&mandos_client_exited,
2903
 
        .quit_now=&quit_now,
2904
 
      }));
2905
 
 
2906
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2907
 
 
2908
 
  __attribute__((cleanup(cleanup_close)))
2909
 
    const int devnull_fd = open("/dev/null",
2910
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
2911
 
  g_assert_cmpint(devnull_fd, >=, 0);
2912
 
  __attribute__((cleanup(cleanup_close)))
2913
 
    const int real_stderr_fd = dup(STDERR_FILENO);
2914
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
2915
 
 
2916
 
  struct timespec starttime, currtime;
2917
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2918
 
  do {
2919
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2920
 
    dup2(devnull_fd, STDERR_FILENO);
2921
 
    const bool success = run_queue(&queue, &cancelled_filenames,
2922
 
                                   &quit_now);
2923
 
    dup2(real_stderr_fd, STDERR_FILENO);
2924
 
    if(not success){
2925
 
      break;
2926
 
    }
2927
 
 
2928
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2929
 
  } while((not mandos_client_exited)
2930
 
          and (not quit_now)
2931
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2932
 
 
2933
 
  g_assert_true(quit_now);
2934
 
  g_assert_true(mandos_client_exited);
2935
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2936
 
}
2937
 
 
2938
 
static
2939
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2940
 
                                             __attribute__((unused))
2941
 
                                             gconstpointer user_data){
2942
 
  bool mandos_client_exited = false;
2943
 
  bool quit_now = false;
2944
 
 
2945
 
  pid_t create_killed_process(void){
2946
 
    const pid_t pid = fork();
2947
 
    if(pid == 0){               /* Child */
2948
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2949
 
        _exit(EXIT_FAILURE);
2950
 
      }
2951
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2952
 
        _exit(EXIT_FAILURE);
2953
 
      }
2954
 
      while(true){
2955
 
        pause();
2956
 
      }
2957
 
    }
2958
 
    kill(pid, SIGKILL);
2959
 
    return pid;
2960
 
  }
2961
 
  const pid_t pid = create_killed_process();
2962
 
  g_assert_true(pid != -1);
2963
 
 
2964
 
  __attribute__((cleanup(string_set_clear)))
2965
 
    string_set cancelled_filenames = {};
2966
 
  __attribute__((cleanup(cleanup_close)))
2967
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2968
 
  g_assert_cmpint(epoll_fd, >=, 0);
2969
 
  __attribute__((cleanup(cleanup_queue)))
2970
 
    task_queue *queue = create_queue();
2971
 
  g_assert_nonnull(queue);
2972
 
  g_assert_true(add_to_queue(queue, (task_context){
2973
 
        .func=wait_for_mandos_client_exit,
2974
 
        .pid=pid,
2975
 
        .mandos_client_exited=&mandos_client_exited,
2976
 
        .quit_now=&quit_now,
2977
 
      }));
2978
 
 
2979
 
  __attribute__((cleanup(cleanup_close)))
2980
 
    const int devnull_fd = open("/dev/null",
2981
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
2982
 
  g_assert_cmpint(devnull_fd, >=, 0);
2983
 
  __attribute__((cleanup(cleanup_close)))
2984
 
    const int real_stderr_fd = dup(STDERR_FILENO);
2985
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
2986
 
 
2987
 
  struct timespec starttime, currtime;
2988
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2989
 
  do {
2990
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2991
 
    dup2(devnull_fd, STDERR_FILENO);
2992
 
    const bool success = run_queue(&queue, &cancelled_filenames,
2993
 
                                   &quit_now);
2994
 
    dup2(real_stderr_fd, STDERR_FILENO);
2995
 
    if(not success){
2996
 
      break;
2997
 
    }
2998
 
 
2999
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3000
 
  } while((not mandos_client_exited)
3001
 
          and (not quit_now)
3002
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
3003
 
 
3004
 
  g_assert_true(mandos_client_exited);
3005
 
  g_assert_true(quit_now);
3006
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3007
 
}
3008
 
 
3009
 
static bool epoll_set_does_not_contain(int, int);
3010
 
 
3011
 
static
3012
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
3013
 
                                              test_fixture *fixture,
3014
 
                                              __attribute__((unused))
3015
 
                                              gconstpointer
3016
 
                                              user_data){
3017
 
  __attribute__((cleanup(cleanup_close)))
3018
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3019
 
  g_assert_cmpint(epoll_fd, >=, 0);
3020
 
 
3021
 
  __attribute__((cleanup(cleanup_buffer)))
3022
 
    buffer password = {};
3023
 
 
3024
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3025
 
  const int fd = open("/proc/self/mem",
3026
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3027
 
 
3028
 
  bool password_is_read = false;
3029
 
  bool quit_now = false;
3030
 
  __attribute__((cleanup(cleanup_queue)))
3031
 
    task_queue *queue = create_queue();
3032
 
  g_assert_nonnull(queue);
3033
 
 
3034
 
  task_context task = {
3035
 
    .func=read_mandos_client_output,
3036
 
    .epoll_fd=epoll_fd,
3037
 
    .fd=fd,
3038
 
    .password=&password,
3039
 
    .password_is_read=&password_is_read,
3040
 
    .quit_now=&quit_now,
3041
 
  };
3042
 
  run_task_with_stderr_to_dev_null(task, queue);
3043
 
  g_assert_false(password_is_read);
3044
 
  g_assert_cmpint((int)password.length, ==, 0);
3045
 
  g_assert_true(quit_now);
3046
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3047
 
 
3048
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3049
 
 
3050
 
  g_assert_cmpint(close(fd), ==, -1);
3051
 
}
3052
 
 
3053
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3054
 
  return not epoll_set_contains(epoll_fd, fd, 0);
3055
 
}
3056
 
 
3057
 
static
3058
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
3059
 
                                           test_fixture *fixture,
3060
 
                                           __attribute__((unused))
3061
 
                                           gconstpointer user_data){
3062
 
  __attribute__((cleanup(cleanup_close)))
3063
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3064
 
  g_assert_cmpint(epoll_fd, >=, 0);
3065
 
 
3066
 
  int pipefds[2];
3067
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3068
 
 
3069
 
  __attribute__((cleanup(cleanup_buffer)))
3070
 
    buffer password = {};
3071
 
 
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);
3077
 
 
3078
 
  task_context task = {
3079
 
    .func=read_mandos_client_output,
3080
 
    .epoll_fd=epoll_fd,
3081
 
    .fd=pipefds[0],
3082
 
    .password=&password,
3083
 
    .password_is_read=&password_is_read,
3084
 
    .quit_now=&quit_now,
3085
 
  };
3086
 
  task.func(task, queue);
3087
 
  g_assert_false(password_is_read);
3088
 
  g_assert_cmpint((int)password.length, ==, 0);
3089
 
  g_assert_false(quit_now);
3090
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3091
 
 
3092
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3093
 
        .func=read_mandos_client_output,
3094
 
        .epoll_fd=epoll_fd,
3095
 
        .fd=pipefds[0],
3096
 
        .password=&password,
3097
 
        .password_is_read=&password_is_read,
3098
 
        .quit_now=&quit_now,
3099
 
      }));
3100
 
 
3101
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3102
 
                                   EPOLLIN | EPOLLRDHUP));
3103
 
 
3104
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3105
 
}
3106
 
 
3107
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
3108
 
                                               test_fixture *fixture,
3109
 
                                               __attribute__((unused))
3110
 
                                               gconstpointer
3111
 
                                               user_data){
3112
 
  __attribute__((cleanup(cleanup_close)))
3113
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3114
 
  g_assert_cmpint(epoll_fd, >=, 0);
3115
 
 
3116
 
  int pipefds[2];
3117
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3118
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3119
 
 
3120
 
  __attribute__((cleanup(cleanup_buffer)))
3121
 
    buffer password = {};
3122
 
 
3123
 
  bool password_is_read = false;
3124
 
  bool quit_now = false;
3125
 
  __attribute__((cleanup(cleanup_queue)))
3126
 
    task_queue *queue = create_queue();
3127
 
  g_assert_nonnull(queue);
3128
 
 
3129
 
  task_context task = {
3130
 
    .func=read_mandos_client_output,
3131
 
    .epoll_fd=epoll_fd,
3132
 
    .fd=pipefds[0],
3133
 
    .password=&password,
3134
 
    .password_is_read=&password_is_read,
3135
 
    .quit_now=&quit_now,
3136
 
  };
3137
 
  task.func(task, queue);
3138
 
  g_assert_true(password_is_read);
3139
 
  g_assert_cmpint((int)password.length, ==, 0);
3140
 
  g_assert_false(quit_now);
3141
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3142
 
 
3143
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3144
 
 
3145
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
3146
 
}
3147
 
 
3148
 
static
3149
 
void test_read_mandos_client_output_once(__attribute__((unused))
3150
 
                                         test_fixture *fixture,
3151
 
                                         __attribute__((unused))
3152
 
                                         gconstpointer user_data){
3153
 
  __attribute__((cleanup(cleanup_close)))
3154
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3155
 
  g_assert_cmpint(epoll_fd, >=, 0);
3156
 
 
3157
 
  int pipefds[2];
3158
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3159
 
 
3160
 
  const char dummy_test_password[] = "dummy test password";
3161
 
  /* Start with a pre-allocated buffer */
3162
 
  __attribute__((cleanup(cleanup_buffer)))
3163
 
    buffer password = {
3164
 
    .data=malloc(sizeof(dummy_test_password)),
3165
 
    .length=0,
3166
 
    .allocated=sizeof(dummy_test_password),
3167
 
  };
3168
 
  g_assert_nonnull(password.data);
3169
 
  if(mlock(password.data, password.allocated) != 0){
3170
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3171
 
  }
3172
 
 
3173
 
  bool password_is_read = false;
3174
 
  bool quit_now = false;
3175
 
  __attribute__((cleanup(cleanup_queue)))
3176
 
    task_queue *queue = create_queue();
3177
 
  g_assert_nonnull(queue);
3178
 
 
3179
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3180
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3181
 
                             sizeof(dummy_test_password)),
3182
 
                  ==, (int)sizeof(dummy_test_password));
3183
 
 
3184
 
  task_context task = {
3185
 
    .func=read_mandos_client_output,
3186
 
    .epoll_fd=epoll_fd,
3187
 
    .fd=pipefds[0],
3188
 
    .password=&password,
3189
 
    .password_is_read=&password_is_read,
3190
 
    .quit_now=&quit_now,
3191
 
  };
3192
 
  task.func(task, queue);
3193
 
 
3194
 
  g_assert_false(password_is_read);
3195
 
  g_assert_cmpint((int)password.length, ==,
3196
 
                  (int)sizeof(dummy_test_password));
3197
 
  g_assert_nonnull(password.data);
3198
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
3199
 
                         sizeof(dummy_test_password)), ==, 0);
3200
 
 
3201
 
  g_assert_false(quit_now);
3202
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3203
 
 
3204
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3205
 
        .func=read_mandos_client_output,
3206
 
        .epoll_fd=epoll_fd,
3207
 
        .fd=pipefds[0],
3208
 
        .password=&password,
3209
 
        .password_is_read=&password_is_read,
3210
 
        .quit_now=&quit_now,
3211
 
      }));
3212
 
 
3213
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3214
 
                                   EPOLLIN | EPOLLRDHUP));
3215
 
 
3216
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3217
 
}
3218
 
 
3219
 
static
3220
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
3221
 
                                           test_fixture *fixture,
3222
 
                                           __attribute__((unused))
3223
 
                                           gconstpointer user_data){
3224
 
  __attribute__((cleanup(cleanup_close)))
3225
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3226
 
  g_assert_cmpint(epoll_fd, >=, 0);
3227
 
 
3228
 
  int pipefds[2];
3229
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3230
 
 
3231
 
  const char dummy_test_password[] = "dummy test password";
3232
 
  /* Start with an empty buffer */
3233
 
  __attribute__((cleanup(cleanup_buffer)))
3234
 
    buffer password = {};
3235
 
 
3236
 
  bool password_is_read = false;
3237
 
  bool quit_now = false;
3238
 
  __attribute__((cleanup(cleanup_queue)))
3239
 
    task_queue *queue = create_queue();
3240
 
  g_assert_nonnull(queue);
3241
 
 
3242
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3243
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3244
 
                             sizeof(dummy_test_password)),
3245
 
                  ==, (int)sizeof(dummy_test_password));
3246
 
 
3247
 
  task_context task = {
3248
 
    .func=read_mandos_client_output,
3249
 
    .epoll_fd=epoll_fd,
3250
 
    .fd=pipefds[0],
3251
 
    .password=&password,
3252
 
    .password_is_read=&password_is_read,
3253
 
    .quit_now=&quit_now,
3254
 
  };
3255
 
  task.func(task, queue);
3256
 
 
3257
 
  g_assert_false(password_is_read);
3258
 
  g_assert_cmpint((int)password.length, ==,
3259
 
                  (int)sizeof(dummy_test_password));
3260
 
  g_assert_nonnull(password.data);
3261
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
3262
 
                         sizeof(dummy_test_password)), ==, 0);
3263
 
 
3264
 
  g_assert_false(quit_now);
3265
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3266
 
 
3267
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3268
 
        .func=read_mandos_client_output,
3269
 
        .epoll_fd=epoll_fd,
3270
 
        .fd=pipefds[0],
3271
 
        .password=&password,
3272
 
        .password_is_read=&password_is_read,
3273
 
        .quit_now=&quit_now,
3274
 
      }));
3275
 
 
3276
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3277
 
                                   EPOLLIN | EPOLLRDHUP));
3278
 
 
3279
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3280
 
}
3281
 
 
3282
 
static
3283
 
void test_read_mandos_client_output_append(__attribute__((unused))
3284
 
                                           test_fixture *fixture,
3285
 
                                           __attribute__((unused))
3286
 
                                           gconstpointer user_data){
3287
 
  __attribute__((cleanup(cleanup_close)))
3288
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3289
 
  g_assert_cmpint(epoll_fd, >=, 0);
3290
 
 
3291
 
  int pipefds[2];
3292
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3293
 
 
3294
 
  const char dummy_test_password[] = "dummy test password";
3295
 
  __attribute__((cleanup(cleanup_buffer)))
3296
 
    buffer password = {
3297
 
    .data=malloc(PIPE_BUF),
3298
 
    .length=PIPE_BUF,
3299
 
    .allocated=PIPE_BUF,
3300
 
  };
3301
 
  g_assert_nonnull(password.data);
3302
 
  if(mlock(password.data, password.allocated) != 0){
3303
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3304
 
  }
3305
 
 
3306
 
  memset(password.data, 'x', PIPE_BUF);
3307
 
  char password_expected[PIPE_BUF];
3308
 
  memcpy(password_expected, password.data, PIPE_BUF);
3309
 
 
3310
 
  bool password_is_read = false;
3311
 
  bool quit_now = false;
3312
 
  __attribute__((cleanup(cleanup_queue)))
3313
 
    task_queue *queue = create_queue();
3314
 
  g_assert_nonnull(queue);
3315
 
 
3316
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3317
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3318
 
                             sizeof(dummy_test_password)),
3319
 
                  ==, (int)sizeof(dummy_test_password));
3320
 
 
3321
 
  task_context task = {
3322
 
    .func=read_mandos_client_output,
3323
 
    .epoll_fd=epoll_fd,
3324
 
    .fd=pipefds[0],
3325
 
    .password=&password,
3326
 
    .password_is_read=&password_is_read,
3327
 
    .quit_now=&quit_now,
3328
 
  };
3329
 
  task.func(task, queue);
3330
 
 
3331
 
  g_assert_false(password_is_read);
3332
 
  g_assert_cmpint((int)password.length, ==,
3333
 
                  PIPE_BUF + sizeof(dummy_test_password));
3334
 
  g_assert_nonnull(password.data);
3335
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3336
 
                  ==, 0);
3337
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3338
 
                         dummy_test_password,
3339
 
                         sizeof(dummy_test_password)), ==, 0);
3340
 
  g_assert_false(quit_now);
3341
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3342
 
 
3343
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3344
 
        .func=read_mandos_client_output,
3345
 
        .epoll_fd=epoll_fd,
3346
 
        .fd=pipefds[0],
3347
 
        .password=&password,
3348
 
        .password_is_read=&password_is_read,
3349
 
        .quit_now=&quit_now,
3350
 
      }));
3351
 
 
3352
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3353
 
                                   EPOLLIN | EPOLLRDHUP));
3354
 
}
3355
 
 
3356
 
static char *make_temporary_directory(void);
3357
 
 
3358
 
static void test_add_inotify_dir_watch(__attribute__((unused))
3359
 
                                       test_fixture *fixture,
3360
 
                                       __attribute__((unused))
3361
 
                                       gconstpointer user_data){
3362
 
  __attribute__((cleanup(cleanup_close)))
3363
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3364
 
  g_assert_cmpint(epoll_fd, >=, 0);
3365
 
  __attribute__((cleanup(cleanup_queue)))
3366
 
    task_queue *queue = create_queue();
3367
 
  g_assert_nonnull(queue);
3368
 
  __attribute__((cleanup(string_set_clear)))
3369
 
    string_set cancelled_filenames = {};
3370
 
  const mono_microsecs current_time = 0;
3371
 
 
3372
 
  bool quit_now = false;
3373
 
  buffer password = {};
3374
 
  bool mandos_client_exited = false;
3375
 
  bool password_is_read = false;
3376
 
 
3377
 
  __attribute__((cleanup(cleanup_string)))
3378
 
    char *tempdir = make_temporary_directory();
3379
 
  g_assert_nonnull(tempdir);
3380
 
 
3381
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3382
 
                                      &password, tempdir,
3383
 
                                      &cancelled_filenames,
3384
 
                                      &current_time,
3385
 
                                      &mandos_client_exited,
3386
 
                                      &password_is_read));
3387
 
 
3388
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3389
 
 
3390
 
  const task_context *const added_read_task
3391
 
    = find_matching_task(queue, (task_context){
3392
 
        .func=read_inotify_event,
3393
 
        .epoll_fd=epoll_fd,
3394
 
        .quit_now=&quit_now,
3395
 
        .password=&password,
3396
 
        .filename=tempdir,
3397
 
        .cancelled_filenames=&cancelled_filenames,
3398
 
        .current_time=&current_time,
3399
 
        .mandos_client_exited=&mandos_client_exited,
3400
 
        .password_is_read=&password_is_read,
3401
 
      });
3402
 
  g_assert_nonnull(added_read_task);
3403
 
 
3404
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3405
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3406
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3407
 
                                   added_read_task->fd,
3408
 
                                   EPOLLIN | EPOLLRDHUP));
3409
 
 
3410
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3411
 
}
3412
 
 
3413
 
static char *make_temporary_directory(void){
3414
 
  char *name = strdup("/tmp/mandosXXXXXX");
3415
 
  g_assert_nonnull(name);
3416
 
  char *result = mkdtemp(name);
3417
 
  if(result == NULL){
3418
 
    free(name);
3419
 
  }
3420
 
  return result;
3421
 
}
3422
 
 
3423
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3424
 
                                            test_fixture *fixture,
3425
 
                                            __attribute__((unused))
3426
 
                                            gconstpointer user_data){
3427
 
  __attribute__((cleanup(cleanup_close)))
3428
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3429
 
  g_assert_cmpint(epoll_fd, >=, 0);
3430
 
  __attribute__((cleanup(cleanup_queue)))
3431
 
    task_queue *queue = create_queue();
3432
 
  g_assert_nonnull(queue);
3433
 
  __attribute__((cleanup(string_set_clear)))
3434
 
    string_set cancelled_filenames = {};
3435
 
  const mono_microsecs current_time = 0;
3436
 
 
3437
 
  bool quit_now = false;
3438
 
  buffer password = {};
3439
 
  bool mandos_client_exited = false;
3440
 
  bool password_is_read = false;
3441
 
 
3442
 
  const char nonexistent_dir[] = "/nonexistent";
3443
 
 
3444
 
  FILE *real_stderr = stderr;
3445
 
  FILE *devnull = fopen("/dev/null", "we");
3446
 
  g_assert_nonnull(devnull);
3447
 
  stderr = devnull;
3448
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3449
 
                                       &password, nonexistent_dir,
3450
 
                                       &cancelled_filenames,
3451
 
                                       &current_time,
3452
 
                                       &mandos_client_exited,
3453
 
                                       &password_is_read));
3454
 
  stderr = real_stderr;
3455
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3456
 
 
3457
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3458
 
}
3459
 
 
3460
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3461
 
                                              test_fixture *fixture,
3462
 
                                            __attribute__((unused))
3463
 
                                              gconstpointer
3464
 
                                              user_data){
3465
 
  __attribute__((cleanup(cleanup_close)))
3466
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3467
 
  g_assert_cmpint(epoll_fd, >=, 0);
3468
 
  __attribute__((cleanup(cleanup_queue)))
3469
 
    task_queue *queue = create_queue();
3470
 
  g_assert_nonnull(queue);
3471
 
  __attribute__((cleanup(string_set_clear)))
3472
 
    string_set cancelled_filenames = {};
3473
 
  const mono_microsecs current_time = 0;
3474
 
 
3475
 
  bool quit_now = false;
3476
 
  buffer password = {};
3477
 
  bool mandos_client_exited = false;
3478
 
  bool password_is_read = false;
3479
 
 
3480
 
  const char not_a_directory[] = "/dev/tty";
3481
 
 
3482
 
  FILE *real_stderr = stderr;
3483
 
  FILE *devnull = fopen("/dev/null", "we");
3484
 
  g_assert_nonnull(devnull);
3485
 
  stderr = devnull;
3486
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3487
 
                                       &password, not_a_directory,
3488
 
                                       &cancelled_filenames,
3489
 
                                       &current_time,
3490
 
                                       &mandos_client_exited,
3491
 
                                       &password_is_read));
3492
 
  stderr = real_stderr;
3493
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3494
 
 
3495
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3496
 
}
3497
 
 
3498
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3499
 
                                              test_fixture *fixture,
3500
 
                                              __attribute__((unused))
3501
 
                                              gconstpointer
3502
 
                                              user_data){
3503
 
  __attribute__((cleanup(cleanup_close)))
3504
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3505
 
  g_assert_cmpint(epoll_fd, >=, 0);
3506
 
  __attribute__((cleanup(cleanup_queue)))
3507
 
    task_queue *queue = create_queue();
3508
 
  g_assert_nonnull(queue);
3509
 
  __attribute__((cleanup(string_set_clear)))
3510
 
    string_set cancelled_filenames = {};
3511
 
  const mono_microsecs current_time = 0;
3512
 
 
3513
 
  bool quit_now = false;
3514
 
  buffer password = {};
3515
 
  bool mandos_client_exited = false;
3516
 
  bool password_is_read = false;
3517
 
 
3518
 
  __attribute__((cleanup(cleanup_string)))
3519
 
    char *tempdir = make_temporary_directory();
3520
 
  g_assert_nonnull(tempdir);
3521
 
 
3522
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3523
 
                                      &password, tempdir,
3524
 
                                      &cancelled_filenames,
3525
 
                                      &current_time,
3526
 
                                      &mandos_client_exited,
3527
 
                                      &password_is_read));
3528
 
 
3529
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3530
 
 
3531
 
  const task_context *const added_read_task
3532
 
    = find_matching_task(queue,
3533
 
                         (task_context){ .func=read_inotify_event });
3534
 
  g_assert_nonnull(added_read_task);
3535
 
 
3536
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3537
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3538
 
 
3539
 
  /* "sufficient to read at least one event." - inotify(7) */
3540
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3541
 
                              + NAME_MAX + 1);
3542
 
  struct inotify_event *ievent = malloc(ievent_size);
3543
 
  g_assert_nonnull(ievent);
3544
 
 
3545
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3546
 
                  -1);
3547
 
  g_assert_cmpint(errno, ==, EAGAIN);
3548
 
 
3549
 
  free(ievent);
3550
 
 
3551
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3552
 
}
3553
 
 
3554
 
static char *make_temporary_file_in_directory(const char
3555
 
                                              *const dir);
3556
 
 
3557
 
static
3558
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3559
 
                                               test_fixture *fixture,
3560
 
                                               __attribute__((unused))
3561
 
                                               gconstpointer
3562
 
                                               user_data){
3563
 
  __attribute__((cleanup(cleanup_close)))
3564
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3565
 
  g_assert_cmpint(epoll_fd, >=, 0);
3566
 
  __attribute__((cleanup(cleanup_queue)))
3567
 
    task_queue *queue = create_queue();
3568
 
  g_assert_nonnull(queue);
3569
 
  __attribute__((cleanup(string_set_clear)))
3570
 
    string_set cancelled_filenames = {};
3571
 
  const mono_microsecs current_time = 0;
3572
 
 
3573
 
  bool quit_now = false;
3574
 
  buffer password = {};
3575
 
  bool mandos_client_exited = false;
3576
 
  bool password_is_read = false;
3577
 
 
3578
 
  __attribute__((cleanup(cleanup_string)))
3579
 
    char *tempdir = make_temporary_directory();
3580
 
  g_assert_nonnull(tempdir);
3581
 
 
3582
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3583
 
                                      &password, tempdir,
3584
 
                                      &cancelled_filenames,
3585
 
                                      &current_time,
3586
 
                                      &mandos_client_exited,
3587
 
                                      &password_is_read));
3588
 
 
3589
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3590
 
 
3591
 
  const task_context *const added_read_task
3592
 
    = find_matching_task(queue,
3593
 
                         (task_context){ .func=read_inotify_event });
3594
 
  g_assert_nonnull(added_read_task);
3595
 
 
3596
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3597
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3598
 
 
3599
 
  __attribute__((cleanup(cleanup_string)))
3600
 
    char *filename = make_temporary_file_in_directory(tempdir);
3601
 
  g_assert_nonnull(filename);
3602
 
 
3603
 
  /* "sufficient to read at least one event." - inotify(7) */
3604
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3605
 
                              + NAME_MAX + 1);
3606
 
  struct inotify_event *ievent = malloc(ievent_size);
3607
 
  g_assert_nonnull(ievent);
3608
 
 
3609
 
  ssize_t read_size = 0;
3610
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3611
 
 
3612
 
  g_assert_cmpint((int)read_size, >, 0);
3613
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3614
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
3615
 
 
3616
 
  free(ievent);
3617
 
 
3618
 
  g_assert_cmpint(unlink(filename), ==, 0);
3619
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3620
 
}
3621
 
 
3622
 
static char *make_temporary_prefixed_file_in_directory(const char
3623
 
                                                       *const prefix,
3624
 
                                                       const char
3625
 
                                                       *const dir){
3626
 
  char *filename = NULL;
3627
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3628
 
                  >, 0);
3629
 
  g_assert_nonnull(filename);
3630
 
  const int fd = mkostemp(filename, O_CLOEXEC);
3631
 
  g_assert_cmpint(fd, >=, 0);
3632
 
  g_assert_cmpint(close(fd), ==, 0);
3633
 
  return filename;
3634
 
}
3635
 
 
3636
 
static char *make_temporary_file_in_directory(const char
3637
 
                                              *const dir){
3638
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
3639
 
}
3640
 
 
3641
 
static
3642
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3643
 
                                            test_fixture *fixture,
3644
 
                                            __attribute__((unused))
3645
 
                                            gconstpointer user_data){
3646
 
  __attribute__((cleanup(cleanup_close)))
3647
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3648
 
  g_assert_cmpint(epoll_fd, >=, 0);
3649
 
  __attribute__((cleanup(cleanup_queue)))
3650
 
    task_queue *queue = create_queue();
3651
 
  g_assert_nonnull(queue);
3652
 
  __attribute__((cleanup(string_set_clear)))
3653
 
    string_set cancelled_filenames = {};
3654
 
  const mono_microsecs current_time = 0;
3655
 
 
3656
 
  bool quit_now = false;
3657
 
  buffer password = {};
3658
 
  bool mandos_client_exited = false;
3659
 
  bool password_is_read = false;
3660
 
 
3661
 
  __attribute__((cleanup(cleanup_string)))
3662
 
    char *watchdir = make_temporary_directory();
3663
 
  g_assert_nonnull(watchdir);
3664
 
 
3665
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3666
 
                                      &password, watchdir,
3667
 
                                      &cancelled_filenames,
3668
 
                                      &current_time,
3669
 
                                      &mandos_client_exited,
3670
 
                                      &password_is_read));
3671
 
 
3672
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3673
 
 
3674
 
  const task_context *const added_read_task
3675
 
    = find_matching_task(queue,
3676
 
                         (task_context){ .func=read_inotify_event });
3677
 
  g_assert_nonnull(added_read_task);
3678
 
 
3679
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3680
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3681
 
 
3682
 
  char *sourcedir = make_temporary_directory();
3683
 
  g_assert_nonnull(sourcedir);
3684
 
 
3685
 
  __attribute__((cleanup(cleanup_string)))
3686
 
    char *filename = make_temporary_file_in_directory(sourcedir);
3687
 
  g_assert_nonnull(filename);
3688
 
 
3689
 
  __attribute__((cleanup(cleanup_string)))
3690
 
    char *targetfilename = NULL;
3691
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3692
 
                           basename(filename)), >, 0);
3693
 
  g_assert_nonnull(targetfilename);
3694
 
 
3695
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3696
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
3697
 
  free(sourcedir);
3698
 
 
3699
 
  /* "sufficient to read at least one event." - inotify(7) */
3700
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3701
 
                              + NAME_MAX + 1);
3702
 
  struct inotify_event *ievent = malloc(ievent_size);
3703
 
  g_assert_nonnull(ievent);
3704
 
 
3705
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3706
 
 
3707
 
  g_assert_cmpint((int)read_size, >, 0);
3708
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
3709
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3710
 
 
3711
 
  free(ievent);
3712
 
 
3713
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3714
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
3715
 
}
3716
 
 
3717
 
static
3718
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3719
 
                                              test_fixture *fixture,
3720
 
                                              __attribute__((unused))
3721
 
                                              gconstpointer
3722
 
                                              user_data){
3723
 
  __attribute__((cleanup(cleanup_close)))
3724
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3725
 
  g_assert_cmpint(epoll_fd, >=, 0);
3726
 
  __attribute__((cleanup(cleanup_queue)))
3727
 
    task_queue *queue = create_queue();
3728
 
  g_assert_nonnull(queue);
3729
 
  __attribute__((cleanup(string_set_clear)))
3730
 
    string_set cancelled_filenames = {};
3731
 
  const mono_microsecs current_time = 0;
3732
 
 
3733
 
  bool quit_now = false;
3734
 
  buffer password = {};
3735
 
  bool mandos_client_exited = false;
3736
 
  bool password_is_read = false;
3737
 
 
3738
 
  __attribute__((cleanup(cleanup_string)))
3739
 
    char *tempdir = make_temporary_directory();
3740
 
  g_assert_nonnull(tempdir);
3741
 
 
3742
 
  __attribute__((cleanup(cleanup_string)))
3743
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3744
 
  g_assert_nonnull(tempfilename);
3745
 
 
3746
 
  __attribute__((cleanup(cleanup_string)))
3747
 
    char *targetdir = make_temporary_directory();
3748
 
  g_assert_nonnull(targetdir);
3749
 
 
3750
 
  __attribute__((cleanup(cleanup_string)))
3751
 
    char *targetfilename = NULL;
3752
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3753
 
                           basename(tempfilename)), >, 0);
3754
 
  g_assert_nonnull(targetfilename);
3755
 
 
3756
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3757
 
                                      &password, tempdir,
3758
 
                                      &cancelled_filenames,
3759
 
                                      &current_time,
3760
 
                                      &mandos_client_exited,
3761
 
                                      &password_is_read));
3762
 
 
3763
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3764
 
 
3765
 
  const task_context *const added_read_task
3766
 
    = find_matching_task(queue,
3767
 
                         (task_context){ .func=read_inotify_event });
3768
 
  g_assert_nonnull(added_read_task);
3769
 
 
3770
 
  /* "sufficient to read at least one event." - inotify(7) */
3771
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3772
 
                              + NAME_MAX + 1);
3773
 
  struct inotify_event *ievent = malloc(ievent_size);
3774
 
  g_assert_nonnull(ievent);
3775
 
 
3776
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3777
 
 
3778
 
  g_assert_cmpint((int)read_size, >, 0);
3779
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3780
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3781
 
 
3782
 
  free(ievent);
3783
 
 
3784
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3785
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3786
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3787
 
}
3788
 
 
3789
 
static
3790
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3791
 
                                          test_fixture *fixture,
3792
 
                                          __attribute__((unused))
3793
 
                                          gconstpointer user_data){
3794
 
  __attribute__((cleanup(cleanup_close)))
3795
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3796
 
  g_assert_cmpint(epoll_fd, >=, 0);
3797
 
  __attribute__((cleanup(cleanup_queue)))
3798
 
    task_queue *queue = create_queue();
3799
 
  g_assert_nonnull(queue);
3800
 
  __attribute__((cleanup(string_set_clear)))
3801
 
    string_set cancelled_filenames = {};
3802
 
  const mono_microsecs current_time = 0;
3803
 
 
3804
 
  bool quit_now = false;
3805
 
  buffer password = {};
3806
 
  bool mandos_client_exited = false;
3807
 
  bool password_is_read = false;
3808
 
 
3809
 
  __attribute__((cleanup(cleanup_string)))
3810
 
    char *tempdir = make_temporary_directory();
3811
 
  g_assert_nonnull(tempdir);
3812
 
 
3813
 
  __attribute__((cleanup(cleanup_string)))
3814
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3815
 
  g_assert_nonnull(tempfile);
3816
 
 
3817
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3818
 
                                      &password, tempdir,
3819
 
                                      &cancelled_filenames,
3820
 
                                      &current_time,
3821
 
                                      &mandos_client_exited,
3822
 
                                      &password_is_read));
3823
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3824
 
 
3825
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3826
 
 
3827
 
  const task_context *const added_read_task
3828
 
    = find_matching_task(queue,
3829
 
                         (task_context){ .func=read_inotify_event });
3830
 
  g_assert_nonnull(added_read_task);
3831
 
 
3832
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3833
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3834
 
 
3835
 
  /* "sufficient to read at least one event." - inotify(7) */
3836
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3837
 
                              + NAME_MAX + 1);
3838
 
  struct inotify_event *ievent = malloc(ievent_size);
3839
 
  g_assert_nonnull(ievent);
3840
 
 
3841
 
  ssize_t read_size = 0;
3842
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3843
 
 
3844
 
  g_assert_cmpint((int)read_size, >, 0);
3845
 
  g_assert_true(ievent->mask & IN_DELETE);
3846
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3847
 
 
3848
 
  free(ievent);
3849
 
 
3850
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3851
 
}
3852
 
 
3853
 
static
3854
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3855
 
                                               test_fixture *fixture,
3856
 
                                               __attribute__((unused))
3857
 
                                               gconstpointer
3858
 
                                               user_data){
3859
 
  __attribute__((cleanup(cleanup_close)))
3860
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3861
 
  g_assert_cmpint(epoll_fd, >=, 0);
3862
 
  __attribute__((cleanup(cleanup_queue)))
3863
 
    task_queue *queue = create_queue();
3864
 
  g_assert_nonnull(queue);
3865
 
  __attribute__((cleanup(string_set_clear)))
3866
 
    string_set cancelled_filenames = {};
3867
 
  const mono_microsecs current_time = 0;
3868
 
 
3869
 
  bool quit_now = false;
3870
 
  buffer password = {};
3871
 
  bool mandos_client_exited = false;
3872
 
  bool password_is_read = false;
3873
 
 
3874
 
  __attribute__((cleanup(cleanup_string)))
3875
 
    char *tempdir = make_temporary_directory();
3876
 
  g_assert_nonnull(tempdir);
3877
 
 
3878
 
  __attribute__((cleanup(cleanup_string)))
3879
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3880
 
  g_assert_nonnull(tempfile);
3881
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3882
 
                         | O_NOFOLLOW);
3883
 
  g_assert_cmpint(tempfile_fd, >, 2);
3884
 
 
3885
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3886
 
                                      &password, tempdir,
3887
 
                                      &cancelled_filenames,
3888
 
                                      &current_time,
3889
 
                                      &mandos_client_exited,
3890
 
                                      &password_is_read));
3891
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3892
 
 
3893
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3894
 
 
3895
 
  const task_context *const added_read_task
3896
 
    = find_matching_task(queue,
3897
 
                         (task_context){ .func=read_inotify_event });
3898
 
  g_assert_nonnull(added_read_task);
3899
 
 
3900
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3901
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3902
 
 
3903
 
  /* "sufficient to read at least one event." - inotify(7) */
3904
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3905
 
                              + NAME_MAX + 1);
3906
 
  struct inotify_event *ievent = malloc(ievent_size);
3907
 
  g_assert_nonnull(ievent);
3908
 
 
3909
 
  ssize_t read_size = 0;
3910
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3911
 
 
3912
 
  g_assert_cmpint((int)read_size, >, 0);
3913
 
  g_assert_true(ievent->mask & IN_DELETE);
3914
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3915
 
 
3916
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3917
 
 
3918
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3919
 
     file not appear as an ievent, so we should not see it now. */
3920
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3921
 
  g_assert_cmpint((int)read_size, ==, -1);
3922
 
  g_assert_true(errno == EAGAIN);
3923
 
 
3924
 
  free(ievent);
3925
 
 
3926
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3927
 
}
3928
 
 
3929
 
static void test_read_inotify_event_readerror(__attribute__((unused))
3930
 
                                              test_fixture *fixture,
3931
 
                                              __attribute__((unused))
3932
 
                                              gconstpointer
3933
 
                                              user_data){
3934
 
  __attribute__((cleanup(cleanup_close)))
3935
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3936
 
  g_assert_cmpint(epoll_fd, >=, 0);
3937
 
  const mono_microsecs current_time = 0;
3938
 
 
3939
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3940
 
  const int fd = open("/proc/self/mem",
3941
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3942
 
 
3943
 
  bool quit_now = false;
3944
 
  __attribute__((cleanup(cleanup_queue)))
3945
 
    task_queue *queue = create_queue();
3946
 
  g_assert_nonnull(queue);
3947
 
 
3948
 
  task_context task = {
3949
 
    .func=read_inotify_event,
3950
 
    .epoll_fd=epoll_fd,
3951
 
    .fd=fd,
3952
 
    .quit_now=&quit_now,
3953
 
    .filename=strdup("/nonexistent"),
3954
 
    .cancelled_filenames = &(string_set){},
3955
 
    .notafter=0,
3956
 
    .current_time=&current_time,
3957
 
  };
3958
 
  g_assert_nonnull(task.filename);
3959
 
  run_task_with_stderr_to_dev_null(task, queue);
3960
 
  g_assert_true(quit_now);
3961
 
  g_assert_true(queue->next_run == 0);
3962
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3963
 
 
3964
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3965
 
 
3966
 
  g_assert_cmpint(close(fd), ==, -1);
3967
 
}
3968
 
 
3969
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3970
 
                                              test_fixture *fixture,
3971
 
                                              __attribute__((unused))
3972
 
                                              gconstpointer
3973
 
                                              user_data){
3974
 
  const mono_microsecs current_time = 17;
3975
 
 
3976
 
  int pipefds[2];
3977
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3978
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
3979
 
 
3980
 
  bool quit_now = false;
3981
 
  buffer password = {};
3982
 
  bool mandos_client_exited = false;
3983
 
  bool password_is_read = false;
3984
 
  __attribute__((cleanup(cleanup_queue)))
3985
 
    task_queue *queue = create_queue();
3986
 
  g_assert_nonnull(queue);
3987
 
 
3988
 
  task_context task = {
3989
 
    .func=read_inotify_event,
3990
 
    .epoll_fd=epoll_fd,
3991
 
    .fd=pipefds[0],
3992
 
    .quit_now=&quit_now,
3993
 
    .password=&password,
3994
 
    .filename=strdup("/nonexistent"),
3995
 
    .cancelled_filenames = &(string_set){},
3996
 
    .notafter=0,
3997
 
    .current_time=&current_time,
3998
 
    .mandos_client_exited=&mandos_client_exited,
3999
 
    .password_is_read=&password_is_read,
4000
 
  };
4001
 
  g_assert_nonnull(task.filename);
4002
 
  run_task_with_stderr_to_dev_null(task, queue);
4003
 
 
4004
 
  g_assert_nonnull(find_matching_task(queue, task));
4005
 
  g_assert_true(queue->next_run == 1000000 + current_time);
4006
 
 
4007
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
4008
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4009
 
}
4010
 
 
4011
 
static void test_read_inotify_event_nodata(__attribute__((unused))
4012
 
                                           test_fixture *fixture,
4013
 
                                           __attribute__((unused))
4014
 
                                           gconstpointer user_data){
4015
 
  __attribute__((cleanup(cleanup_close)))
4016
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4017
 
  g_assert_cmpint(epoll_fd, >=, 0);
4018
 
  const mono_microsecs current_time = 0;
4019
 
 
4020
 
  int pipefds[2];
4021
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4022
 
 
4023
 
  bool quit_now = false;
4024
 
  buffer password = {};
4025
 
  bool mandos_client_exited = false;
4026
 
  bool password_is_read = false;
4027
 
  __attribute__((cleanup(cleanup_queue)))
4028
 
    task_queue *queue = create_queue();
4029
 
  g_assert_nonnull(queue);
4030
 
 
4031
 
  task_context task = {
4032
 
    .func=read_inotify_event,
4033
 
    .epoll_fd=epoll_fd,
4034
 
    .fd=pipefds[0],
4035
 
    .quit_now=&quit_now,
4036
 
    .password=&password,
4037
 
    .filename=strdup("/nonexistent"),
4038
 
    .cancelled_filenames = &(string_set){},
4039
 
    .notafter=0,
4040
 
    .current_time=&current_time,
4041
 
    .mandos_client_exited=&mandos_client_exited,
4042
 
    .password_is_read=&password_is_read,
4043
 
  };
4044
 
  g_assert_nonnull(task.filename);
4045
 
  task.func(task, queue);
4046
 
  g_assert_false(quit_now);
4047
 
  g_assert_true(queue->next_run == 0);
4048
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4049
 
 
4050
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4051
 
        .func=read_inotify_event,
4052
 
        .epoll_fd=epoll_fd,
4053
 
        .fd=pipefds[0],
4054
 
        .quit_now=&quit_now,
4055
 
        .password=&password,
4056
 
        .filename=task.filename,
4057
 
        .cancelled_filenames=task.cancelled_filenames,
4058
 
        .current_time=&current_time,
4059
 
        .mandos_client_exited=&mandos_client_exited,
4060
 
        .password_is_read=&password_is_read,
4061
 
      }));
4062
 
 
4063
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4064
 
                                   EPOLLIN | EPOLLRDHUP));
4065
 
 
4066
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4067
 
}
4068
 
 
4069
 
static void test_read_inotify_event_eof(__attribute__((unused))
4070
 
                                        test_fixture *fixture,
4071
 
                                        __attribute__((unused))
4072
 
                                        gconstpointer user_data){
4073
 
  __attribute__((cleanup(cleanup_close)))
4074
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4075
 
  g_assert_cmpint(epoll_fd, >=, 0);
4076
 
  const mono_microsecs current_time = 0;
4077
 
 
4078
 
  int pipefds[2];
4079
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4080
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4081
 
 
4082
 
  bool quit_now = false;
4083
 
  buffer password = {};
4084
 
  __attribute__((cleanup(cleanup_queue)))
4085
 
    task_queue *queue = create_queue();
4086
 
  g_assert_nonnull(queue);
4087
 
 
4088
 
  task_context task = {
4089
 
    .func=read_inotify_event,
4090
 
    .epoll_fd=epoll_fd,
4091
 
    .fd=pipefds[0],
4092
 
    .quit_now=&quit_now,
4093
 
    .password=&password,
4094
 
    .filename=strdup("/nonexistent"),
4095
 
    .cancelled_filenames = &(string_set){},
4096
 
    .notafter=0,
4097
 
    .current_time=&current_time,
4098
 
  };
4099
 
  run_task_with_stderr_to_dev_null(task, queue);
4100
 
  g_assert_true(quit_now);
4101
 
  g_assert_true(queue->next_run == 0);
4102
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4103
 
 
4104
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4105
 
 
4106
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
4107
 
}
4108
 
 
4109
 
static
4110
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4111
 
                                            test_fixture *fixture,
4112
 
                                            __attribute__((unused))
4113
 
                                            gconstpointer user_data){
4114
 
  __attribute__((cleanup(cleanup_close)))
4115
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4116
 
  g_assert_cmpint(epoll_fd, >=, 0);
4117
 
  const mono_microsecs current_time = 0;
4118
 
 
4119
 
  int pipefds[2];
4120
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4121
 
 
4122
 
  /* "sufficient to read at least one event." - inotify(7) */
4123
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4124
 
                                  + NAME_MAX + 1);
4125
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4126
 
  struct {
4127
 
    struct inotify_event event;
4128
 
    char name_buffer[NAME_MAX + 1];
4129
 
  } ievent_buffer;
4130
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4131
 
 
4132
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4133
 
  ievent->mask = IN_CLOSE_WRITE;
4134
 
  ievent->len = sizeof(dummy_file_name);
4135
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4136
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4137
 
                              + sizeof(dummy_file_name));
4138
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4139
 
                  ==, ievent_size);
4140
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4141
 
 
4142
 
  bool quit_now = false;
4143
 
  buffer password = {};
4144
 
  bool mandos_client_exited = false;
4145
 
  bool password_is_read = false;
4146
 
  __attribute__((cleanup(cleanup_queue)))
4147
 
    task_queue *queue = create_queue();
4148
 
  g_assert_nonnull(queue);
4149
 
 
4150
 
  task_context task = {
4151
 
    .func=read_inotify_event,
4152
 
    .epoll_fd=epoll_fd,
4153
 
    .fd=pipefds[0],
4154
 
    .quit_now=&quit_now,
4155
 
    .password=&password,
4156
 
    .filename=strdup("/nonexistent"),
4157
 
    .cancelled_filenames = &(string_set){},
4158
 
    .notafter=0,
4159
 
    .current_time=&current_time,
4160
 
    .mandos_client_exited=&mandos_client_exited,
4161
 
    .password_is_read=&password_is_read,
4162
 
  };
4163
 
  task.func(task, queue);
4164
 
  g_assert_false(quit_now);
4165
 
  g_assert_true(queue->next_run != 0);
4166
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4167
 
 
4168
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4169
 
        .func=read_inotify_event,
4170
 
        .epoll_fd=epoll_fd,
4171
 
        .fd=pipefds[0],
4172
 
        .quit_now=&quit_now,
4173
 
        .password=&password,
4174
 
        .filename=task.filename,
4175
 
        .cancelled_filenames=task.cancelled_filenames,
4176
 
        .current_time=&current_time,
4177
 
        .mandos_client_exited=&mandos_client_exited,
4178
 
        .password_is_read=&password_is_read,
4179
 
      }));
4180
 
 
4181
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4182
 
                                   EPOLLIN | EPOLLRDHUP));
4183
 
 
4184
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4185
 
 
4186
 
  __attribute__((cleanup(cleanup_string)))
4187
 
    char *filename = NULL;
4188
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4189
 
                           dummy_file_name), >, 0);
4190
 
  g_assert_nonnull(filename);
4191
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4192
 
        .func=open_and_parse_question,
4193
 
        .epoll_fd=epoll_fd,
4194
 
        .filename=filename,
4195
 
        .question_filename=filename,
4196
 
        .password=&password,
4197
 
        .cancelled_filenames=task.cancelled_filenames,
4198
 
        .current_time=&current_time,
4199
 
        .mandos_client_exited=&mandos_client_exited,
4200
 
        .password_is_read=&password_is_read,
4201
 
      }));
4202
 
}
4203
 
 
4204
 
static
4205
 
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4206
 
                                         test_fixture *fixture,
4207
 
                                         __attribute__((unused))
4208
 
                                         gconstpointer user_data){
4209
 
  __attribute__((cleanup(cleanup_close)))
4210
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4211
 
  g_assert_cmpint(epoll_fd, >=, 0);
4212
 
  const mono_microsecs current_time = 0;
4213
 
 
4214
 
  int pipefds[2];
4215
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4216
 
 
4217
 
  /* "sufficient to read at least one event." - inotify(7) */
4218
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4219
 
                                  + NAME_MAX + 1);
4220
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4221
 
  struct {
4222
 
    struct inotify_event event;
4223
 
    char name_buffer[NAME_MAX + 1];
4224
 
  } ievent_buffer;
4225
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4226
 
 
4227
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4228
 
  ievent->mask = IN_MOVED_TO;
4229
 
  ievent->len = sizeof(dummy_file_name);
4230
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4231
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4232
 
                              + sizeof(dummy_file_name));
4233
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4234
 
                  ==, ievent_size);
4235
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4236
 
 
4237
 
  bool quit_now = false;
4238
 
  buffer password = {};
4239
 
  bool mandos_client_exited = false;
4240
 
  bool password_is_read = false;
4241
 
  __attribute__((cleanup(cleanup_queue)))
4242
 
    task_queue *queue = create_queue();
4243
 
  g_assert_nonnull(queue);
4244
 
 
4245
 
  task_context task = {
4246
 
    .func=read_inotify_event,
4247
 
    .epoll_fd=epoll_fd,
4248
 
    .fd=pipefds[0],
4249
 
    .quit_now=&quit_now,
4250
 
    .password=&password,
4251
 
    .filename=strdup("/nonexistent"),
4252
 
    .cancelled_filenames = &(string_set){},
4253
 
    .notafter=0,
4254
 
    .current_time=&current_time,
4255
 
    .mandos_client_exited=&mandos_client_exited,
4256
 
    .password_is_read=&password_is_read,
4257
 
  };
4258
 
  task.func(task, queue);
4259
 
  g_assert_false(quit_now);
4260
 
  g_assert_true(queue->next_run != 0);
4261
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4262
 
 
4263
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4264
 
        .func=read_inotify_event,
4265
 
        .epoll_fd=epoll_fd,
4266
 
        .fd=pipefds[0],
4267
 
        .quit_now=&quit_now,
4268
 
        .password=&password,
4269
 
        .filename=task.filename,
4270
 
        .cancelled_filenames=task.cancelled_filenames,
4271
 
        .current_time=&current_time,
4272
 
        .mandos_client_exited=&mandos_client_exited,
4273
 
        .password_is_read=&password_is_read,
4274
 
      }));
4275
 
 
4276
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4277
 
                                   EPOLLIN | EPOLLRDHUP));
4278
 
 
4279
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4280
 
 
4281
 
  __attribute__((cleanup(cleanup_string)))
4282
 
    char *filename = NULL;
4283
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4284
 
                           dummy_file_name), >, 0);
4285
 
  g_assert_nonnull(filename);
4286
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4287
 
        .func=open_and_parse_question,
4288
 
        .epoll_fd=epoll_fd,
4289
 
        .filename=filename,
4290
 
        .question_filename=filename,
4291
 
        .password=&password,
4292
 
        .cancelled_filenames=task.cancelled_filenames,
4293
 
        .current_time=&current_time,
4294
 
        .mandos_client_exited=&mandos_client_exited,
4295
 
        .password_is_read=&password_is_read,
4296
 
      }));
4297
 
}
4298
 
 
4299
 
static
4300
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4301
 
                                           test_fixture *fixture,
4302
 
                                           __attribute__((unused))
4303
 
                                           gconstpointer user_data){
4304
 
  __attribute__((cleanup(cleanup_close)))
4305
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4306
 
  g_assert_cmpint(epoll_fd, >=, 0);
4307
 
  __attribute__((cleanup(string_set_clear)))
4308
 
    string_set cancelled_filenames = {};
4309
 
  const mono_microsecs current_time = 0;
4310
 
 
4311
 
  int pipefds[2];
4312
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4313
 
 
4314
 
  /* "sufficient to read at least one event." - inotify(7) */
4315
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4316
 
                                  + NAME_MAX + 1);
4317
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4318
 
  struct {
4319
 
    struct inotify_event event;
4320
 
    char name_buffer[NAME_MAX + 1];
4321
 
  } ievent_buffer;
4322
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4323
 
 
4324
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4325
 
  ievent->mask = IN_MOVED_FROM;
4326
 
  ievent->len = sizeof(dummy_file_name);
4327
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4328
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4329
 
                              + sizeof(dummy_file_name));
4330
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4331
 
                  ==, ievent_size);
4332
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4333
 
 
4334
 
  bool quit_now = false;
4335
 
  buffer password = {};
4336
 
  bool mandos_client_exited = false;
4337
 
  bool password_is_read = false;
4338
 
  __attribute__((cleanup(cleanup_queue)))
4339
 
    task_queue *queue = create_queue();
4340
 
  g_assert_nonnull(queue);
4341
 
 
4342
 
  task_context task = {
4343
 
    .func=read_inotify_event,
4344
 
    .epoll_fd=epoll_fd,
4345
 
    .fd=pipefds[0],
4346
 
    .quit_now=&quit_now,
4347
 
    .password=&password,
4348
 
    .filename=strdup("/nonexistent"),
4349
 
    .cancelled_filenames=&cancelled_filenames,
4350
 
    .current_time=&current_time,
4351
 
    .mandos_client_exited=&mandos_client_exited,
4352
 
    .password_is_read=&password_is_read,
4353
 
  };
4354
 
  task.func(task, queue);
4355
 
  g_assert_false(quit_now);
4356
 
  g_assert_true(queue->next_run == 0);
4357
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4358
 
 
4359
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4360
 
        .func=read_inotify_event,
4361
 
        .epoll_fd=epoll_fd,
4362
 
        .fd=pipefds[0],
4363
 
        .quit_now=&quit_now,
4364
 
        .password=&password,
4365
 
        .filename=task.filename,
4366
 
        .cancelled_filenames=&cancelled_filenames,
4367
 
        .current_time=&current_time,
4368
 
        .mandos_client_exited=&mandos_client_exited,
4369
 
        .password_is_read=&password_is_read,
4370
 
      }));
4371
 
 
4372
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4373
 
                                   EPOLLIN | EPOLLRDHUP));
4374
 
 
4375
 
  __attribute__((cleanup(cleanup_string)))
4376
 
    char *filename = NULL;
4377
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4378
 
                           dummy_file_name), >, 0);
4379
 
  g_assert_nonnull(filename);
4380
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4381
 
                                    filename));
4382
 
}
4383
 
 
4384
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4385
 
                                              test_fixture *fixture,
4386
 
                                              __attribute__((unused))
4387
 
                                              gconstpointer
4388
 
                                              user_data){
4389
 
  __attribute__((cleanup(cleanup_close)))
4390
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4391
 
  g_assert_cmpint(epoll_fd, >=, 0);
4392
 
  __attribute__((cleanup(string_set_clear)))
4393
 
    string_set cancelled_filenames = {};
4394
 
  const mono_microsecs current_time = 0;
4395
 
 
4396
 
  int pipefds[2];
4397
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4398
 
 
4399
 
  /* "sufficient to read at least one event." - inotify(7) */
4400
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4401
 
                                  + NAME_MAX + 1);
4402
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4403
 
  struct {
4404
 
    struct inotify_event event;
4405
 
    char name_buffer[NAME_MAX + 1];
4406
 
  } ievent_buffer;
4407
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4408
 
 
4409
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4410
 
  ievent->mask = IN_DELETE;
4411
 
  ievent->len = sizeof(dummy_file_name);
4412
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4413
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4414
 
                              + sizeof(dummy_file_name));
4415
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4416
 
                  ==, ievent_size);
4417
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4418
 
 
4419
 
  bool quit_now = false;
4420
 
  buffer password = {};
4421
 
  bool mandos_client_exited = false;
4422
 
  bool password_is_read = false;
4423
 
  __attribute__((cleanup(cleanup_queue)))
4424
 
    task_queue *queue = create_queue();
4425
 
  g_assert_nonnull(queue);
4426
 
 
4427
 
  task_context task = {
4428
 
    .func=read_inotify_event,
4429
 
    .epoll_fd=epoll_fd,
4430
 
    .fd=pipefds[0],
4431
 
    .quit_now=&quit_now,
4432
 
    .password=&password,
4433
 
    .filename=strdup("/nonexistent"),
4434
 
    .cancelled_filenames=&cancelled_filenames,
4435
 
    .current_time=&current_time,
4436
 
    .mandos_client_exited=&mandos_client_exited,
4437
 
    .password_is_read=&password_is_read,
4438
 
  };
4439
 
  task.func(task, queue);
4440
 
  g_assert_false(quit_now);
4441
 
  g_assert_true(queue->next_run == 0);
4442
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4443
 
 
4444
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4445
 
        .func=read_inotify_event,
4446
 
        .epoll_fd=epoll_fd,
4447
 
        .fd=pipefds[0],
4448
 
        .quit_now=&quit_now,
4449
 
        .password=&password,
4450
 
        .filename=task.filename,
4451
 
        .cancelled_filenames=&cancelled_filenames,
4452
 
        .current_time=&current_time,
4453
 
        .mandos_client_exited=&mandos_client_exited,
4454
 
        .password_is_read=&password_is_read,
4455
 
      }));
4456
 
 
4457
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4458
 
                                   EPOLLIN | EPOLLRDHUP));
4459
 
 
4460
 
  __attribute__((cleanup(cleanup_string)))
4461
 
    char *filename = NULL;
4462
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4463
 
                           dummy_file_name), >, 0);
4464
 
  g_assert_nonnull(filename);
4465
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4466
 
                                    filename));
4467
 
}
4468
 
 
4469
 
static void
4470
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4471
 
                                               test_fixture *fixture,
4472
 
                                               __attribute__((unused))
4473
 
                                               gconstpointer
4474
 
                                               user_data){
4475
 
  __attribute__((cleanup(cleanup_close)))
4476
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4477
 
  g_assert_cmpint(epoll_fd, >=, 0);
4478
 
  const mono_microsecs current_time = 0;
4479
 
 
4480
 
  int pipefds[2];
4481
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4482
 
 
4483
 
  /* "sufficient to read at least one event." - inotify(7) */
4484
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4485
 
                                  + NAME_MAX + 1);
4486
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4487
 
  struct {
4488
 
    struct inotify_event event;
4489
 
    char name_buffer[NAME_MAX + 1];
4490
 
  } ievent_buffer;
4491
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4492
 
 
4493
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4494
 
  ievent->mask = IN_CLOSE_WRITE;
4495
 
  ievent->len = sizeof(dummy_file_name);
4496
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4497
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4498
 
                              + sizeof(dummy_file_name));
4499
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4500
 
                  ==, ievent_size);
4501
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4502
 
 
4503
 
  bool quit_now = false;
4504
 
  buffer password = {};
4505
 
  bool mandos_client_exited = false;
4506
 
  bool password_is_read = false;
4507
 
  __attribute__((cleanup(cleanup_queue)))
4508
 
    task_queue *queue = create_queue();
4509
 
  g_assert_nonnull(queue);
4510
 
 
4511
 
  task_context task = {
4512
 
    .func=read_inotify_event,
4513
 
    .epoll_fd=epoll_fd,
4514
 
    .fd=pipefds[0],
4515
 
    .quit_now=&quit_now,
4516
 
    .password=&password,
4517
 
    .filename=strdup("/nonexistent"),
4518
 
    .cancelled_filenames = &(string_set){},
4519
 
    .notafter=0,
4520
 
    .current_time=&current_time,
4521
 
    .mandos_client_exited=&mandos_client_exited,
4522
 
    .password_is_read=&password_is_read,
4523
 
  };
4524
 
  task.func(task, queue);
4525
 
  g_assert_false(quit_now);
4526
 
  g_assert_true(queue->next_run == 0);
4527
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4528
 
 
4529
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4530
 
        .func=read_inotify_event,
4531
 
        .epoll_fd=epoll_fd,
4532
 
        .fd=pipefds[0],
4533
 
        .quit_now=&quit_now,
4534
 
        .password=&password,
4535
 
        .filename=task.filename,
4536
 
        .cancelled_filenames=task.cancelled_filenames,
4537
 
        .current_time=&current_time,
4538
 
        .mandos_client_exited=&mandos_client_exited,
4539
 
        .password_is_read=&password_is_read,
4540
 
      }));
4541
 
 
4542
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4543
 
                                   EPOLLIN | EPOLLRDHUP));
4544
 
}
4545
 
 
4546
 
static void
4547
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4548
 
                                            test_fixture *fixture,
4549
 
                                            __attribute__((unused))
4550
 
                                            gconstpointer user_data){
4551
 
  __attribute__((cleanup(cleanup_close)))
4552
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4553
 
  g_assert_cmpint(epoll_fd, >=, 0);
4554
 
  const mono_microsecs current_time = 0;
4555
 
 
4556
 
  int pipefds[2];
4557
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4558
 
 
4559
 
  /* "sufficient to read at least one event." - inotify(7) */
4560
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4561
 
                                  + NAME_MAX + 1);
4562
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4563
 
  struct {
4564
 
    struct inotify_event event;
4565
 
    char name_buffer[NAME_MAX + 1];
4566
 
  } ievent_buffer;
4567
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4568
 
 
4569
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4570
 
  ievent->mask = IN_MOVED_TO;
4571
 
  ievent->len = sizeof(dummy_file_name);
4572
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4573
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4574
 
                              + sizeof(dummy_file_name));
4575
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4576
 
                  ==, ievent_size);
4577
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4578
 
 
4579
 
  bool quit_now = false;
4580
 
  buffer password = {};
4581
 
  bool mandos_client_exited = false;
4582
 
  bool password_is_read = false;
4583
 
  __attribute__((cleanup(cleanup_queue)))
4584
 
    task_queue *queue = create_queue();
4585
 
  g_assert_nonnull(queue);
4586
 
 
4587
 
  task_context task = {
4588
 
    .func=read_inotify_event,
4589
 
    .epoll_fd=epoll_fd,
4590
 
    .fd=pipefds[0],
4591
 
    .quit_now=&quit_now,
4592
 
    .password=&password,
4593
 
    .filename=strdup("/nonexistent"),
4594
 
    .cancelled_filenames = &(string_set){},
4595
 
    .notafter=0,
4596
 
    .current_time=&current_time,
4597
 
    .mandos_client_exited=&mandos_client_exited,
4598
 
    .password_is_read=&password_is_read,
4599
 
  };
4600
 
  task.func(task, queue);
4601
 
  g_assert_false(quit_now);
4602
 
  g_assert_true(queue->next_run == 0);
4603
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4604
 
 
4605
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4606
 
        .func=read_inotify_event,
4607
 
        .epoll_fd=epoll_fd,
4608
 
        .fd=pipefds[0],
4609
 
        .quit_now=&quit_now,
4610
 
        .password=&password,
4611
 
        .filename=task.filename,
4612
 
        .cancelled_filenames=task.cancelled_filenames,
4613
 
        .current_time=&current_time,
4614
 
        .mandos_client_exited=&mandos_client_exited,
4615
 
        .password_is_read=&password_is_read,
4616
 
      }));
4617
 
 
4618
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4619
 
                                   EPOLLIN | EPOLLRDHUP));
4620
 
}
4621
 
 
4622
 
static void
4623
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4624
 
                                              test_fixture *fixture,
4625
 
                                              __attribute__((unused))
4626
 
                                              gconstpointer
4627
 
                                              user_data){
4628
 
  __attribute__((cleanup(cleanup_close)))
4629
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4630
 
  g_assert_cmpint(epoll_fd, >=, 0);
4631
 
  __attribute__((cleanup(string_set_clear)))
4632
 
    string_set cancelled_filenames = {};
4633
 
  const mono_microsecs current_time = 0;
4634
 
 
4635
 
  int pipefds[2];
4636
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4637
 
 
4638
 
  /* "sufficient to read at least one event." - inotify(7) */
4639
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4640
 
                                  + NAME_MAX + 1);
4641
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4642
 
  struct {
4643
 
    struct inotify_event event;
4644
 
    char name_buffer[NAME_MAX + 1];
4645
 
  } ievent_buffer;
4646
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4647
 
 
4648
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4649
 
  ievent->mask = IN_MOVED_FROM;
4650
 
  ievent->len = sizeof(dummy_file_name);
4651
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4652
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4653
 
                              + sizeof(dummy_file_name));
4654
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4655
 
                  ==, ievent_size);
4656
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4657
 
 
4658
 
  bool quit_now = false;
4659
 
  buffer password = {};
4660
 
  bool mandos_client_exited = false;
4661
 
  bool password_is_read = false;
4662
 
  __attribute__((cleanup(cleanup_queue)))
4663
 
    task_queue *queue = create_queue();
4664
 
  g_assert_nonnull(queue);
4665
 
 
4666
 
  task_context task = {
4667
 
    .func=read_inotify_event,
4668
 
    .epoll_fd=epoll_fd,
4669
 
    .fd=pipefds[0],
4670
 
    .quit_now=&quit_now,
4671
 
    .password=&password,
4672
 
    .filename=strdup("/nonexistent"),
4673
 
    .cancelled_filenames=&cancelled_filenames,
4674
 
    .current_time=&current_time,
4675
 
    .mandos_client_exited=&mandos_client_exited,
4676
 
    .password_is_read=&password_is_read,
4677
 
  };
4678
 
  task.func(task, queue);
4679
 
  g_assert_false(quit_now);
4680
 
  g_assert_true(queue->next_run == 0);
4681
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4682
 
 
4683
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4684
 
        .func=read_inotify_event,
4685
 
        .epoll_fd=epoll_fd,
4686
 
        .fd=pipefds[0],
4687
 
        .quit_now=&quit_now,
4688
 
        .password=&password,
4689
 
        .filename=task.filename,
4690
 
        .cancelled_filenames=&cancelled_filenames,
4691
 
        .current_time=&current_time,
4692
 
        .mandos_client_exited=&mandos_client_exited,
4693
 
        .password_is_read=&password_is_read,
4694
 
      }));
4695
 
 
4696
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4697
 
                                   EPOLLIN | EPOLLRDHUP));
4698
 
 
4699
 
  __attribute__((cleanup(cleanup_string)))
4700
 
    char *filename = NULL;
4701
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4702
 
                           dummy_file_name), >, 0);
4703
 
  g_assert_nonnull(filename);
4704
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4705
 
}
4706
 
 
4707
 
static
4708
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4709
 
                                               test_fixture *fixture,
4710
 
                                               __attribute__((unused))
4711
 
                                               gconstpointer
4712
 
                                               user_data){
4713
 
  __attribute__((cleanup(cleanup_close)))
4714
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4715
 
  g_assert_cmpint(epoll_fd, >=, 0);
4716
 
  __attribute__((cleanup(string_set_clear)))
4717
 
    string_set cancelled_filenames = {};
4718
 
  const mono_microsecs current_time = 0;
4719
 
 
4720
 
  int pipefds[2];
4721
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4722
 
 
4723
 
  /* "sufficient to read at least one event." - inotify(7) */
4724
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4725
 
                                  + NAME_MAX + 1);
4726
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4727
 
  struct {
4728
 
    struct inotify_event event;
4729
 
    char name_buffer[NAME_MAX + 1];
4730
 
  } ievent_buffer;
4731
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4732
 
 
4733
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4734
 
  ievent->mask = IN_DELETE;
4735
 
  ievent->len = sizeof(dummy_file_name);
4736
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4737
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4738
 
                              + sizeof(dummy_file_name));
4739
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4740
 
                  ==, ievent_size);
4741
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4742
 
 
4743
 
  bool quit_now = false;
4744
 
  buffer password = {};
4745
 
  bool mandos_client_exited = false;
4746
 
  bool password_is_read = false;
4747
 
  __attribute__((cleanup(cleanup_queue)))
4748
 
    task_queue *queue = create_queue();
4749
 
  g_assert_nonnull(queue);
4750
 
 
4751
 
  task_context task = {
4752
 
    .func=read_inotify_event,
4753
 
    .epoll_fd=epoll_fd,
4754
 
    .fd=pipefds[0],
4755
 
    .quit_now=&quit_now,
4756
 
    .password=&password,
4757
 
    .filename=strdup("/nonexistent"),
4758
 
    .cancelled_filenames=&cancelled_filenames,
4759
 
    .current_time=&current_time,
4760
 
    .mandos_client_exited=&mandos_client_exited,
4761
 
    .password_is_read=&password_is_read,
4762
 
  };
4763
 
  task.func(task, queue);
4764
 
  g_assert_false(quit_now);
4765
 
  g_assert_true(queue->next_run == 0);
4766
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4767
 
 
4768
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4769
 
        .func=read_inotify_event,
4770
 
        .epoll_fd=epoll_fd,
4771
 
        .fd=pipefds[0],
4772
 
        .quit_now=&quit_now,
4773
 
        .password=&password,
4774
 
        .filename=task.filename,
4775
 
        .cancelled_filenames=&cancelled_filenames,
4776
 
        .current_time=&current_time,
4777
 
        .mandos_client_exited=&mandos_client_exited,
4778
 
        .password_is_read=&password_is_read,
4779
 
      }));
4780
 
 
4781
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4782
 
                                   EPOLLIN | EPOLLRDHUP));
4783
 
 
4784
 
  __attribute__((cleanup(cleanup_string)))
4785
 
    char *filename = NULL;
4786
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4787
 
                           dummy_file_name), >, 0);
4788
 
  g_assert_nonnull(filename);
4789
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4790
 
}
4791
 
 
4792
 
static
4793
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4794
 
                                         test_fixture *fixture,
4795
 
                                         __attribute__((unused))
4796
 
                                         gconstpointer user_data){
4797
 
  __attribute__((cleanup(cleanup_close)))
4798
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4799
 
  g_assert_cmpint(epoll_fd, >=, 0);
4800
 
  __attribute__((cleanup(string_set_clear)))
4801
 
    string_set cancelled_filenames = {};
4802
 
  bool mandos_client_exited = false;
4803
 
  bool password_is_read = false;
4804
 
  __attribute__((cleanup(cleanup_queue)))
4805
 
    task_queue *queue = create_queue();
4806
 
  g_assert_nonnull(queue);
4807
 
 
4808
 
  char *const filename = strdup("/nonexistent");
4809
 
  g_assert_nonnull(filename);
4810
 
  task_context task = {
4811
 
    .func=open_and_parse_question,
4812
 
    .question_filename=filename,
4813
 
    .epoll_fd=epoll_fd,
4814
 
    .password=(buffer[]){{}},
4815
 
    .filename=filename,
4816
 
    .cancelled_filenames=&cancelled_filenames,
4817
 
    .current_time=(mono_microsecs[]){0},
4818
 
    .mandos_client_exited=&mandos_client_exited,
4819
 
    .password_is_read=&password_is_read,
4820
 
  };
4821
 
  task.func(task, queue);
4822
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4823
 
}
4824
 
 
4825
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
4826
 
                                             test_fixture *fixture,
4827
 
                                             __attribute__((unused))
4828
 
                                             gconstpointer user_data){
4829
 
  __attribute__((cleanup(cleanup_close)))
4830
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4831
 
  g_assert_cmpint(epoll_fd, >=, 0);
4832
 
  __attribute__((cleanup(string_set_clear)))
4833
 
    string_set cancelled_filenames = {};
4834
 
  buffer password = {};
4835
 
  bool mandos_client_exited = false;
4836
 
  bool password_is_read = false;
4837
 
  __attribute__((cleanup(cleanup_queue)))
4838
 
    task_queue *queue = create_queue();
4839
 
  g_assert_nonnull(queue);
4840
 
  const mono_microsecs current_time = 0;
4841
 
 
4842
 
  char *filename = strdup("/proc/self/mem");
4843
 
  task_context task = {
4844
 
    .func=open_and_parse_question,
4845
 
    .question_filename=filename,
4846
 
    .epoll_fd=epoll_fd,
4847
 
    .password=&password,
4848
 
    .filename=filename,
4849
 
    .cancelled_filenames=&cancelled_filenames,
4850
 
    .current_time=&current_time,
4851
 
    .mandos_client_exited=&mandos_client_exited,
4852
 
    .password_is_read=&password_is_read,
4853
 
  };
4854
 
  run_task_with_stderr_to_dev_null(task, queue);
4855
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4856
 
}
4857
 
 
4858
 
static void
4859
 
test_open_and_parse_question_parse_error(__attribute__((unused))
4860
 
                                         test_fixture *fixture,
4861
 
                                         __attribute__((unused))
4862
 
                                         gconstpointer user_data){
4863
 
  __attribute__((cleanup(cleanup_close)))
4864
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4865
 
  g_assert_cmpint(epoll_fd, >=, 0);
4866
 
  __attribute__((cleanup(string_set_clear)))
4867
 
    string_set cancelled_filenames = {};
4868
 
  __attribute__((cleanup(cleanup_queue)))
4869
 
    task_queue *queue = create_queue();
4870
 
  g_assert_nonnull(queue);
4871
 
 
4872
 
  __attribute__((cleanup(cleanup_string)))
4873
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4874
 
  g_assert_nonnull(tempfilename);
4875
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4876
 
  g_assert_cmpint(tempfile, >, 0);
4877
 
  const char bad_data[] = "this is bad syntax\n";
4878
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4879
 
                  ==, sizeof(bad_data));
4880
 
  g_assert_cmpint(close(tempfile), ==, 0);
4881
 
 
4882
 
  char *const filename = strdup(tempfilename);
4883
 
  g_assert_nonnull(filename);
4884
 
  task_context task = {
4885
 
    .func=open_and_parse_question,
4886
 
    .question_filename=filename,
4887
 
    .epoll_fd=epoll_fd,
4888
 
    .password=(buffer[]){{}},
4889
 
    .filename=filename,
4890
 
    .cancelled_filenames=&cancelled_filenames,
4891
 
    .current_time=(mono_microsecs[]){0},
4892
 
    .mandos_client_exited=(bool[]){false},
4893
 
    .password_is_read=(bool[]){false},
4894
 
  };
4895
 
  run_task_with_stderr_to_dev_null(task, queue);
4896
 
 
4897
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4898
 
 
4899
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4900
 
}
4901
 
 
4902
 
static
4903
 
void test_open_and_parse_question_nosocket(__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);
4915
 
 
4916
 
  __attribute__((cleanup(cleanup_string)))
4917
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4918
 
  g_assert_nonnull(tempfilename);
4919
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4920
 
  g_assert_cmpint(questionfile, >, 0);
4921
 
  FILE *qf = fdopen(questionfile, "w");
4922
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4923
 
  g_assert_cmpint(fclose(qf), ==, 0);
4924
 
 
4925
 
  char *const filename = strdup(tempfilename);
4926
 
  g_assert_nonnull(filename);
4927
 
  task_context task = {
4928
 
    .func=open_and_parse_question,
4929
 
    .question_filename=filename,
4930
 
    .epoll_fd=epoll_fd,
4931
 
    .password=(buffer[]){{}},
4932
 
    .filename=filename,
4933
 
    .cancelled_filenames=&cancelled_filenames,
4934
 
    .current_time=(mono_microsecs[]){0},
4935
 
    .mandos_client_exited=(bool[]){false},
4936
 
    .password_is_read=(bool[]){false},
4937
 
  };
4938
 
  run_task_with_stderr_to_dev_null(task, queue);
4939
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4940
 
 
4941
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4942
 
}
4943
 
 
4944
 
static
4945
 
void test_open_and_parse_question_badsocket(__attribute__((unused))
4946
 
                                            test_fixture *fixture,
4947
 
                                            __attribute__((unused))
4948
 
                                            gconstpointer user_data){
4949
 
  __attribute__((cleanup(cleanup_close)))
4950
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4951
 
  g_assert_cmpint(epoll_fd, >=, 0);
4952
 
  __attribute__((cleanup(string_set_clear)))
4953
 
    string_set cancelled_filenames = {};
4954
 
  __attribute__((cleanup(cleanup_queue)))
4955
 
    task_queue *queue = create_queue();
4956
 
  g_assert_nonnull(queue);
4957
 
 
4958
 
  __attribute__((cleanup(cleanup_string)))
4959
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4960
 
  g_assert_nonnull(tempfilename);
4961
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4962
 
  g_assert_cmpint(questionfile, >, 0);
4963
 
  FILE *qf = fdopen(questionfile, "w");
4964
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4965
 
  g_assert_cmpint(fclose(qf), ==, 0);
4966
 
 
4967
 
  char *const filename = strdup(tempfilename);
4968
 
  g_assert_nonnull(filename);
4969
 
  task_context task = {
4970
 
    .func=open_and_parse_question,
4971
 
    .question_filename=filename,
4972
 
    .epoll_fd=epoll_fd,
4973
 
    .password=(buffer[]){{}},
4974
 
    .filename=filename,
4975
 
    .cancelled_filenames=&cancelled_filenames,
4976
 
    .current_time=(mono_microsecs[]){0},
4977
 
    .mandos_client_exited=(bool[]){false},
4978
 
    .password_is_read=(bool[]){false},
4979
 
  };
4980
 
  run_task_with_stderr_to_dev_null(task, queue);
4981
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4982
 
 
4983
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4984
 
}
4985
 
 
4986
 
static
4987
 
void test_open_and_parse_question_nopid(__attribute__((unused))
4988
 
                                        test_fixture *fixture,
4989
 
                                        __attribute__((unused))
4990
 
                                        gconstpointer user_data){
4991
 
  __attribute__((cleanup(cleanup_close)))
4992
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4993
 
  g_assert_cmpint(epoll_fd, >=, 0);
4994
 
  __attribute__((cleanup(string_set_clear)))
4995
 
    string_set cancelled_filenames = {};
4996
 
  __attribute__((cleanup(cleanup_queue)))
4997
 
    task_queue *queue = create_queue();
4998
 
  g_assert_nonnull(queue);
4999
 
 
5000
 
  __attribute__((cleanup(cleanup_string)))
5001
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5002
 
  g_assert_nonnull(tempfilename);
5003
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5004
 
  g_assert_cmpint(questionfile, >, 0);
5005
 
  FILE *qf = fdopen(questionfile, "w");
5006
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5007
 
  g_assert_cmpint(fclose(qf), ==, 0);
5008
 
 
5009
 
  char *const filename = strdup(tempfilename);
5010
 
  g_assert_nonnull(filename);
5011
 
  task_context task = {
5012
 
    .func=open_and_parse_question,
5013
 
    .question_filename=filename,
5014
 
    .epoll_fd=epoll_fd,
5015
 
    .password=(buffer[]){{}},
5016
 
    .filename=filename,
5017
 
    .cancelled_filenames=&cancelled_filenames,
5018
 
    .current_time=(mono_microsecs[]){0},
5019
 
    .mandos_client_exited=(bool[]){false},
5020
 
    .password_is_read=(bool[]){false},
5021
 
  };
5022
 
  run_task_with_stderr_to_dev_null(task, queue);
5023
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5024
 
 
5025
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5026
 
}
5027
 
 
5028
 
static
5029
 
void test_open_and_parse_question_badpid(__attribute__((unused))
5030
 
                                         test_fixture *fixture,
5031
 
                                         __attribute__((unused))
5032
 
                                         gconstpointer user_data){
5033
 
  __attribute__((cleanup(cleanup_close)))
5034
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5035
 
  g_assert_cmpint(epoll_fd, >=, 0);
5036
 
  __attribute__((cleanup(string_set_clear)))
5037
 
    string_set cancelled_filenames = {};
5038
 
  __attribute__((cleanup(cleanup_queue)))
5039
 
    task_queue *queue = create_queue();
5040
 
  g_assert_nonnull(queue);
5041
 
 
5042
 
  __attribute__((cleanup(cleanup_string)))
5043
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5044
 
  g_assert_nonnull(tempfilename);
5045
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5046
 
  g_assert_cmpint(questionfile, >, 0);
5047
 
  FILE *qf = fdopen(questionfile, "w");
5048
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5049
 
                  >, 0);
5050
 
  g_assert_cmpint(fclose(qf), ==, 0);
5051
 
 
5052
 
  char *const filename = strdup(tempfilename);
5053
 
  g_assert_nonnull(filename);
5054
 
  task_context task = {
5055
 
    .func=open_and_parse_question,
5056
 
    .question_filename=filename,
5057
 
    .epoll_fd=epoll_fd,
5058
 
    .password=(buffer[]){{}},
5059
 
    .filename=filename,
5060
 
    .cancelled_filenames=&cancelled_filenames,
5061
 
    .current_time=(mono_microsecs[]){0},
5062
 
    .mandos_client_exited=(bool[]){false},
5063
 
    .password_is_read=(bool[]){false},
5064
 
  };
5065
 
  run_task_with_stderr_to_dev_null(task, queue);
5066
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5067
 
 
5068
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5069
 
}
5070
 
 
5071
 
static void
5072
 
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5073
 
                                         test_fixture *fixture,
5074
 
                                         __attribute__((unused))
5075
 
                                         gconstpointer user_data){
5076
 
  __attribute__((cleanup(cleanup_close)))
5077
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5078
 
  g_assert_cmpint(epoll_fd, >=, 0);
5079
 
  __attribute__((cleanup(string_set_clear)))
5080
 
    string_set cancelled_filenames = {};
5081
 
  buffer password = {};
5082
 
  bool mandos_client_exited = false;
5083
 
  bool password_is_read = false;
5084
 
  __attribute__((cleanup(cleanup_queue)))
5085
 
    task_queue *queue = create_queue();
5086
 
  g_assert_nonnull(queue);
5087
 
  const mono_microsecs current_time = 0;
5088
 
 
5089
 
  /* Find value of sysctl kernel.pid_max */
5090
 
  uintmax_t pid_max = 0;
5091
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5092
 
  g_assert_nonnull(sysctl_pid_max);
5093
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5094
 
                  ==, 1);
5095
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5096
 
 
5097
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5098
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
5099
 
 
5100
 
  __attribute__((cleanup(cleanup_string)))
5101
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5102
 
  g_assert_nonnull(tempfilename);
5103
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5104
 
  g_assert_cmpint(questionfile, >, 0);
5105
 
  FILE *qf = fdopen(questionfile, "w");
5106
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5107
 
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5108
 
                  >, 0);
5109
 
  g_assert_cmpint(fclose(qf), ==, 0);
5110
 
 
5111
 
  char *const question_filename = strdup(tempfilename);
5112
 
  g_assert_nonnull(question_filename);
5113
 
  task_context task = {
5114
 
    .func=open_and_parse_question,
5115
 
    .question_filename=question_filename,
5116
 
    .epoll_fd=epoll_fd,
5117
 
    .password=&password,
5118
 
    .filename=question_filename,
5119
 
    .cancelled_filenames=&cancelled_filenames,
5120
 
    .current_time=&current_time,
5121
 
    .mandos_client_exited=&mandos_client_exited,
5122
 
    .password_is_read=&password_is_read,
5123
 
  };
5124
 
  run_task_with_stderr_to_dev_null(task, queue);
5125
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5126
 
 
5127
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5128
 
}
5129
 
 
5130
 
static void
5131
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
5132
 
                                         test_fixture *fixture,
5133
 
                                         __attribute__((unused))
5134
 
                                         gconstpointer user_data){
5135
 
  __attribute__((cleanup(cleanup_close)))
5136
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5137
 
  g_assert_cmpint(epoll_fd, >=, 0);
5138
 
  __attribute__((cleanup(string_set_clear)))
5139
 
    string_set cancelled_filenames = {};
5140
 
  buffer password = {};
5141
 
  bool mandos_client_exited = false;
5142
 
  bool password_is_read = false;
5143
 
  __attribute__((cleanup(cleanup_queue)))
5144
 
    task_queue *queue = create_queue();
5145
 
  g_assert_nonnull(queue);
5146
 
  const mono_microsecs current_time = 0;
5147
 
 
5148
 
  __attribute__((cleanup(cleanup_string)))
5149
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5150
 
  g_assert_nonnull(tempfilename);
5151
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5152
 
  g_assert_cmpint(questionfile, >, 0);
5153
 
  FILE *qf = fdopen(questionfile, "w");
5154
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5155
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5156
 
  g_assert_cmpint(fclose(qf), ==, 0);
5157
 
 
5158
 
  char *const filename = strdup(tempfilename);
5159
 
  g_assert_nonnull(filename);
5160
 
  task_context task = {
5161
 
    .func=open_and_parse_question,
5162
 
    .question_filename=filename,
5163
 
    .epoll_fd=epoll_fd,
5164
 
    .password=&password,
5165
 
    .filename=filename,
5166
 
    .cancelled_filenames=&cancelled_filenames,
5167
 
    .current_time=&current_time,
5168
 
    .mandos_client_exited=&mandos_client_exited,
5169
 
    .password_is_read=&password_is_read,
5170
 
  };
5171
 
  task.func(task, queue);
5172
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5173
 
 
5174
 
  __attribute__((cleanup(cleanup_string)))
5175
 
    char *socket_filename = strdup("/nonexistent");
5176
 
  g_assert_nonnull(socket_filename);
5177
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
5178
 
        .func=connect_question_socket,
5179
 
        .question_filename=tempfilename,
5180
 
        .filename=socket_filename,
5181
 
        .epoll_fd=epoll_fd,
5182
 
        .password=&password,
5183
 
        .current_time=&current_time,
5184
 
        .mandos_client_exited=&mandos_client_exited,
5185
 
        .password_is_read=&password_is_read,
5186
 
      }));
5187
 
 
5188
 
  g_assert_true(queue->next_run != 0);
5189
 
 
5190
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5191
 
}
5192
 
 
5193
 
static void
5194
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5195
 
                                          test_fixture *fixture,
5196
 
                                          __attribute__((unused))
5197
 
                                          gconstpointer user_data){
5198
 
  __attribute__((cleanup(cleanup_close)))
5199
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5200
 
  g_assert_cmpint(epoll_fd, >=, 0);
5201
 
  __attribute__((cleanup(string_set_clear)))
5202
 
    string_set cancelled_filenames = {};
5203
 
  buffer password = {};
5204
 
  bool mandos_client_exited = false;
5205
 
  bool password_is_read = false;
5206
 
  __attribute__((cleanup(cleanup_queue)))
5207
 
    task_queue *queue = create_queue();
5208
 
  g_assert_nonnull(queue);
5209
 
  const mono_microsecs current_time = 0;
5210
 
 
5211
 
  __attribute__((cleanup(cleanup_string)))
5212
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5213
 
  g_assert_nonnull(tempfilename);
5214
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5215
 
  g_assert_cmpint(questionfile, >, 0);
5216
 
  FILE *qf = fdopen(questionfile, "w");
5217
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5218
 
                          PRIuMAX "\nNotAfter=\n",
5219
 
                          (uintmax_t)getpid()), >, 0);
5220
 
  g_assert_cmpint(fclose(qf), ==, 0);
5221
 
 
5222
 
  char *const filename = strdup(tempfilename);
5223
 
  g_assert_nonnull(filename);
5224
 
  task_context task = {
5225
 
    .func=open_and_parse_question,
5226
 
    .question_filename=filename,
5227
 
    .epoll_fd=epoll_fd,
5228
 
    .password=&password,
5229
 
    .filename=filename,
5230
 
    .cancelled_filenames=&cancelled_filenames,
5231
 
    .current_time=&current_time,
5232
 
    .mandos_client_exited=&mandos_client_exited,
5233
 
    .password_is_read=&password_is_read,
5234
 
  };
5235
 
  run_task_with_stderr_to_dev_null(task, queue);
5236
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5237
 
 
5238
 
  __attribute__((cleanup(cleanup_string)))
5239
 
    char *socket_filename = strdup("/nonexistent");
5240
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
5241
 
        .func=connect_question_socket,
5242
 
        .question_filename=tempfilename,
5243
 
        .filename=socket_filename,
5244
 
        .epoll_fd=epoll_fd,
5245
 
        .password=&password,
5246
 
        .current_time=&current_time,
5247
 
        .mandos_client_exited=&mandos_client_exited,
5248
 
        .password_is_read=&password_is_read,
5249
 
      }));
5250
 
  g_assert_true(queue->next_run != 0);
5251
 
 
5252
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5253
 
}
5254
 
 
5255
 
static
5256
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5257
 
                                                  current_time,
5258
 
                                                  const mono_microsecs
5259
 
                                                  notafter,
5260
 
                                                  const mono_microsecs
5261
 
                                                  next_queue_run){
5262
 
  __attribute__((cleanup(cleanup_close)))
5263
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5264
 
  g_assert_cmpint(epoll_fd, >=, 0);
5265
 
  __attribute__((cleanup(string_set_clear)))
5266
 
    string_set cancelled_filenames = {};
5267
 
  buffer password = {};
5268
 
  bool mandos_client_exited = false;
5269
 
  bool password_is_read = false;
5270
 
  __attribute__((cleanup(cleanup_queue)))
5271
 
    task_queue *queue = create_queue();
5272
 
  g_assert_nonnull(queue);
5273
 
  queue->next_run = next_queue_run;
5274
 
 
5275
 
  __attribute__((cleanup(cleanup_string)))
5276
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5277
 
  g_assert_nonnull(tempfilename);
5278
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5279
 
  g_assert_cmpint(questionfile, >, 0);
5280
 
  FILE *qf = fdopen(questionfile, "w");
5281
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5282
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5283
 
                          (uintmax_t)getpid(), notafter), >, 0);
5284
 
  g_assert_cmpint(fclose(qf), ==, 0);
5285
 
 
5286
 
  char *const filename = strdup(tempfilename);
5287
 
  g_assert_nonnull(filename);
5288
 
  task_context task = {
5289
 
    .func=open_and_parse_question,
5290
 
    .question_filename=filename,
5291
 
    .epoll_fd=epoll_fd,
5292
 
    .password=&password,
5293
 
    .filename=filename,
5294
 
    .cancelled_filenames=&cancelled_filenames,
5295
 
    .current_time=&current_time,
5296
 
    .mandos_client_exited=&mandos_client_exited,
5297
 
    .password_is_read=&password_is_read,
5298
 
  };
5299
 
  task.func(task, queue);
5300
 
 
5301
 
  if(queue->length >= 1){
5302
 
    __attribute__((cleanup(cleanup_string)))
5303
 
      char *socket_filename = strdup("/nonexistent");
5304
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5305
 
          .func=connect_question_socket,
5306
 
          .filename=socket_filename,
5307
 
          .epoll_fd=epoll_fd,
5308
 
          .password=&password,
5309
 
          .current_time=&current_time,
5310
 
          .cancelled_filenames=&cancelled_filenames,
5311
 
          .mandos_client_exited=&mandos_client_exited,
5312
 
          .password_is_read=&password_is_read,
5313
 
        }));
5314
 
    g_assert_true(queue->next_run != 0);
5315
 
  }
5316
 
 
5317
 
  if(notafter == 0){
5318
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5319
 
  } else if(current_time >= notafter) {
5320
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5321
 
  } else {
5322
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5323
 
          .func=cancel_old_question,
5324
 
          .question_filename=tempfilename,
5325
 
          .filename=tempfilename,
5326
 
          .notafter=notafter,
5327
 
          .cancelled_filenames=&cancelled_filenames,
5328
 
          .current_time=&current_time,
5329
 
        }));
5330
 
  }
5331
 
  g_assert_true(queue->next_run == 1);
5332
 
 
5333
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5334
 
}
5335
 
 
5336
 
static void
5337
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
5338
 
                                        test_fixture *fixture,
5339
 
                                        __attribute__((unused))
5340
 
                                        gconstpointer user_data){
5341
 
  /* current_time, notafter, next_queue_run */
5342
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
5343
 
}
5344
 
 
5345
 
static void
5346
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
5347
 
                                        test_fixture *fixture,
5348
 
                                        __attribute__((unused))
5349
 
                                        gconstpointer user_data){
5350
 
  /* current_time, notafter, next_queue_run */
5351
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
5352
 
}
5353
 
 
5354
 
static void
5355
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5356
 
                                          test_fixture *fixture,
5357
 
                                          __attribute__((unused))
5358
 
                                          gconstpointer user_data){
5359
 
  /* current_time, notafter, next_queue_run */
5360
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
5361
 
}
5362
 
 
5363
 
static void
5364
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5365
 
                                          test_fixture *fixture,
5366
 
                                          __attribute__((unused))
5367
 
                                          gconstpointer user_data){
5368
 
  /* current_time, notafter, next_queue_run */
5369
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
5370
 
}
5371
 
 
5372
 
static void
5373
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5374
 
                                            test_fixture *fixture,
5375
 
                                            __attribute__((unused))
5376
 
                                            gconstpointer user_data){
5377
 
  /* current_time, notafter, next_queue_run */
5378
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
5379
 
}
5380
 
 
5381
 
static void
5382
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
5383
 
                                           test_fixture *fixture,
5384
 
                                           __attribute__((unused))
5385
 
                                           gconstpointer user_data){
5386
 
  /* current_time, notafter, next_queue_run */
5387
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
5388
 
}
5389
 
 
5390
 
static void assert_cancel_old_question_param(const mono_microsecs
5391
 
                                             next_queue_run,
5392
 
                                             const mono_microsecs
5393
 
                                             notafter,
5394
 
                                             const mono_microsecs
5395
 
                                             current_time,
5396
 
                                             const mono_microsecs
5397
 
                                             next_set_to){
5398
 
  __attribute__((cleanup(string_set_clear)))
5399
 
    string_set cancelled_filenames = {};
5400
 
  __attribute__((cleanup(cleanup_queue)))
5401
 
    task_queue *queue = create_queue();
5402
 
  g_assert_nonnull(queue);
5403
 
  queue->next_run = next_queue_run;
5404
 
 
5405
 
  char *const question_filename = strdup("/nonexistent");
5406
 
  g_assert_nonnull(question_filename);
5407
 
  task_context task = {
5408
 
    .func=cancel_old_question,
5409
 
    .question_filename=question_filename,
5410
 
    .filename=question_filename,
5411
 
    .notafter=notafter,
5412
 
    .cancelled_filenames=&cancelled_filenames,
5413
 
    .current_time=&current_time,
5414
 
  };
5415
 
  task.func(task, queue);
5416
 
 
5417
 
  if(current_time >= notafter){
5418
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5419
 
    g_assert_true(string_set_contains(cancelled_filenames,
5420
 
                                      "/nonexistent"));
5421
 
  } else {
5422
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5423
 
          .func=cancel_old_question,
5424
 
          .question_filename=question_filename,
5425
 
          .filename=question_filename,
5426
 
          .notafter=notafter,
5427
 
          .cancelled_filenames=&cancelled_filenames,
5428
 
          .current_time=&current_time,
5429
 
        }));
5430
 
 
5431
 
    g_assert_false(string_set_contains(cancelled_filenames,
5432
 
                                       question_filename));
5433
 
  }
5434
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
5435
 
                   (unsigned int)next_set_to);
5436
 
}
5437
 
 
5438
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5439
 
                                           test_fixture *fixture,
5440
 
                                           __attribute__((unused))
5441
 
                                           gconstpointer user_data){
5442
 
  /* next_queue_run unset,
5443
 
     cancellation should happen because time has come,
5444
 
     next_queue_run should be unchanged */
5445
 
  /* next_queue_run, notafter, current_time, next_set_to */
5446
 
  assert_cancel_old_question_param(0, 1, 2, 0);
5447
 
}
5448
 
 
5449
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5450
 
                                           test_fixture *fixture,
5451
 
                                           __attribute__((unused))
5452
 
                                           gconstpointer user_data){
5453
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
5454
 
     and current_time is not yet notafter or greater,
5455
 
     update value of next_queue_run to value of notafter */
5456
 
  /* next_queue_run, notafter, current_time, next_set_to */
5457
 
  assert_cancel_old_question_param(0, 2, 1, 2);
5458
 
}
5459
 
 
5460
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5461
 
                                           test_fixture *fixture,
5462
 
                                           __attribute__((unused))
5463
 
                                           gconstpointer user_data){
5464
 
  /* next_queue_run 1,
5465
 
     cancellation should happen because time has come,
5466
 
     next_queue_run should be unchanged */
5467
 
  /* next_queue_run, notafter, current_time, next_set_to */
5468
 
  assert_cancel_old_question_param(1, 2, 3, 1);
5469
 
}
5470
 
 
5471
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5472
 
                                           test_fixture *fixture,
5473
 
                                           __attribute__((unused))
5474
 
                                           gconstpointer user_data){
5475
 
  /* If next_queue_run is set,
5476
 
     and current_time is not yet notafter or greater,
5477
 
     and notafter is larger than next_queue_run
5478
 
     next_queue_run should be unchanged */
5479
 
  /* next_queue_run, notafter, current_time, next_set_to */
5480
 
  assert_cancel_old_question_param(1, 3, 2, 1);
5481
 
}
5482
 
 
5483
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5484
 
                                           test_fixture *fixture,
5485
 
                                           __attribute__((unused))
5486
 
                                           gconstpointer user_data){
5487
 
  /* next_queue_run 2,
5488
 
     cancellation should happen because time has come,
5489
 
     next_queue_run should be unchanged */
5490
 
  /* next_queue_run, notafter, current_time, next_set_to */
5491
 
  assert_cancel_old_question_param(2, 1, 3, 2);
5492
 
}
5493
 
 
5494
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5495
 
                                           test_fixture *fixture,
5496
 
                                           __attribute__((unused))
5497
 
                                           gconstpointer user_data){
5498
 
  /* If next_queue_run is set,
5499
 
     and current_time is not yet notafter or greater,
5500
 
     and notafter is larger than next_queue_run
5501
 
     next_queue_run should be unchanged */
5502
 
  /* next_queue_run, notafter, current_time, next_set_to */
5503
 
  assert_cancel_old_question_param(2, 3, 1, 2);
5504
 
}
5505
 
 
5506
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5507
 
                                           test_fixture *fixture,
5508
 
                                           __attribute__((unused))
5509
 
                                           gconstpointer user_data){
5510
 
  /* next_queue_run 3,
5511
 
     cancellation should happen because time has come,
5512
 
     next_queue_run should be unchanged */
5513
 
  /* next_queue_run, notafter, current_time, next_set_to */
5514
 
  assert_cancel_old_question_param(3, 1, 2, 3);
5515
 
}
5516
 
 
5517
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5518
 
                                           test_fixture *fixture,
5519
 
                                           __attribute__((unused))
5520
 
                                           gconstpointer user_data){
5521
 
  /* If next_queue_run is set,
5522
 
     and current_time is not yet notafter or greater,
5523
 
     and notafter is smaller than next_queue_run
5524
 
     update value of next_queue_run to value of notafter */
5525
 
  /* next_queue_run, notafter, current_time, next_set_to */
5526
 
  assert_cancel_old_question_param(3, 2, 1, 2);
5527
 
}
5528
 
 
5529
 
static void
5530
 
test_connect_question_socket_name_too_long(__attribute__((unused))
5531
 
                                           test_fixture *fixture,
5532
 
                                           __attribute__((unused))
5533
 
                                           gconstpointer user_data){
5534
 
  __attribute__((cleanup(cleanup_close)))
5535
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5536
 
  g_assert_cmpint(epoll_fd, >=, 0);
5537
 
  const char question_filename[] = "/nonexistent/question";
5538
 
  __attribute__((cleanup(string_set_clear)))
5539
 
    string_set cancelled_filenames = {};
5540
 
  __attribute__((cleanup(cleanup_queue)))
5541
 
    task_queue *queue = create_queue();
5542
 
  g_assert_nonnull(queue);
5543
 
  __attribute__((cleanup(cleanup_string)))
5544
 
    char *tempdir = make_temporary_directory();
5545
 
  g_assert_nonnull(tempdir);
5546
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5547
 
  char socket_name[sizeof(unix_socket.sun_path)];
5548
 
  memset(socket_name, 'x', sizeof(socket_name));
5549
 
  socket_name[sizeof(socket_name)-1] = '\0';
5550
 
  char *filename = NULL;
5551
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5552
 
                  >, 0);
5553
 
  g_assert_nonnull(filename);
5554
 
 
5555
 
  task_context task = {
5556
 
    .func=connect_question_socket,
5557
 
    .question_filename=strdup(question_filename),
5558
 
    .epoll_fd=epoll_fd,
5559
 
    .password=(buffer[]){{}},
5560
 
    .filename=filename,
5561
 
    .cancelled_filenames=&cancelled_filenames,
5562
 
    .mandos_client_exited=(bool[]){false},
5563
 
    .password_is_read=(bool[]){false},
5564
 
    .current_time=(mono_microsecs[]){0},
5565
 
  };
5566
 
  g_assert_nonnull(task.question_filename);
5567
 
  run_task_with_stderr_to_dev_null(task, queue);
5568
 
 
5569
 
  g_assert_true(string_set_contains(cancelled_filenames,
5570
 
                                    question_filename));
5571
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5572
 
  g_assert_true(queue->next_run == 0);
5573
 
 
5574
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5575
 
}
5576
 
 
5577
 
static
5578
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
5579
 
                                               test_fixture *fixture,
5580
 
                                               __attribute__((unused))
5581
 
                                               gconstpointer
5582
 
                                               user_data){
5583
 
  __attribute__((cleanup(cleanup_close)))
5584
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5585
 
  g_assert_cmpint(epoll_fd, >=, 0);
5586
 
  const char question_filename[] = "/nonexistent/question";
5587
 
  __attribute__((cleanup(string_set_clear)))
5588
 
    string_set cancelled_filenames = {};
5589
 
  const mono_microsecs current_time = 3;
5590
 
  __attribute__((cleanup(cleanup_queue)))
5591
 
    task_queue *queue = create_queue();
5592
 
  g_assert_nonnull(queue);
5593
 
  __attribute__((cleanup(cleanup_string)))
5594
 
    char *tempdir = make_temporary_directory();
5595
 
  g_assert_nonnull(tempdir);
5596
 
  char socket_name[] = "nonexistent";
5597
 
  char *filename = NULL;
5598
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5599
 
                  >, 0);
5600
 
  g_assert_nonnull(filename);
5601
 
 
5602
 
  task_context task = {
5603
 
    .func=connect_question_socket,
5604
 
    .question_filename=strdup(question_filename),
5605
 
    .epoll_fd=epoll_fd,
5606
 
    .password=(buffer[]){{}},
5607
 
    .filename=filename,
5608
 
    .cancelled_filenames=&cancelled_filenames,
5609
 
    .mandos_client_exited=(bool[]){false},
5610
 
    .password_is_read=(bool[]){false},
5611
 
    .current_time=&current_time,
5612
 
  };
5613
 
  g_assert_nonnull(task.question_filename);
5614
 
  run_task_with_stderr_to_dev_null(task, queue);
5615
 
 
5616
 
  g_assert_nonnull(find_matching_task(queue, task));
5617
 
 
5618
 
  g_assert_false(string_set_contains(cancelled_filenames,
5619
 
                                     question_filename));
5620
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5621
 
  g_assert_true(queue->next_run == 1000000 + current_time);
5622
 
 
5623
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5624
 
}
5625
 
 
5626
 
static
5627
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5628
 
                                            test_fixture *fixture,
5629
 
                                            __attribute__((unused))
5630
 
                                            gconstpointer user_data){
5631
 
  __attribute__((cleanup(cleanup_close)))
5632
 
    const int epoll_fd = open("/dev/null",
5633
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5634
 
  __attribute__((cleanup(cleanup_string)))
5635
 
    char *const question_filename = strdup("/nonexistent/question");
5636
 
  g_assert_nonnull(question_filename);
5637
 
  __attribute__((cleanup(string_set_clear)))
5638
 
    string_set cancelled_filenames = {};
5639
 
  const mono_microsecs current_time = 5;
5640
 
  __attribute__((cleanup(cleanup_queue)))
5641
 
    task_queue *queue = create_queue();
5642
 
  g_assert_nonnull(queue);
5643
 
  __attribute__((cleanup(cleanup_string)))
5644
 
    char *tempdir = make_temporary_directory();
5645
 
  g_assert_nonnull(tempdir);
5646
 
  __attribute__((cleanup(cleanup_close)))
5647
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5648
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5649
 
  g_assert_cmpint(sock_fd, >=, 0);
5650
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5651
 
  const char socket_name[] = "socket_name";
5652
 
  __attribute__((cleanup(cleanup_string)))
5653
 
    char *filename = NULL;
5654
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5655
 
                  >, 0);
5656
 
  g_assert_nonnull(filename);
5657
 
  g_assert_cmpint((int)strlen(filename), <,
5658
 
                  (int)sizeof(sock_name.sun_path));
5659
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5660
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5661
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5662
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
5663
 
  task_context task = {
5664
 
    .func=connect_question_socket,
5665
 
    .question_filename=strdup(question_filename),
5666
 
    .epoll_fd=epoll_fd,
5667
 
    .password=(buffer[]){{}},
5668
 
    .filename=strdup(filename),
5669
 
    .cancelled_filenames=&cancelled_filenames,
5670
 
    .mandos_client_exited=(bool[]){false},
5671
 
    .password_is_read=(bool[]){false},
5672
 
    .current_time=&current_time,
5673
 
  };
5674
 
  g_assert_nonnull(task.question_filename);
5675
 
  run_task_with_stderr_to_dev_null(task, queue);
5676
 
 
5677
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5678
 
  const task_context *const added_task
5679
 
    = find_matching_task(queue, task);
5680
 
  g_assert_nonnull(added_task);
5681
 
  g_assert_true(queue->next_run == 1000000 + current_time);
5682
 
 
5683
 
  g_assert_cmpint(unlink(filename), ==, 0);
5684
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5685
 
}
5686
 
 
5687
 
static
5688
 
void test_connect_question_socket_usable(__attribute__((unused))
5689
 
                                         test_fixture *fixture,
5690
 
                                         __attribute__((unused))
5691
 
                                         gconstpointer user_data){
5692
 
  __attribute__((cleanup(cleanup_close)))
5693
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5694
 
  g_assert_cmpint(epoll_fd, >=, 0);
5695
 
  __attribute__((cleanup(cleanup_string)))
5696
 
    char *const question_filename = strdup("/nonexistent/question");
5697
 
  g_assert_nonnull(question_filename);
5698
 
  __attribute__((cleanup(string_set_clear)))
5699
 
    string_set cancelled_filenames = {};
5700
 
  buffer password = {};
5701
 
  bool mandos_client_exited = false;
5702
 
  bool password_is_read = false;
5703
 
  const mono_microsecs current_time = 0;
5704
 
  __attribute__((cleanup(cleanup_queue)))
5705
 
    task_queue *queue = create_queue();
5706
 
  g_assert_nonnull(queue);
5707
 
  __attribute__((cleanup(cleanup_string)))
5708
 
    char *tempdir = make_temporary_directory();
5709
 
  g_assert_nonnull(tempdir);
5710
 
  __attribute__((cleanup(cleanup_close)))
5711
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5712
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5713
 
  g_assert_cmpint(sock_fd, >=, 0);
5714
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5715
 
  const char socket_name[] = "socket_name";
5716
 
  __attribute__((cleanup(cleanup_string)))
5717
 
    char *filename = NULL;
5718
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5719
 
                  >, 0);
5720
 
  g_assert_nonnull(filename);
5721
 
  g_assert_cmpint((int)strlen(filename), <,
5722
 
                  (int)sizeof(sock_name.sun_path));
5723
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5724
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5725
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5726
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
5727
 
  task_context task = {
5728
 
    .func=connect_question_socket,
5729
 
    .question_filename=strdup(question_filename),
5730
 
    .epoll_fd=epoll_fd,
5731
 
    .password=&password,
5732
 
    .filename=strdup(filename),
5733
 
    .cancelled_filenames=&cancelled_filenames,
5734
 
    .mandos_client_exited=&mandos_client_exited,
5735
 
    .password_is_read=&password_is_read,
5736
 
    .current_time=&current_time,
5737
 
  };
5738
 
  g_assert_nonnull(task.question_filename);
5739
 
  task.func(task, queue);
5740
 
 
5741
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5742
 
  const task_context *const added_task
5743
 
    = find_matching_task(queue, (task_context){
5744
 
        .func=send_password_to_socket,
5745
 
        .question_filename=question_filename,
5746
 
        .filename=filename,
5747
 
        .epoll_fd=epoll_fd,
5748
 
        .password=&password,
5749
 
        .cancelled_filenames=&cancelled_filenames,
5750
 
        .mandos_client_exited=&mandos_client_exited,
5751
 
        .password_is_read=&password_is_read,
5752
 
        .current_time=&current_time,
5753
 
      });
5754
 
  g_assert_nonnull(added_task);
5755
 
  g_assert_cmpint(added_task->fd, >, 0);
5756
 
 
5757
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5758
 
                                   EPOLLOUT));
5759
 
 
5760
 
  const int fd = added_task->fd;
5761
 
  g_assert_cmpint(fd, >, 0);
5762
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
5763
 
 
5764
 
  /* write to fd */
5765
 
  char write_data[PIPE_BUF];
5766
 
  {
5767
 
    /* Construct test password buffer */
5768
 
    /* Start with + since that is what the real procotol uses */
5769
 
    write_data[0] = '+';
5770
 
    /* Set a special character at string end just to mark the end */
5771
 
    write_data[sizeof(write_data)-2] = 'y';
5772
 
    /* Set NUL at buffer end, as suggested by the protocol */
5773
 
    write_data[sizeof(write_data)-1] = '\0';
5774
 
    /* Fill rest of password with 'x' */
5775
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
5776
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5777
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
5778
 
  }
5779
 
 
5780
 
  /* read from sock_fd */
5781
 
  char read_data[sizeof(write_data)];
5782
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5783
 
                  ==, sizeof(read_data));
5784
 
 
5785
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5786
 
                == 0);
5787
 
 
5788
 
  /* writing to sock_fd should fail */
5789
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5790
 
                       MSG_NOSIGNAL), <, 0);
5791
 
 
5792
 
  /* reading from fd should fail */
5793
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5794
 
                            MSG_NOSIGNAL), <, 0);
5795
 
 
5796
 
  g_assert_cmpint(unlink(filename), ==, 0);
5797
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5798
 
}
5799
 
 
5800
 
static void
5801
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5802
 
                                               test_fixture *fixture,
5803
 
                                               __attribute__((unused))
5804
 
                                               gconstpointer
5805
 
                                               user_data){
5806
 
  __attribute__((cleanup(cleanup_close)))
5807
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5808
 
  g_assert_cmpint(epoll_fd, >=, 0);
5809
 
  __attribute__((cleanup(cleanup_string)))
5810
 
    char *const question_filename = strdup("/nonexistent/question");
5811
 
  g_assert_nonnull(question_filename);
5812
 
  __attribute__((cleanup(cleanup_string)))
5813
 
    char *const filename = strdup("/nonexistent/socket");
5814
 
  g_assert_nonnull(filename);
5815
 
  __attribute__((cleanup(string_set_clear)))
5816
 
    string_set cancelled_filenames = {};
5817
 
  buffer password = {};
5818
 
  bool password_is_read = true;
5819
 
  __attribute__((cleanup(cleanup_queue)))
5820
 
    task_queue *queue = create_queue();
5821
 
  g_assert_nonnull(queue);
5822
 
  int socketfds[2];
5823
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5824
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5825
 
                             socketfds), ==, 0);
5826
 
  __attribute__((cleanup(cleanup_close)))
5827
 
    const int read_socket = socketfds[0];
5828
 
  const int write_socket = socketfds[1];
5829
 
  task_context task = {
5830
 
    .func=send_password_to_socket,
5831
 
    .question_filename=strdup(question_filename),
5832
 
    .filename=strdup(filename),
5833
 
    .epoll_fd=epoll_fd,
5834
 
    .fd=write_socket,
5835
 
    .password=&password,
5836
 
    .cancelled_filenames=&cancelled_filenames,
5837
 
    .mandos_client_exited=(bool[]){false},
5838
 
    .password_is_read=&password_is_read,
5839
 
    .current_time=(mono_microsecs[]){0},
5840
 
  };
5841
 
  g_assert_nonnull(task.question_filename);
5842
 
 
5843
 
  task.func(task, queue);
5844
 
 
5845
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5846
 
 
5847
 
  const task_context *const added_task
5848
 
    = find_matching_task(queue, task);
5849
 
  g_assert_nonnull(added_task);
5850
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5851
 
  g_assert_true(password_is_read);
5852
 
 
5853
 
  g_assert_cmpint(added_task->fd, >, 0);
5854
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5855
 
                                   EPOLLOUT));
5856
 
}
5857
 
 
5858
 
static void
5859
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
5860
 
                                               test_fixture *fixture,
5861
 
                                               __attribute__((unused))
5862
 
                                               gconstpointer
5863
 
                                               user_data){
5864
 
  __attribute__((cleanup(cleanup_close)))
5865
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5866
 
  g_assert_cmpint(epoll_fd, >=, 0);
5867
 
  __attribute__((cleanup(cleanup_string)))
5868
 
    char *const question_filename = strdup("/nonexistent/question");
5869
 
  g_assert_nonnull(question_filename);
5870
 
  __attribute__((cleanup(cleanup_string)))
5871
 
    char *const filename = strdup("/nonexistent/socket");
5872
 
  __attribute__((cleanup(string_set_clear)))
5873
 
    string_set cancelled_filenames = {};
5874
 
  buffer password = {};
5875
 
  __attribute__((cleanup(cleanup_queue)))
5876
 
    task_queue *queue = create_queue();
5877
 
  g_assert_nonnull(queue);
5878
 
  int socketfds[2];
5879
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5880
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5881
 
                             socketfds), ==, 0);
5882
 
  __attribute__((cleanup(cleanup_close)))
5883
 
    const int read_socket = socketfds[0];
5884
 
  const int write_socket = socketfds[1];
5885
 
  task_context task = {
5886
 
    .func=send_password_to_socket,
5887
 
    .question_filename=strdup(question_filename),
5888
 
    .filename=strdup(filename),
5889
 
    .epoll_fd=epoll_fd,
5890
 
    .fd=write_socket,
5891
 
    .password=&password,
5892
 
    .cancelled_filenames=&cancelled_filenames,
5893
 
    .mandos_client_exited=(bool[]){false},
5894
 
    .password_is_read=(bool[]){false},
5895
 
    .current_time=(mono_microsecs[]){0},
5896
 
  };
5897
 
  g_assert_nonnull(task.question_filename);
5898
 
 
5899
 
  task.func(task, queue);
5900
 
 
5901
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5902
 
 
5903
 
  const task_context *const added_task = find_matching_task(queue,
5904
 
                                                            task);
5905
 
  g_assert_nonnull(added_task);
5906
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5907
 
  g_assert_true(queue->next_run == 0);
5908
 
 
5909
 
  g_assert_cmpint(added_task->fd, >, 0);
5910
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5911
 
                                   EPOLLOUT));
5912
 
}
5913
 
 
5914
 
static
5915
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5916
 
                                           test_fixture *fixture,
5917
 
                                           __attribute__((unused))
5918
 
                                           gconstpointer user_data){
5919
 
  __attribute__((cleanup(cleanup_close)))
5920
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5921
 
  g_assert_cmpint(epoll_fd, >=, 0);
5922
 
  const char question_filename[] = "/nonexistent/question";
5923
 
  char *const filename = strdup("/nonexistent/socket");
5924
 
  __attribute__((cleanup(string_set_clear)))
5925
 
    string_set cancelled_filenames = {};
5926
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5927
 
  __attribute__((cleanup(cleanup_buffer)))
5928
 
    buffer password = {
5929
 
    .data=malloc(oversized),
5930
 
    .length=oversized,
5931
 
    .allocated=oversized,
5932
 
  };
5933
 
  g_assert_nonnull(password.data);
5934
 
  if(mlock(password.data, password.allocated) != 0){
5935
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
5936
 
  }
5937
 
  /* Construct test password buffer */
5938
 
  /* Start with + since that is what the real procotol uses */
5939
 
  password.data[0] = '+';
5940
 
  /* Set a special character at string end just to mark the end */
5941
 
  password.data[oversized-3] = 'y';
5942
 
  /* Set NUL at buffer end, as suggested by the protocol */
5943
 
  password.data[oversized-2] = '\0';
5944
 
  /* Fill rest of password with 'x' */
5945
 
  memset(password.data+1, 'x', oversized-3);
5946
 
 
5947
 
  __attribute__((cleanup(cleanup_queue)))
5948
 
    task_queue *queue = create_queue();
5949
 
  g_assert_nonnull(queue);
5950
 
  int socketfds[2];
5951
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5952
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5953
 
                             socketfds), ==, 0);
5954
 
  __attribute__((cleanup(cleanup_close)))
5955
 
    const int read_socket = socketfds[0];
5956
 
  __attribute__((cleanup(cleanup_close)))
5957
 
    const int write_socket = socketfds[1];
5958
 
  task_context task = {
5959
 
    .func=send_password_to_socket,
5960
 
    .question_filename=strdup(question_filename),
5961
 
    .filename=filename,
5962
 
    .epoll_fd=epoll_fd,
5963
 
    .fd=write_socket,
5964
 
    .password=&password,
5965
 
    .cancelled_filenames=&cancelled_filenames,
5966
 
    .mandos_client_exited=(bool[]){true},
5967
 
    .password_is_read=(bool[]){true},
5968
 
    .current_time=(mono_microsecs[]){0},
5969
 
  };
5970
 
  g_assert_nonnull(task.question_filename);
5971
 
 
5972
 
  run_task_with_stderr_to_dev_null(task, queue);
5973
 
 
5974
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5975
 
  g_assert_true(string_set_contains(cancelled_filenames,
5976
 
                                    question_filename));
5977
 
}
5978
 
 
5979
 
static void test_send_password_to_socket_retry(__attribute__((unused))
5980
 
                                               test_fixture *fixture,
5981
 
                                               __attribute__((unused))
5982
 
                                               gconstpointer
5983
 
                                               user_data){
5984
 
  __attribute__((cleanup(cleanup_close)))
5985
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5986
 
  g_assert_cmpint(epoll_fd, >=, 0);
5987
 
  __attribute__((cleanup(cleanup_string)))
5988
 
    char *const question_filename = strdup("/nonexistent/question");
5989
 
  g_assert_nonnull(question_filename);
5990
 
  __attribute__((cleanup(cleanup_string)))
5991
 
    char *const filename = strdup("/nonexistent/socket");
5992
 
  g_assert_nonnull(filename);
5993
 
  __attribute__((cleanup(string_set_clear)))
5994
 
    string_set cancelled_filenames = {};
5995
 
  __attribute__((cleanup(cleanup_buffer)))
5996
 
    buffer password = {};
5997
 
 
5998
 
  __attribute__((cleanup(cleanup_queue)))
5999
 
    task_queue *queue = create_queue();
6000
 
  g_assert_nonnull(queue);
6001
 
  int socketfds[2];
6002
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6003
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6004
 
                             socketfds), ==, 0);
6005
 
  __attribute__((cleanup(cleanup_close)))
6006
 
    const int read_socket = socketfds[0];
6007
 
  const int write_socket = socketfds[1];
6008
 
  /* Close the server side socket to force ECONNRESET on client */
6009
 
  g_assert_cmpint(close(read_socket), ==, 0);
6010
 
  task_context task = {
6011
 
    .func=send_password_to_socket,
6012
 
    .question_filename=strdup(question_filename),
6013
 
    .filename=strdup(filename),
6014
 
    .epoll_fd=epoll_fd,
6015
 
    .fd=write_socket,
6016
 
    .password=&password,
6017
 
    .cancelled_filenames=&cancelled_filenames,
6018
 
    .mandos_client_exited=(bool[]){true},
6019
 
    .password_is_read=(bool[]){true},
6020
 
    .current_time=(mono_microsecs[]){0},
6021
 
  };
6022
 
  g_assert_nonnull(task.question_filename);
6023
 
 
6024
 
  task.func(task, queue);
6025
 
 
6026
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6027
 
 
6028
 
  const task_context *const added_task = find_matching_task(queue,
6029
 
                                                            task);
6030
 
  g_assert_nonnull(added_task);
6031
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
6032
 
 
6033
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6034
 
                                   EPOLLOUT));
6035
 
}
6036
 
 
6037
 
static
6038
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6039
 
                                            test_fixture *fixture,
6040
 
                                            __attribute__((unused))
6041
 
                                            gconstpointer user_data){
6042
 
  __attribute__((cleanup(cleanup_close)))
6043
 
    const int epoll_fd = open("/dev/null",
6044
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
6045
 
  __attribute__((cleanup(cleanup_string)))
6046
 
    char *const question_filename = strdup("/nonexistent/question");
6047
 
  g_assert_nonnull(question_filename);
6048
 
  __attribute__((cleanup(cleanup_string)))
6049
 
    char *const filename = strdup("/nonexistent/socket");
6050
 
  g_assert_nonnull(filename);
6051
 
  __attribute__((cleanup(string_set_clear)))
6052
 
    string_set cancelled_filenames = {};
6053
 
  __attribute__((cleanup(cleanup_buffer)))
6054
 
    buffer password = {};
6055
 
 
6056
 
  const mono_microsecs current_time = 11;
6057
 
  __attribute__((cleanup(cleanup_queue)))
6058
 
    task_queue *queue = create_queue();
6059
 
  g_assert_nonnull(queue);
6060
 
  int socketfds[2];
6061
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6062
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6063
 
                             socketfds), ==, 0);
6064
 
  __attribute__((cleanup(cleanup_close)))
6065
 
    const int read_socket = socketfds[0];
6066
 
  const int write_socket = socketfds[1];
6067
 
  /* Close the server side socket to force ECONNRESET on client */
6068
 
  g_assert_cmpint(close(read_socket), ==, 0);
6069
 
  task_context task = {
6070
 
    .func=send_password_to_socket,
6071
 
    .question_filename=strdup(question_filename),
6072
 
    .filename=strdup(filename),
6073
 
    .epoll_fd=epoll_fd,
6074
 
    .fd=write_socket,
6075
 
    .password=&password,
6076
 
    .cancelled_filenames=&cancelled_filenames,
6077
 
    .mandos_client_exited=(bool[]){true},
6078
 
    .password_is_read=(bool[]){true},
6079
 
    .current_time=&current_time,
6080
 
  };
6081
 
  g_assert_nonnull(task.question_filename);
6082
 
 
6083
 
  run_task_with_stderr_to_dev_null(task, queue);
6084
 
 
6085
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6086
 
 
6087
 
  const task_context *const added_task = find_matching_task(queue,
6088
 
                                                            task);
6089
 
  g_assert_nonnull(added_task);
6090
 
  g_assert_true(queue->next_run == current_time + 1000000);
6091
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
6092
 
}
6093
 
 
6094
 
static void assert_send_password_to_socket_password(buffer password){
6095
 
  __attribute__((cleanup(cleanup_close)))
6096
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6097
 
  g_assert_cmpint(epoll_fd, >=, 0);
6098
 
  char *const question_filename = strdup("/nonexistent/question");
6099
 
  g_assert_nonnull(question_filename);
6100
 
  char *const filename = strdup("/nonexistent/socket");
6101
 
  g_assert_nonnull(filename);
6102
 
  __attribute__((cleanup(string_set_clear)))
6103
 
    string_set cancelled_filenames = {};
6104
 
 
6105
 
  __attribute__((cleanup(cleanup_queue)))
6106
 
    task_queue *queue = create_queue();
6107
 
  g_assert_nonnull(queue);
6108
 
  int socketfds[2];
6109
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6110
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6111
 
                             socketfds), ==, 0);
6112
 
  __attribute__((cleanup(cleanup_close)))
6113
 
    const int read_socket = socketfds[0];
6114
 
  const int write_socket = socketfds[1];
6115
 
  task_context task = {
6116
 
    .func=send_password_to_socket,
6117
 
    .question_filename=question_filename,
6118
 
    .filename=filename,
6119
 
    .epoll_fd=epoll_fd,
6120
 
    .fd=write_socket,
6121
 
    .password=&password,
6122
 
    .cancelled_filenames=&cancelled_filenames,
6123
 
    .mandos_client_exited=(bool[]){true},
6124
 
    .password_is_read=(bool[]){true},
6125
 
    .current_time=(mono_microsecs[]){0},
6126
 
  };
6127
 
 
6128
 
  char *expected_written_data = malloc(password.length + 2);
6129
 
  g_assert_nonnull(expected_written_data);
6130
 
  expected_written_data[0] = '+';
6131
 
  expected_written_data[password.length + 1] = '\0';
6132
 
  if(password.length > 0){
6133
 
    g_assert_nonnull(password.data);
6134
 
    memcpy(expected_written_data + 1, password.data, password.length);
6135
 
  }
6136
 
 
6137
 
  task.func(task, queue);
6138
 
 
6139
 
  char buf[PIPE_BUF];
6140
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6141
 
                  (int)(password.length + 2));
6142
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6143
 
 
6144
 
  g_assert_true(memcmp(expected_written_data, buf,
6145
 
                       password.length + 2) == 0);
6146
 
 
6147
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6148
 
 
6149
 
  free(expected_written_data);
6150
 
}
6151
 
 
6152
 
static void
6153
 
test_send_password_to_socket_null_password(__attribute__((unused))
6154
 
                                           test_fixture *fixture,
6155
 
                                           __attribute__((unused))
6156
 
                                           gconstpointer user_data){
6157
 
  __attribute__((cleanup(cleanup_buffer)))
6158
 
    buffer password = {};
6159
 
  assert_send_password_to_socket_password(password);
6160
 
}
6161
 
 
6162
 
static void
6163
 
test_send_password_to_socket_empty_password(__attribute__((unused))
6164
 
                                            test_fixture *fixture,
6165
 
                                            __attribute__((unused))
6166
 
                                            gconstpointer user_data){
6167
 
  __attribute__((cleanup(cleanup_buffer)))
6168
 
    buffer password = {
6169
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
6170
 
    .length=0,
6171
 
    .allocated=0,               /* deliberate lie */
6172
 
  };
6173
 
  g_assert_nonnull(password.data);
6174
 
  assert_send_password_to_socket_password(password);
6175
 
}
6176
 
 
6177
 
static void
6178
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6179
 
                                            test_fixture *fixture,
6180
 
                                            __attribute__((unused))
6181
 
                                            gconstpointer user_data){
6182
 
  __attribute__((cleanup(cleanup_buffer)))
6183
 
    buffer password = {
6184
 
    .data=strdup(""),
6185
 
    .length=0,
6186
 
    .allocated=1,
6187
 
  };
6188
 
  if(mlock(password.data, password.allocated) != 0){
6189
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6190
 
  }
6191
 
  assert_send_password_to_socket_password(password);
6192
 
}
6193
 
 
6194
 
static void
6195
 
test_send_password_to_socket_text_password(__attribute__((unused))
6196
 
                                           test_fixture *fixture,
6197
 
                                           __attribute__((unused))
6198
 
                                           gconstpointer user_data){
6199
 
  const char dummy_test_password[] = "dummy test password";
6200
 
  __attribute__((cleanup(cleanup_buffer)))
6201
 
    buffer password = {
6202
 
    .data = strdup(dummy_test_password),
6203
 
    .length = strlen(dummy_test_password),
6204
 
    .allocated = sizeof(dummy_test_password),
6205
 
  };
6206
 
  if(mlock(password.data, password.allocated) != 0){
6207
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6208
 
  }
6209
 
  assert_send_password_to_socket_password(password);
6210
 
}
6211
 
 
6212
 
static void
6213
 
test_send_password_to_socket_binary_password(__attribute__((unused))
6214
 
                                             test_fixture *fixture,
6215
 
                                             __attribute__((unused))
6216
 
                                             gconstpointer user_data){
6217
 
  __attribute__((cleanup(cleanup_buffer)))
6218
 
    buffer password = {
6219
 
    .data=malloc(255),
6220
 
    .length=255,
6221
 
    .allocated=255,
6222
 
  };
6223
 
  g_assert_nonnull(password.data);
6224
 
  if(mlock(password.data, password.allocated) != 0){
6225
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6226
 
  }
6227
 
  char c = 1;                   /* Start at 1, avoiding NUL */
6228
 
  for(int i=0; i < 255; i++){
6229
 
    password.data[i] = c++;
6230
 
  }
6231
 
  assert_send_password_to_socket_password(password);
6232
 
}
6233
 
 
6234
 
static void
6235
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6236
 
                                              test_fixture *fixture,
6237
 
                                              __attribute__((unused))
6238
 
                                              gconstpointer
6239
 
                                              user_data){
6240
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6241
 
  __attribute__((cleanup(cleanup_buffer)))
6242
 
    buffer password = {
6243
 
    .data=malloc(sizeof(test_password)),
6244
 
    .length=sizeof(test_password),
6245
 
    .allocated=sizeof(test_password),
6246
 
  };
6247
 
  g_assert_nonnull(password.data);
6248
 
  if(mlock(password.data, password.allocated) !=0){
6249
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6250
 
  }
6251
 
  memcpy(password.data, test_password, password.allocated);
6252
 
  assert_send_password_to_socket_password(password);
6253
 
}
6254
 
 
6255
 
static bool assert_add_existing_questions_to_devnull(task_queue
6256
 
                                                     *const,
6257
 
                                                     const int,
6258
 
                                                     buffer *const,
6259
 
                                                     string_set *,
6260
 
                                                     const
6261
 
                                                     mono_microsecs
6262
 
                                                     *const,
6263
 
                                                     bool *const,
6264
 
                                                     bool *const,
6265
 
                                                     const char
6266
 
                                                     *const);
6267
 
 
6268
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6269
 
                                               test_fixture *fixture,
6270
 
                                               __attribute__((unused))
6271
 
                                               gconstpointer
6272
 
                                               user_data){
6273
 
  __attribute__((cleanup(cleanup_queue)))
6274
 
    task_queue *queue = create_queue();
6275
 
  g_assert_nonnull(queue);
6276
 
  __attribute__((cleanup(cleanup_close)))
6277
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6278
 
  g_assert_cmpint(epoll_fd, >=, 0);
6279
 
  __attribute__((cleanup(string_set_clear)))
6280
 
    string_set cancelled_filenames = {};
6281
 
 
6282
 
  g_assert_false(assert_add_existing_questions_to_devnull
6283
 
                 (queue,
6284
 
                  epoll_fd,
6285
 
                  (buffer[]){{}}, /* password */
6286
 
                  &cancelled_filenames,
6287
 
                  (mono_microsecs[]){0}, /* current_time */
6288
 
                  (bool[]){false},       /* mandos_client_exited */
6289
 
                  (bool[]){false},       /* password_is_read */
6290
 
                  "/nonexistent"));      /* dirname */
6291
 
 
6292
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6293
 
}
6294
 
 
6295
 
static
6296
 
bool assert_add_existing_questions_to_devnull(task_queue
6297
 
                                              *const queue,
6298
 
                                              const int
6299
 
                                              epoll_fd,
6300
 
                                              buffer *const
6301
 
                                              password,
6302
 
                                              string_set
6303
 
                                              *cancelled_filenames,
6304
 
                                              const mono_microsecs
6305
 
                                              *const current_time,
6306
 
                                              bool *const
6307
 
                                              mandos_client_exited,
6308
 
                                              bool *const
6309
 
                                              password_is_read,
6310
 
                                              const char *const
6311
 
                                              dirname){
6312
 
  __attribute__((cleanup(cleanup_close)))
6313
 
    const int devnull_fd = open("/dev/null",
6314
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
6315
 
  g_assert_cmpint(devnull_fd, >=, 0);
6316
 
  __attribute__((cleanup(cleanup_close)))
6317
 
    const int real_stderr_fd = dup(STDERR_FILENO);
6318
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
6319
 
  dup2(devnull_fd, STDERR_FILENO);
6320
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
6321
 
                                          cancelled_filenames,
6322
 
                                          current_time,
6323
 
                                          mandos_client_exited,
6324
 
                                          password_is_read, dirname);
6325
 
  dup2(real_stderr_fd, STDERR_FILENO);
6326
 
  return ret;
6327
 
}
6328
 
 
6329
 
static
6330
 
void test_add_existing_questions_no_questions(__attribute__((unused))
6331
 
                                              test_fixture *fixture,
6332
 
                                              __attribute__((unused))
6333
 
                                              gconstpointer
6334
 
                                              user_data){
6335
 
  __attribute__((cleanup(cleanup_queue)))
6336
 
    task_queue *queue = create_queue();
6337
 
  g_assert_nonnull(queue);
6338
 
  __attribute__((cleanup(cleanup_close)))
6339
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6340
 
  g_assert_cmpint(epoll_fd, >=, 0);
6341
 
  __attribute__((cleanup(string_set_clear)))
6342
 
    string_set cancelled_filenames = {};
6343
 
  __attribute__((cleanup(cleanup_string)))
6344
 
    char *tempdir = make_temporary_directory();
6345
 
  g_assert_nonnull(tempdir);
6346
 
 
6347
 
  g_assert_false(assert_add_existing_questions_to_devnull
6348
 
                 (queue,
6349
 
                  epoll_fd,
6350
 
                  (buffer[]){{}}, /* password */
6351
 
                  &cancelled_filenames,
6352
 
                  (mono_microsecs[]){0}, /* current_time */
6353
 
                  (bool[]){false},       /* mandos_client_exited */
6354
 
                  (bool[]){false},       /* password_is_read */
6355
 
                  tempdir));
6356
 
 
6357
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6358
 
 
6359
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6360
 
}
6361
 
 
6362
 
static char *make_question_file_in_directory(const char *const);
6363
 
 
6364
 
static
6365
 
void test_add_existing_questions_one_question(__attribute__((unused))
6366
 
                                              test_fixture *fixture,
6367
 
                                              __attribute__((unused))
6368
 
                                              gconstpointer
6369
 
                                              user_data){
6370
 
  __attribute__((cleanup(cleanup_queue)))
6371
 
    task_queue *queue = create_queue();
6372
 
  g_assert_nonnull(queue);
6373
 
  __attribute__((cleanup(cleanup_close)))
6374
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6375
 
  g_assert_cmpint(epoll_fd, >=, 0);
6376
 
  __attribute__((cleanup(cleanup_buffer)))
6377
 
    buffer password = {};
6378
 
  __attribute__((cleanup(string_set_clear)))
6379
 
    string_set cancelled_filenames = {};
6380
 
  const mono_microsecs current_time = 0;
6381
 
  bool mandos_client_exited = false;
6382
 
  bool password_is_read = false;
6383
 
  __attribute__((cleanup(cleanup_string)))
6384
 
    char *tempdir = make_temporary_directory();
6385
 
  g_assert_nonnull(tempdir);
6386
 
  __attribute__((cleanup(cleanup_string)))
6387
 
    char *question_filename
6388
 
    = make_question_file_in_directory(tempdir);
6389
 
  g_assert_nonnull(question_filename);
6390
 
 
6391
 
  g_assert_true(assert_add_existing_questions_to_devnull
6392
 
                (queue,
6393
 
                 epoll_fd,
6394
 
                 &password,
6395
 
                 &cancelled_filenames,
6396
 
                 &current_time,
6397
 
                 &mandos_client_exited,
6398
 
                 &password_is_read,
6399
 
                 tempdir));
6400
 
 
6401
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6402
 
 
6403
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6404
 
        .func=open_and_parse_question,
6405
 
        .epoll_fd=epoll_fd,
6406
 
        .filename=question_filename,
6407
 
        .question_filename=question_filename,
6408
 
        .password=&password,
6409
 
        .cancelled_filenames=&cancelled_filenames,
6410
 
        .current_time=&current_time,
6411
 
        .mandos_client_exited=&mandos_client_exited,
6412
 
        .password_is_read=&password_is_read,
6413
 
      }));
6414
 
 
6415
 
  g_assert_true(queue->next_run == 1);
6416
 
 
6417
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
6418
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6419
 
}
6420
 
 
6421
 
static char *make_question_file_in_directory(const char
6422
 
                                             *const dir){
6423
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
6424
 
}
6425
 
 
6426
 
static
6427
 
void test_add_existing_questions_two_questions(__attribute__((unused))
6428
 
                                               test_fixture *fixture,
6429
 
                                               __attribute__((unused))
6430
 
                                               gconstpointer
6431
 
                                               user_data){
6432
 
  __attribute__((cleanup(cleanup_queue)))
6433
 
    task_queue *queue = create_queue();
6434
 
  g_assert_nonnull(queue);
6435
 
  __attribute__((cleanup(cleanup_close)))
6436
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6437
 
  g_assert_cmpint(epoll_fd, >=, 0);
6438
 
  __attribute__((cleanup(cleanup_buffer)))
6439
 
    buffer password = {};
6440
 
  __attribute__((cleanup(string_set_clear)))
6441
 
    string_set cancelled_filenames = {};
6442
 
  const mono_microsecs current_time = 0;
6443
 
  bool mandos_client_exited = false;
6444
 
  bool password_is_read = false;
6445
 
  __attribute__((cleanup(cleanup_string)))
6446
 
    char *tempdir = make_temporary_directory();
6447
 
  g_assert_nonnull(tempdir);
6448
 
  __attribute__((cleanup(cleanup_string)))
6449
 
    char *question_filename1
6450
 
    = make_question_file_in_directory(tempdir);
6451
 
  g_assert_nonnull(question_filename1);
6452
 
  __attribute__((cleanup(cleanup_string)))
6453
 
    char *question_filename2
6454
 
    = make_question_file_in_directory(tempdir);
6455
 
  g_assert_nonnull(question_filename2);
6456
 
 
6457
 
  g_assert_true(assert_add_existing_questions_to_devnull
6458
 
                (queue,
6459
 
                 epoll_fd,
6460
 
                 &password,
6461
 
                 &cancelled_filenames,
6462
 
                 &current_time,
6463
 
                 &mandos_client_exited,
6464
 
                 &password_is_read,
6465
 
                 tempdir));
6466
 
 
6467
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6468
 
 
6469
 
  g_assert_true(queue->next_run == 1);
6470
 
 
6471
 
  __attribute__((cleanup(string_set_clear)))
6472
 
    string_set seen_questions = {};
6473
 
 
6474
 
  bool queue_contains_question_opener(char *const question_filename){
6475
 
    return(find_matching_task(queue, (task_context){
6476
 
          .func=open_and_parse_question,
6477
 
          .epoll_fd=epoll_fd,
6478
 
          .question_filename=question_filename,
6479
 
          .password=&password,
6480
 
          .cancelled_filenames=&cancelled_filenames,
6481
 
          .current_time=&current_time,
6482
 
          .mandos_client_exited=&mandos_client_exited,
6483
 
          .password_is_read=&password_is_read,
6484
 
        }) != NULL);
6485
 
  }
6486
 
 
6487
 
  g_assert_true(queue_contains_question_opener(question_filename1));
6488
 
  g_assert_true(queue_contains_question_opener(question_filename2));
6489
 
 
6490
 
  g_assert_true(queue->next_run == 1);
6491
 
 
6492
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6493
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6494
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6495
 
}
6496
 
 
6497
 
static void
6498
 
test_add_existing_questions_non_questions(__attribute__((unused))
6499
 
                                          test_fixture *fixture,
6500
 
                                          __attribute__((unused))
6501
 
                                          gconstpointer user_data){
6502
 
  __attribute__((cleanup(cleanup_queue)))
6503
 
    task_queue *queue = create_queue();
6504
 
  g_assert_nonnull(queue);
6505
 
  __attribute__((cleanup(cleanup_close)))
6506
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6507
 
  g_assert_cmpint(epoll_fd, >=, 0);
6508
 
  __attribute__((cleanup(string_set_clear)))
6509
 
    string_set cancelled_filenames = {};
6510
 
  __attribute__((cleanup(cleanup_string)))
6511
 
    char *tempdir = make_temporary_directory();
6512
 
  g_assert_nonnull(tempdir);
6513
 
  __attribute__((cleanup(cleanup_string)))
6514
 
    char *question_filename1
6515
 
    = make_temporary_file_in_directory(tempdir);
6516
 
  g_assert_nonnull(question_filename1);
6517
 
  __attribute__((cleanup(cleanup_string)))
6518
 
    char *question_filename2
6519
 
    = make_temporary_file_in_directory(tempdir);
6520
 
  g_assert_nonnull(question_filename2);
6521
 
 
6522
 
  g_assert_false(assert_add_existing_questions_to_devnull
6523
 
                 (queue,
6524
 
                  epoll_fd,
6525
 
                  (buffer[]){{}}, /* password */
6526
 
                  &cancelled_filenames,
6527
 
                  (mono_microsecs[]){0}, /* current_time */
6528
 
                  (bool[]){false},       /* mandos_client_exited */
6529
 
                  (bool[]){false},       /* password_is_read */
6530
 
                  tempdir));
6531
 
 
6532
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6533
 
 
6534
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6535
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6536
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6537
 
}
6538
 
 
6539
 
static void
6540
 
test_add_existing_questions_both_types(__attribute__((unused))
6541
 
                                       test_fixture *fixture,
6542
 
                                       __attribute__((unused))
6543
 
                                       gconstpointer user_data){
6544
 
  __attribute__((cleanup(cleanup_queue)))
6545
 
    task_queue *queue = create_queue();
6546
 
  g_assert_nonnull(queue);
6547
 
  __attribute__((cleanup(cleanup_close)))
6548
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6549
 
  g_assert_cmpint(epoll_fd, >=, 0);
6550
 
  __attribute__((cleanup(cleanup_buffer)))
6551
 
    buffer password = {};
6552
 
  __attribute__((cleanup(string_set_clear)))
6553
 
    string_set cancelled_filenames = {};
6554
 
  const mono_microsecs current_time = 0;
6555
 
  bool mandos_client_exited = false;
6556
 
  bool password_is_read = false;
6557
 
  __attribute__((cleanup(cleanup_string)))
6558
 
    char *tempdir = make_temporary_directory();
6559
 
  g_assert_nonnull(tempdir);
6560
 
  __attribute__((cleanup(cleanup_string)))
6561
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6562
 
  g_assert_nonnull(tempfilename1);
6563
 
  __attribute__((cleanup(cleanup_string)))
6564
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6565
 
  g_assert_nonnull(tempfilename2);
6566
 
  __attribute__((cleanup(cleanup_string)))
6567
 
    char *question_filename
6568
 
    = make_question_file_in_directory(tempdir);
6569
 
  g_assert_nonnull(question_filename);
6570
 
 
6571
 
  g_assert_true(assert_add_existing_questions_to_devnull
6572
 
                (queue,
6573
 
                 epoll_fd,
6574
 
                 &password,
6575
 
                 &cancelled_filenames,
6576
 
                 &current_time,
6577
 
                 &mandos_client_exited,
6578
 
                 &password_is_read,
6579
 
                 tempdir));
6580
 
 
6581
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6582
 
 
6583
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6584
 
        .func=open_and_parse_question,
6585
 
        .epoll_fd=epoll_fd,
6586
 
        .filename=question_filename,
6587
 
        .question_filename=question_filename,
6588
 
        .password=&password,
6589
 
        .cancelled_filenames=&cancelled_filenames,
6590
 
        .current_time=&current_time,
6591
 
        .mandos_client_exited=&mandos_client_exited,
6592
 
        .password_is_read=&password_is_read,
6593
 
      }));
6594
 
 
6595
 
  g_assert_true(queue->next_run == 1);
6596
 
 
6597
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
6598
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
6599
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
6600
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6601
 
}
6602
 
 
6603
 
static void test_wait_for_event_timeout(__attribute__((unused))
6604
 
                                        test_fixture *fixture,
6605
 
                                        __attribute__((unused))
6606
 
                                        gconstpointer user_data){
6607
 
  __attribute__((cleanup(cleanup_close)))
6608
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6609
 
  g_assert_cmpint(epoll_fd, >=, 0);
6610
 
 
6611
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
6612
 
}
6613
 
 
6614
 
static void test_wait_for_event_event(__attribute__((unused))
6615
 
                                      test_fixture *fixture,
6616
 
                                      __attribute__((unused))
6617
 
                                      gconstpointer user_data){
6618
 
  __attribute__((cleanup(cleanup_close)))
6619
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6620
 
  g_assert_cmpint(epoll_fd, >=, 0);
6621
 
  int pipefds[2];
6622
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6623
 
  __attribute__((cleanup(cleanup_close)))
6624
 
    const int read_pipe = pipefds[0];
6625
 
  __attribute__((cleanup(cleanup_close)))
6626
 
    const int write_pipe = pipefds[1];
6627
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6628
 
                            &(struct epoll_event)
6629
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6630
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6631
 
 
6632
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6633
 
}
6634
 
 
6635
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
6636
 
                                        __attribute__((unused))
6637
 
                                        gconstpointer user_data){
6638
 
  const pid_t pid = fork();
6639
 
  if(pid == 0){         /* Child */
6640
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
6641
 
      _exit(EXIT_FAILURE);
6642
 
    }
6643
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
6644
 
      _exit(EXIT_FAILURE);
6645
 
    }
6646
 
    exit(EXIT_SUCCESS);
6647
 
  }
6648
 
  g_assert_true(pid != -1);
6649
 
  __attribute__((cleanup(cleanup_close)))
6650
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6651
 
  g_assert_cmpint(epoll_fd, >=, 0);
6652
 
 
6653
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6654
 
 
6655
 
  int status;
6656
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
6657
 
  g_assert_true(WIFEXITED(status));
6658
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6659
 
}
6660
 
 
6661
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6662
 
                                           test_fixture *fixture,
6663
 
                                           __attribute__((unused))
6664
 
                                           gconstpointer user_data){
6665
 
  __attribute__((cleanup(cleanup_queue)))
6666
 
    task_queue *queue = create_queue();
6667
 
  g_assert_nonnull(queue);
6668
 
  queue->next_run = 1;
6669
 
  __attribute__((cleanup(cleanup_close)))
6670
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6671
 
  __attribute__((cleanup(string_set_clear)))
6672
 
    string_set cancelled_filenames = {};
6673
 
  bool quit_now = false;
6674
 
 
6675
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6676
 
  g_assert_false(quit_now);
6677
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6678
 
}
6679
 
 
6680
 
static
6681
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6682
 
                                               test_fixture *fixture,
6683
 
                                               __attribute__((unused))
6684
 
                                               gconstpointer
6685
 
                                               user_data){
6686
 
  __attribute__((cleanup(cleanup_queue)))
6687
 
    task_queue *queue = create_queue();
6688
 
  g_assert_nonnull(queue);
6689
 
  __attribute__((cleanup(string_set_clear)))
6690
 
    string_set cancelled_filenames = {};
6691
 
  bool quit_now = false;
6692
 
  const char question_filename[] = "/nonexistent/question_filename";
6693
 
  g_assert_true(string_set_add(&cancelled_filenames,
6694
 
                               question_filename));
6695
 
 
6696
 
  g_assert_true(add_to_queue(queue,
6697
 
                             (task_context){ .func=dummy_func }));
6698
 
 
6699
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6700
 
  g_assert_false(quit_now);
6701
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6702
 
  g_assert_false(string_set_contains(cancelled_filenames,
6703
 
                                     question_filename));
6704
 
}
6705
 
 
6706
 
static
6707
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6708
 
                                              test_fixture *fixture,
6709
 
                                              __attribute__((unused))
6710
 
                                              gconstpointer
6711
 
                                              user_data){
6712
 
  __attribute__((cleanup(cleanup_queue)))
6713
 
    task_queue *queue = create_queue();
6714
 
  g_assert_nonnull(queue);
6715
 
  __attribute__((cleanup(string_set_clear)))
6716
 
    string_set cancelled_filenames = {};
6717
 
  bool quit_now = false;
6718
 
  int pipefds[2];
6719
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6720
 
  __attribute__((cleanup(cleanup_close)))
6721
 
    const int read_pipe = pipefds[0];
6722
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
6723
 
  const char question_filename[] = "/nonexistent/question_filename";
6724
 
  g_assert_true(string_set_add(&cancelled_filenames,
6725
 
                               question_filename));
6726
 
  __attribute__((nonnull))
6727
 
    void quit_func(const task_context task,
6728
 
                   __attribute__((unused)) task_queue *const q){
6729
 
    g_assert_nonnull(task.quit_now);
6730
 
    *task.quit_now = true;
6731
 
  }
6732
 
  task_context task = {
6733
 
    .func=quit_func,
6734
 
    .question_filename=strdup(question_filename),
6735
 
    .quit_now=&quit_now,
6736
 
    .fd=read_pipe,
6737
 
  };
6738
 
  g_assert_nonnull(task.question_filename);
6739
 
 
6740
 
  g_assert_true(add_to_queue(queue, task));
6741
 
 
6742
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6743
 
  g_assert_false(quit_now);
6744
 
 
6745
 
  /* read_pipe should be closed already */
6746
 
  errno = 0;
6747
 
  bool read_pipe_closed = (close(read_pipe) == -1);
6748
 
  read_pipe_closed &= (errno == EBADF);
6749
 
  g_assert_true(read_pipe_closed);
6750
 
}
6751
 
 
6752
 
static void test_run_queue_one_task(__attribute__((unused))
6753
 
                                    test_fixture *fixture,
6754
 
                                    __attribute__((unused))
6755
 
                                    gconstpointer user_data){
6756
 
  __attribute__((cleanup(cleanup_queue)))
6757
 
    task_queue *queue = create_queue();
6758
 
  g_assert_nonnull(queue);
6759
 
  __attribute__((cleanup(string_set_clear)))
6760
 
    string_set cancelled_filenames = {};
6761
 
  bool quit_now = false;
6762
 
 
6763
 
  __attribute__((nonnull))
6764
 
    void next_run_func(__attribute__((unused))
6765
 
                       const task_context task,
6766
 
                       task_queue *const q){
6767
 
    q->next_run = 1;
6768
 
  }
6769
 
 
6770
 
  task_context task = {
6771
 
    .func=next_run_func,
6772
 
  };
6773
 
  g_assert_true(add_to_queue(queue, task));
6774
 
 
6775
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6776
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6777
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6778
 
}
6779
 
 
6780
 
static void test_run_queue_two_tasks(__attribute__((unused))
6781
 
                                     test_fixture *fixture,
6782
 
                                     __attribute__((unused))
6783
 
                                     gconstpointer user_data){
6784
 
  __attribute__((cleanup(cleanup_queue)))
6785
 
    task_queue *queue = create_queue();
6786
 
  g_assert_nonnull(queue);
6787
 
  queue->next_run = 1;
6788
 
  __attribute__((cleanup(string_set_clear)))
6789
 
    string_set cancelled_filenames = {};
6790
 
  bool quit_now = false;
6791
 
  bool mandos_client_exited = false;
6792
 
 
6793
 
  __attribute__((nonnull))
6794
 
    void next_run_func(__attribute__((unused))
6795
 
                       const task_context task,
6796
 
                       task_queue *const q){
6797
 
    q->next_run = 1;
6798
 
  }
6799
 
 
6800
 
  __attribute__((nonnull))
6801
 
    void exited_func(const task_context task,
6802
 
                     __attribute__((unused)) task_queue *const q){
6803
 
    *task.mandos_client_exited = true;
6804
 
  }
6805
 
 
6806
 
  task_context task1 = {
6807
 
    .func=next_run_func,
6808
 
  };
6809
 
  g_assert_true(add_to_queue(queue, task1));
6810
 
 
6811
 
  task_context task2 = {
6812
 
    .func=exited_func,
6813
 
    .mandos_client_exited=&mandos_client_exited,
6814
 
  };
6815
 
  g_assert_true(add_to_queue(queue, task2));
6816
 
 
6817
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6818
 
  g_assert_false(quit_now);
6819
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6820
 
  g_assert_true(mandos_client_exited);
6821
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6822
 
}
6823
 
 
6824
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6825
 
                                          test_fixture *fixture,
6826
 
                                          __attribute__((unused))
6827
 
                                          gconstpointer user_data){
6828
 
  __attribute__((cleanup(cleanup_queue)))
6829
 
    task_queue *queue = create_queue();
6830
 
  g_assert_nonnull(queue);
6831
 
  __attribute__((cleanup(string_set_clear)))
6832
 
    string_set cancelled_filenames = {};
6833
 
  bool quit_now = false;
6834
 
  bool mandos_client_exited = false;
6835
 
  bool password_is_read = false;
6836
 
 
6837
 
  __attribute__((nonnull))
6838
 
    void set_exited_func(const task_context task,
6839
 
                         __attribute__((unused)) task_queue *const q){
6840
 
    *task.mandos_client_exited = true;
6841
 
    *task.quit_now = true;
6842
 
  }
6843
 
  task_context task1 = {
6844
 
    .func=set_exited_func,
6845
 
    .quit_now=&quit_now,
6846
 
    .mandos_client_exited=&mandos_client_exited,
6847
 
  };
6848
 
  g_assert_true(add_to_queue(queue, task1));
6849
 
 
6850
 
  __attribute__((nonnull))
6851
 
    void set_read_func(const task_context task,
6852
 
                       __attribute__((unused)) task_queue *const q){
6853
 
    *task.quit_now = true;
6854
 
    *task.password_is_read = true;
6855
 
  }
6856
 
  task_context task2 = {
6857
 
    .func=set_read_func,
6858
 
    .quit_now=&quit_now,
6859
 
    .password_is_read=&password_is_read,
6860
 
  };
6861
 
  g_assert_true(add_to_queue(queue, task2));
6862
 
 
6863
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6864
 
  g_assert_true(quit_now);
6865
 
  g_assert_true(mandos_client_exited xor password_is_read);
6866
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6867
 
}
6868
 
 
6869
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6870
 
                                             test_fixture *fixture,
6871
 
                                             __attribute__((unused))
6872
 
                                             gconstpointer user_data){
6873
 
  __attribute__((cleanup(cleanup_queue)))
6874
 
    task_queue *queue = create_queue();
6875
 
  g_assert_nonnull(queue);
6876
 
  __attribute__((cleanup(string_set_clear)))
6877
 
    string_set cancelled_filenames = {};
6878
 
  int pipefds[2];
6879
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6880
 
  __attribute__((cleanup(cleanup_close)))
6881
 
    const int read_pipe = pipefds[0];
6882
 
  __attribute__((cleanup(cleanup_close)))
6883
 
    const int write_pipe = pipefds[1];
6884
 
  bool quit_now = false;
6885
 
 
6886
 
  __attribute__((nonnull))
6887
 
    void read_func(const task_context task,
6888
 
                   __attribute__((unused)) task_queue *const q){
6889
 
    *task.quit_now = true;
6890
 
  }
6891
 
  task_context task1 = {
6892
 
    .func=read_func,
6893
 
    .quit_now=&quit_now,
6894
 
    .fd=read_pipe,
6895
 
  };
6896
 
  g_assert_true(add_to_queue(queue, task1));
6897
 
 
6898
 
  __attribute__((nonnull))
6899
 
    void write_func(const task_context task,
6900
 
                    __attribute__((unused)) task_queue *const q){
6901
 
    *task.quit_now = true;
6902
 
  }
6903
 
  task_context task2 = {
6904
 
    .func=write_func,
6905
 
    .quit_now=&quit_now,
6906
 
    .fd=write_pipe,
6907
 
  };
6908
 
  g_assert_true(add_to_queue(queue, task2));
6909
 
 
6910
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6911
 
  g_assert_true(quit_now);
6912
 
 
6913
 
  /* Either read_pipe or write_pipe should be closed already */
6914
 
  errno = 0;
6915
 
  bool close_read_pipe = (close(read_pipe) == -1);
6916
 
  close_read_pipe &= (errno == EBADF);
6917
 
  errno = 0;
6918
 
  bool close_write_pipe = (close(write_pipe) == -1);
6919
 
  close_write_pipe &= (errno == EBADF);
6920
 
  g_assert_true(close_read_pipe xor close_write_pipe);
6921
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6922
 
}
6923
 
 
6924
 
static void test_setup_signal_handler(__attribute__((unused))
6925
 
                                      test_fixture *fixture,
6926
 
                                      __attribute__((unused))
6927
 
                                      gconstpointer user_data){
6928
 
  /* Save current SIGCHLD action, whatever it is */
6929
 
  struct sigaction expected_sigchld_action;
6930
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6931
 
                  ==, 0);
6932
 
 
6933
 
  /* Act; i.e. run the setup_signal_handler() function */
6934
 
  struct sigaction actual_old_sigchld_action;
6935
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6936
 
 
6937
 
  /* Check that the function correctly set "actual_old_sigchld_action"
6938
 
     to the same values as the previously saved
6939
 
     "expected_sigchld_action" */
6940
 
  /* Check member sa_handler */
6941
 
  g_assert_true(actual_old_sigchld_action.sa_handler
6942
 
                == expected_sigchld_action.sa_handler);
6943
 
  /* Check member sa_mask */
6944
 
  for(int signum = 1; signum < NSIG; signum++){
6945
 
    const int expected_old_block_state
6946
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
6947
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
6948
 
    const int actual_old_block_state
6949
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
6950
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
6951
 
    g_assert_cmpint(actual_old_block_state,
6952
 
                    ==, expected_old_block_state);
6953
 
  }
6954
 
  /* Check member sa_flags */
6955
 
  g_assert_true((actual_old_sigchld_action.sa_flags
6956
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6957
 
                == (expected_sigchld_action.sa_flags
6958
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6959
 
 
6960
 
  /* Retrieve the current signal handler for SIGCHLD as set by
6961
 
     setup_signal_handler() */
6962
 
  struct sigaction actual_new_sigchld_action;
6963
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
6964
 
                            &actual_new_sigchld_action), ==, 0);
6965
 
  /* Check that the signal handler (member sa_handler) is correctly
6966
 
     set to the "handle_sigchld" function */
6967
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6968
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6969
 
  g_assert_true(actual_new_sigchld_action.sa_handler
6970
 
                == handle_sigchld);
6971
 
  /* Check (in member sa_mask) that at least a handful of signals are
6972
 
     actually blocked during the signal handler */
6973
 
  for(int signum = 1; signum < NSIG; signum++){
6974
 
    int actual_new_block_state;
6975
 
    switch(signum){
6976
 
    case SIGTERM:
6977
 
    case SIGINT:
6978
 
    case SIGQUIT:
6979
 
    case SIGHUP:
6980
 
      actual_new_block_state
6981
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
6982
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
6983
 
      continue;
6984
 
    case SIGKILL:               /* non-blockable */
6985
 
    case SIGSTOP:               /* non-blockable */
6986
 
    case SIGCHLD:               /* always blocked */
6987
 
    default:
6988
 
      continue;
6989
 
    }
6990
 
  }
6991
 
  /* Check member sa_flags */
6992
 
  g_assert_true((actual_new_sigchld_action.sa_flags
6993
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6994
 
                == (SA_NOCLDSTOP | SA_RESTART));
6995
 
 
6996
 
  /* Restore signal handler */
6997
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6998
 
                  ==, 0);
6999
 
}
7000
 
 
7001
 
static void test_restore_signal_handler(__attribute__((unused))
7002
 
                                        test_fixture *fixture,
7003
 
                                        __attribute__((unused))
7004
 
                                        gconstpointer user_data){
7005
 
  /* Save current SIGCHLD action, whatever it is */
7006
 
  struct sigaction expected_sigchld_action;
7007
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7008
 
                  ==, 0);
7009
 
  /* Since we haven't established a signal handler yet, there should
7010
 
     not be one established.  But another test may have relied on
7011
 
     restore_signal_handler() to restore the signal handler, and if
7012
 
     restore_signal_handler() is buggy (which we should be prepared
7013
 
     for in this test) the signal handler may not have been restored
7014
 
     properly; check for this: */
7015
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7016
 
 
7017
 
  /* Establish a signal handler */
7018
 
  struct sigaction sigchld_action = {
7019
 
    .sa_handler=handle_sigchld,
7020
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
7021
 
  };
7022
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7023
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7024
 
 
7025
 
  /* Act; i.e. run the restore_signal_handler() function */
7026
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
7027
 
 
7028
 
  /* Retrieve the restored signal handler data */
7029
 
  struct sigaction actual_restored_sigchld_action;
7030
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
7031
 
                            &actual_restored_sigchld_action), ==, 0);
7032
 
 
7033
 
  /* Check that the function correctly restored the signal action, as
7034
 
     saved in "actual_restored_sigchld_action", to the same values as
7035
 
     the previously saved "expected_sigchld_action" */
7036
 
  /* Check member sa_handler */
7037
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
7038
 
                == expected_sigchld_action.sa_handler);
7039
 
  /* Check member sa_mask */
7040
 
  for(int signum = 1; signum < NSIG; signum++){
7041
 
    const int expected_old_block_state
7042
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
7043
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
7044
 
    const int actual_restored_block_state
7045
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7046
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
7047
 
    g_assert_cmpint(actual_restored_block_state,
7048
 
                    ==, expected_old_block_state);
7049
 
  }
7050
 
  /* Check member sa_flags */
7051
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
7052
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7053
 
                == (expected_sigchld_action.sa_flags
7054
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7055
 
}
7056
 
 
7057
 
static void test_block_sigchld(__attribute__((unused))
7058
 
                               test_fixture *fixture,
7059
 
                               __attribute__((unused))
7060
 
                               gconstpointer user_data){
7061
 
  /* Save original signal mask */
7062
 
  sigset_t expected_sigmask;
7063
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7064
 
                  ==, 0);
7065
 
 
7066
 
  /* Make sure SIGCHLD is unblocked for this test */
7067
 
  sigset_t sigchld_sigmask;
7068
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7069
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7070
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7071
 
                                  NULL), ==, 0);
7072
 
 
7073
 
  /* Act; i.e. run the block_sigchld() function */
7074
 
  sigset_t actual_old_sigmask;
7075
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
7076
 
 
7077
 
  /* Check the actual_old_sigmask; it should be the same as the
7078
 
     previously saved signal mask "expected_sigmask". */
7079
 
  for(int signum = 1; signum < NSIG; signum++){
7080
 
    const int expected_old_block_state
7081
 
      = sigismember(&expected_sigmask, signum);
7082
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
7083
 
    const int actual_old_block_state
7084
 
      = sigismember(&actual_old_sigmask, signum);
7085
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
7086
 
    g_assert_cmpint(actual_old_block_state,
7087
 
                    ==, expected_old_block_state);
7088
 
  }
7089
 
 
7090
 
  /* Retrieve the newly set signal mask */
7091
 
  sigset_t actual_sigmask;
7092
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7093
 
 
7094
 
  /* SIGCHLD should be blocked */
7095
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7096
 
 
7097
 
  /* Restore signal mask */
7098
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7099
 
                                  NULL), ==, 0);
7100
 
}
7101
 
 
7102
 
static void test_restore_sigmask(__attribute__((unused))
7103
 
                                 test_fixture *fixture,
7104
 
                                 __attribute__((unused))
7105
 
                                 gconstpointer user_data){
7106
 
  /* Save original signal mask */
7107
 
  sigset_t orig_sigmask;
7108
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7109
 
 
7110
 
  /* Make sure SIGCHLD is blocked for this test */
7111
 
  sigset_t sigchld_sigmask;
7112
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7113
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7114
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7115
 
                                  NULL), ==, 0);
7116
 
 
7117
 
  /* Act; i.e. run the restore_sigmask() function */
7118
 
  g_assert_true(restore_sigmask(&orig_sigmask));
7119
 
 
7120
 
  /* Retrieve the newly restored signal mask */
7121
 
  sigset_t restored_sigmask;
7122
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7123
 
                  ==, 0);
7124
 
 
7125
 
  /* Check the restored_sigmask; it should be the same as the
7126
 
     previously saved signal mask "orig_sigmask". */
7127
 
  for(int signum = 1; signum < NSIG; signum++){
7128
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
7129
 
    g_assert_cmpint(orig_block_state, >=, 0);
7130
 
    const int restored_block_state = sigismember(&restored_sigmask,
7131
 
                                                 signum);
7132
 
    g_assert_cmpint(restored_block_state, >=, 0);
7133
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
7134
 
  }
7135
 
 
7136
 
  /* Restore signal mask */
7137
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7138
 
                                  NULL), ==, 0);
7139
 
}
7140
 
 
7141
 
static void test_parse_arguments_noargs(__attribute__((unused))
7142
 
                                        test_fixture *fixture,
7143
 
                                        __attribute__((unused))
7144
 
                                        gconstpointer user_data){
7145
 
  char *argv[] = {
7146
 
    strdup("prgname"),
7147
 
    NULL };
7148
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7149
 
 
7150
 
  char *agent_directory = NULL;
7151
 
  char *helper_directory = NULL;
7152
 
  uid_t user = 0;
7153
 
  gid_t group = 0;
7154
 
  char *mandos_argz = NULL;
7155
 
  size_t mandos_argz_length = 0;
7156
 
 
7157
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7158
 
                                &helper_directory, &user, &group,
7159
 
                                &mandos_argz, &mandos_argz_length));
7160
 
  g_assert_null(agent_directory);
7161
 
  g_assert_null(helper_directory);
7162
 
  g_assert_true(user == 0);
7163
 
  g_assert_true(group == 0);
7164
 
  g_assert_null(mandos_argz);
7165
 
  g_assert_true(mandos_argz_length == 0);
7166
 
 
7167
 
  for(char **arg = argv; *arg != NULL; arg++){
7168
 
    free(*arg);
7169
 
  }
7170
 
}
7171
 
 
7172
 
__attribute__((nonnull))
7173
 
static bool parse_arguments_devnull(int argc, char *argv[],
7174
 
                                    const bool exit_failure,
7175
 
                                    char **agent_directory,
7176
 
                                    char **helper_directory,
7177
 
                                    uid_t *const user,
7178
 
                                    gid_t *const group,
7179
 
                                    char **mandos_argz,
7180
 
                                    size_t *mandos_argz_length){
7181
 
 
7182
 
  FILE *real_stderr = stderr;
7183
 
  FILE *devnull = fopen("/dev/null", "we");
7184
 
  g_assert_nonnull(devnull);
7185
 
  stderr = devnull;
7186
 
 
7187
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
7188
 
                                   agent_directory,
7189
 
                                   helper_directory, user, group,
7190
 
                                   mandos_argz, mandos_argz_length);
7191
 
  const error_t saved_errno = errno;
7192
 
 
7193
 
  stderr = real_stderr;
7194
 
  g_assert_cmpint(fclose(devnull), ==, 0);
7195
 
 
7196
 
  errno = saved_errno;
7197
 
 
7198
 
  return ret;
7199
 
}
7200
 
 
7201
 
static void test_parse_arguments_invalid(__attribute__((unused))
7202
 
                                         test_fixture *fixture,
7203
 
                                         __attribute__((unused))
7204
 
                                         gconstpointer user_data){
7205
 
  char *argv[] = {
7206
 
    strdup("prgname"),
7207
 
    strdup("--invalid"),
7208
 
    NULL };
7209
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7210
 
 
7211
 
  char *agent_directory = NULL;
7212
 
  char *helper_directory = NULL;
7213
 
  uid_t user = 0;
7214
 
  gid_t group = 0;
7215
 
  char *mandos_argz = NULL;
7216
 
  size_t mandos_argz_length = 0;
7217
 
 
7218
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7219
 
                                         &agent_directory,
7220
 
                                         &helper_directory, &user,
7221
 
                                         &group, &mandos_argz,
7222
 
                                         &mandos_argz_length));
7223
 
 
7224
 
  g_assert_true(errno == EINVAL);
7225
 
  g_assert_null(agent_directory);
7226
 
  g_assert_null(helper_directory);
7227
 
  g_assert_null(mandos_argz);
7228
 
  g_assert_true(mandos_argz_length == 0);
7229
 
 
7230
 
  for(char **arg = argv; *arg != NULL; arg++){
7231
 
    free(*arg);
7232
 
  }
7233
 
}
7234
 
 
7235
 
static void test_parse_arguments_long_dir(__attribute__((unused))
7236
 
                                          test_fixture *fixture,
7237
 
                                          __attribute__((unused))
7238
 
                                          gconstpointer user_data){
7239
 
  char *argv[] = {
7240
 
    strdup("prgname"),
7241
 
    strdup("--agent-directory"),
7242
 
    strdup("/tmp"),
7243
 
    NULL };
7244
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7245
 
 
7246
 
  __attribute__((cleanup(cleanup_string)))
7247
 
    char *agent_directory = NULL;
7248
 
  char *helper_directory = NULL;
7249
 
  uid_t user = 0;
7250
 
  gid_t group = 0;
7251
 
  __attribute__((cleanup(cleanup_string)))
7252
 
    char *mandos_argz = NULL;
7253
 
  size_t mandos_argz_length = 0;
7254
 
 
7255
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7256
 
                                &helper_directory, &user, &group,
7257
 
                                &mandos_argz, &mandos_argz_length));
7258
 
 
7259
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7260
 
  g_assert_null(helper_directory);
7261
 
  g_assert_true(user == 0);
7262
 
  g_assert_true(group == 0);
7263
 
  g_assert_null(mandos_argz);
7264
 
  g_assert_true(mandos_argz_length == 0);
7265
 
 
7266
 
  for(char **arg = argv; *arg != NULL; arg++){
7267
 
    free(*arg);
7268
 
  }
7269
 
}
7270
 
 
7271
 
static void test_parse_arguments_short_dir(__attribute__((unused))
7272
 
                                           test_fixture *fixture,
7273
 
                                           __attribute__((unused))
7274
 
                                           gconstpointer user_data){
7275
 
  char *argv[] = {
7276
 
    strdup("prgname"),
7277
 
    strdup("-d"),
7278
 
    strdup("/tmp"),
7279
 
    NULL };
7280
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7281
 
 
7282
 
  __attribute__((cleanup(cleanup_string)))
7283
 
    char *agent_directory = NULL;
7284
 
  char *helper_directory = NULL;
7285
 
  uid_t user = 0;
7286
 
  gid_t group = 0;
7287
 
  __attribute__((cleanup(cleanup_string)))
7288
 
    char *mandos_argz = NULL;
7289
 
  size_t mandos_argz_length = 0;
7290
 
 
7291
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7292
 
                                &helper_directory, &user, &group,
7293
 
                                &mandos_argz, &mandos_argz_length));
7294
 
 
7295
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7296
 
  g_assert_null(helper_directory);
7297
 
  g_assert_true(user == 0);
7298
 
  g_assert_true(group == 0);
7299
 
  g_assert_null(mandos_argz);
7300
 
  g_assert_true(mandos_argz_length == 0);
7301
 
 
7302
 
  for(char **arg = argv; *arg != NULL; arg++){
7303
 
    free(*arg);
7304
 
  }
7305
 
}
7306
 
 
7307
 
static
7308
 
void test_parse_arguments_helper_directory(__attribute__((unused))
7309
 
                                           test_fixture *fixture,
7310
 
                                           __attribute__((unused))
7311
 
                                           gconstpointer user_data){
7312
 
  char *argv[] = {
7313
 
    strdup("prgname"),
7314
 
    strdup("--helper-directory"),
7315
 
    strdup("/tmp"),
7316
 
    NULL };
7317
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7318
 
 
7319
 
  char *agent_directory = NULL;
7320
 
  __attribute__((cleanup(cleanup_string)))
7321
 
    char *helper_directory = NULL;
7322
 
  uid_t user = 0;
7323
 
  gid_t group = 0;
7324
 
  __attribute__((cleanup(cleanup_string)))
7325
 
    char *mandos_argz = NULL;
7326
 
  size_t mandos_argz_length = 0;
7327
 
 
7328
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7329
 
                                &helper_directory, &user, &group,
7330
 
                                &mandos_argz, &mandos_argz_length));
7331
 
 
7332
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
7333
 
  g_assert_null(agent_directory);
7334
 
  g_assert_true(user == 0);
7335
 
  g_assert_true(group == 0);
7336
 
  g_assert_null(mandos_argz);
7337
 
  g_assert_true(mandos_argz_length == 0);
7338
 
 
7339
 
  for(char **arg = argv; *arg != NULL; arg++){
7340
 
    free(*arg);
7341
 
  }
7342
 
}
7343
 
 
7344
 
static
7345
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7346
 
                                            test_fixture *fixture,
7347
 
                                            __attribute__((unused))
7348
 
                                            gconstpointer user_data){
7349
 
  char *argv[] = {
7350
 
    strdup("prgname"),
7351
 
    strdup("--plugin-helper-dir"),
7352
 
    strdup("/tmp"),
7353
 
    NULL };
7354
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7355
 
 
7356
 
  char *agent_directory = NULL;
7357
 
  __attribute__((cleanup(cleanup_string)))
7358
 
    char *helper_directory = NULL;
7359
 
  uid_t user = 0;
7360
 
  gid_t group = 0;
7361
 
  __attribute__((cleanup(cleanup_string)))
7362
 
    char *mandos_argz = NULL;
7363
 
  size_t mandos_argz_length = 0;
7364
 
 
7365
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7366
 
                                &helper_directory, &user, &group,
7367
 
                                &mandos_argz, &mandos_argz_length));
7368
 
 
7369
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
7370
 
  g_assert_null(agent_directory);
7371
 
  g_assert_true(user == 0);
7372
 
  g_assert_true(group == 0);
7373
 
  g_assert_null(mandos_argz);
7374
 
  g_assert_true(mandos_argz_length == 0);
7375
 
 
7376
 
  for(char **arg = argv; *arg != NULL; arg++){
7377
 
    free(*arg);
7378
 
  }
7379
 
}
7380
 
 
7381
 
static void test_parse_arguments_user(__attribute__((unused))
7382
 
                                      test_fixture *fixture,
7383
 
                                      __attribute__((unused))
7384
 
                                      gconstpointer user_data){
7385
 
  char *argv[] = {
7386
 
    strdup("prgname"),
7387
 
    strdup("--user"),
7388
 
    strdup("1000"),
7389
 
    NULL };
7390
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7391
 
 
7392
 
  char *agent_directory = NULL;
7393
 
  __attribute__((cleanup(cleanup_string)))
7394
 
    char *helper_directory = NULL;
7395
 
  uid_t user = 0;
7396
 
  gid_t group = 0;
7397
 
  __attribute__((cleanup(cleanup_string)))
7398
 
    char *mandos_argz = NULL;
7399
 
  size_t mandos_argz_length = 0;
7400
 
 
7401
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7402
 
                                &helper_directory, &user, &group,
7403
 
                                &mandos_argz, &mandos_argz_length));
7404
 
 
7405
 
  g_assert_null(helper_directory);
7406
 
  g_assert_null(agent_directory);
7407
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
7408
 
  g_assert_true(group == 0);
7409
 
  g_assert_null(mandos_argz);
7410
 
  g_assert_true(mandos_argz_length == 0);
7411
 
 
7412
 
  for(char **arg = argv; *arg != NULL; arg++){
7413
 
    free(*arg);
7414
 
  }
7415
 
}
7416
 
 
7417
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
7418
 
                                              test_fixture *fixture,
7419
 
                                              __attribute__((unused))
7420
 
                                              gconstpointer
7421
 
                                              user_data){
7422
 
  char *argv[] = {
7423
 
    strdup("prgname"),
7424
 
    strdup("--user"),
7425
 
    strdup("invalid"),
7426
 
    NULL };
7427
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7428
 
 
7429
 
  char *agent_directory = NULL;
7430
 
  __attribute__((cleanup(cleanup_string)))
7431
 
    char *helper_directory = NULL;
7432
 
  uid_t user = 0;
7433
 
  gid_t group = 0;
7434
 
  __attribute__((cleanup(cleanup_string)))
7435
 
    char *mandos_argz = NULL;
7436
 
  size_t mandos_argz_length = 0;
7437
 
 
7438
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7439
 
                                         &agent_directory,
7440
 
                                         &helper_directory, &user,
7441
 
                                         &group, &mandos_argz,
7442
 
                                         &mandos_argz_length));
7443
 
 
7444
 
  g_assert_null(helper_directory);
7445
 
  g_assert_null(agent_directory);
7446
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
7447
 
  g_assert_true(group == 0);
7448
 
  g_assert_null(mandos_argz);
7449
 
  g_assert_true(mandos_argz_length == 0);
7450
 
 
7451
 
  for(char **arg = argv; *arg != NULL; arg++){
7452
 
    free(*arg);
7453
 
  }
7454
 
}
7455
 
 
7456
 
static
7457
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7458
 
                                            test_fixture *fixture,
7459
 
                                            __attribute__((unused))
7460
 
                                            gconstpointer user_data){
7461
 
  char *argv[] = {
7462
 
    strdup("prgname"),
7463
 
    strdup("--user"),
7464
 
    strdup("0"),
7465
 
    NULL };
7466
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7467
 
 
7468
 
  char *agent_directory = NULL;
7469
 
  __attribute__((cleanup(cleanup_string)))
7470
 
    char *helper_directory = NULL;
7471
 
  uid_t user = 0;
7472
 
  gid_t group = 0;
7473
 
  __attribute__((cleanup(cleanup_string)))
7474
 
    char *mandos_argz = NULL;
7475
 
  size_t mandos_argz_length = 0;
7476
 
 
7477
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7478
 
                                         &agent_directory,
7479
 
                                         &helper_directory, &user,
7480
 
                                         &group, &mandos_argz,
7481
 
                                         &mandos_argz_length));
7482
 
 
7483
 
  g_assert_null(helper_directory);
7484
 
  g_assert_null(agent_directory);
7485
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
7486
 
  g_assert_true(group == 0);
7487
 
  g_assert_null(mandos_argz);
7488
 
  g_assert_true(mandos_argz_length == 0);
7489
 
 
7490
 
  for(char **arg = argv; *arg != NULL; arg++){
7491
 
    free(*arg);
7492
 
  }
7493
 
}
7494
 
 
7495
 
static void test_parse_arguments_group(__attribute__((unused))
7496
 
                                       test_fixture *fixture,
7497
 
                                       __attribute__((unused))
7498
 
                                       gconstpointer user_data){
7499
 
  char *argv[] = {
7500
 
    strdup("prgname"),
7501
 
    strdup("--group"),
7502
 
    strdup("1000"),
7503
 
    NULL };
7504
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7505
 
 
7506
 
  char *agent_directory = NULL;
7507
 
  __attribute__((cleanup(cleanup_string)))
7508
 
    char *helper_directory = NULL;
7509
 
  uid_t user = 0;
7510
 
  gid_t group = 0;
7511
 
  __attribute__((cleanup(cleanup_string)))
7512
 
    char *mandos_argz = NULL;
7513
 
  size_t mandos_argz_length = 0;
7514
 
 
7515
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7516
 
                                &helper_directory, &user, &group,
7517
 
                                &mandos_argz, &mandos_argz_length));
7518
 
 
7519
 
  g_assert_null(helper_directory);
7520
 
  g_assert_null(agent_directory);
7521
 
  g_assert_true(user == 0);
7522
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
7523
 
  g_assert_null(mandos_argz);
7524
 
  g_assert_true(mandos_argz_length == 0);
7525
 
 
7526
 
  for(char **arg = argv; *arg != NULL; arg++){
7527
 
    free(*arg);
7528
 
  }
7529
 
}
7530
 
 
7531
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
7532
 
                                               test_fixture *fixture,
7533
 
                                               __attribute__((unused))
7534
 
                                               gconstpointer
7535
 
                                               user_data){
7536
 
  char *argv[] = {
7537
 
    strdup("prgname"),
7538
 
    strdup("--group"),
7539
 
    strdup("invalid"),
7540
 
    NULL };
7541
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7542
 
 
7543
 
  char *agent_directory = NULL;
7544
 
  __attribute__((cleanup(cleanup_string)))
7545
 
    char *helper_directory = NULL;
7546
 
  uid_t user = 0;
7547
 
  gid_t group = 0;
7548
 
  __attribute__((cleanup(cleanup_string)))
7549
 
    char *mandos_argz = NULL;
7550
 
  size_t mandos_argz_length = 0;
7551
 
 
7552
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7553
 
                                         &agent_directory,
7554
 
                                         &helper_directory, &user,
7555
 
                                         &group, &mandos_argz,
7556
 
                                         &mandos_argz_length));
7557
 
 
7558
 
  g_assert_null(helper_directory);
7559
 
  g_assert_null(agent_directory);
7560
 
  g_assert_true(user == 0);
7561
 
  g_assert_true(group == 0);
7562
 
  g_assert_null(mandos_argz);
7563
 
  g_assert_true(mandos_argz_length == 0);
7564
 
 
7565
 
  for(char **arg = argv; *arg != NULL; arg++){
7566
 
    free(*arg);
7567
 
  }
7568
 
}
7569
 
 
7570
 
static
7571
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7572
 
                                             test_fixture *fixture,
7573
 
                                             __attribute__((unused))
7574
 
                                             gconstpointer user_data){
7575
 
  char *argv[] = {
7576
 
    strdup("prgname"),
7577
 
    strdup("--group"),
7578
 
    strdup("0"),
7579
 
    NULL };
7580
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7581
 
 
7582
 
  char *agent_directory = NULL;
7583
 
  __attribute__((cleanup(cleanup_string)))
7584
 
    char *helper_directory = NULL;
7585
 
  uid_t user = 0;
7586
 
  gid_t group = 0;
7587
 
  __attribute__((cleanup(cleanup_string)))
7588
 
    char *mandos_argz = NULL;
7589
 
  size_t mandos_argz_length = 0;
7590
 
 
7591
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7592
 
                                         &agent_directory,
7593
 
                                         &helper_directory, &user,
7594
 
                                         &group, &mandos_argz,
7595
 
                                         &mandos_argz_length));
7596
 
 
7597
 
  g_assert_null(helper_directory);
7598
 
  g_assert_null(agent_directory);
7599
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
7600
 
  g_assert_true(group == 0);
7601
 
  g_assert_null(mandos_argz);
7602
 
  g_assert_true(mandos_argz_length == 0);
7603
 
 
7604
 
  for(char **arg = argv; *arg != NULL; arg++){
7605
 
    free(*arg);
7606
 
  }
7607
 
}
7608
 
 
7609
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7610
 
                                               test_fixture *fixture,
7611
 
                                               __attribute__((unused))
7612
 
                                               gconstpointer
7613
 
                                               user_data){
7614
 
  char *argv[] = {
7615
 
    strdup("prgname"),
7616
 
    strdup("mandos-client"),
7617
 
    NULL };
7618
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7619
 
 
7620
 
  __attribute__((cleanup(cleanup_string)))
7621
 
    char *agent_directory = NULL;
7622
 
  __attribute__((cleanup(cleanup_string)))
7623
 
    char *helper_directory = NULL;
7624
 
  uid_t user = 0;
7625
 
  gid_t group = 0;
7626
 
  __attribute__((cleanup(cleanup_string)))
7627
 
    char *mandos_argz = NULL;
7628
 
  size_t mandos_argz_length = 0;
7629
 
 
7630
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7631
 
                                &helper_directory, &user, &group,
7632
 
                                &mandos_argz, &mandos_argz_length));
7633
 
 
7634
 
  g_assert_null(agent_directory);
7635
 
  g_assert_null(helper_directory);
7636
 
  g_assert_true(user == 0);
7637
 
  g_assert_true(group == 0);
7638
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7639
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7640
 
                                            mandos_argz_length),
7641
 
                   ==, 1);
7642
 
 
7643
 
  for(char **arg = argv; *arg != NULL; arg++){
7644
 
    free(*arg);
7645
 
  }
7646
 
}
7647
 
 
7648
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
7649
 
                                             test_fixture *fixture,
7650
 
                                             __attribute__((unused))
7651
 
                                             gconstpointer user_data){
7652
 
  char *argv[] = {
7653
 
    strdup("prgname"),
7654
 
    strdup("mandos-client"),
7655
 
    strdup("one"),
7656
 
    strdup("two"),
7657
 
    strdup("three"),
7658
 
    NULL };
7659
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7660
 
 
7661
 
  __attribute__((cleanup(cleanup_string)))
7662
 
    char *agent_directory = NULL;
7663
 
  __attribute__((cleanup(cleanup_string)))
7664
 
    char *helper_directory = NULL;
7665
 
  uid_t user = 0;
7666
 
  gid_t group = 0;
7667
 
  __attribute__((cleanup(cleanup_string)))
7668
 
    char *mandos_argz = NULL;
7669
 
  size_t mandos_argz_length = 0;
7670
 
 
7671
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7672
 
                                &helper_directory, &user, &group,
7673
 
                                &mandos_argz, &mandos_argz_length));
7674
 
 
7675
 
  g_assert_null(agent_directory);
7676
 
  g_assert_null(helper_directory);
7677
 
  g_assert_true(user == 0);
7678
 
  g_assert_true(group == 0);
7679
 
  char *marg = mandos_argz;
7680
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7681
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7682
 
  g_assert_cmpstr(marg, ==, "one");
7683
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7684
 
  g_assert_cmpstr(marg, ==, "two");
7685
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7686
 
  g_assert_cmpstr(marg, ==, "three");
7687
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7688
 
                                            mandos_argz_length),
7689
 
                   ==, 4);
7690
 
 
7691
 
  for(char **arg = argv; *arg != NULL; arg++){
7692
 
    free(*arg);
7693
 
  }
7694
 
}
7695
 
 
7696
 
static void test_parse_arguments_all_args(__attribute__((unused))
7697
 
                                          test_fixture *fixture,
7698
 
                                          __attribute__((unused))
7699
 
                                          gconstpointer user_data){
7700
 
  char *argv[] = {
7701
 
    strdup("prgname"),
7702
 
    strdup("--agent-directory"),
7703
 
    strdup("/tmp"),
7704
 
    strdup("--helper-directory"),
7705
 
    strdup("/var/tmp"),
7706
 
    strdup("--user"),
7707
 
    strdup("1"),
7708
 
    strdup("--group"),
7709
 
    strdup("2"),
7710
 
    strdup("mandos-client"),
7711
 
    strdup("one"),
7712
 
    strdup("two"),
7713
 
    strdup("three"),
7714
 
    NULL };
7715
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7716
 
 
7717
 
  __attribute__((cleanup(cleanup_string)))
7718
 
    char *agent_directory = NULL;
7719
 
  __attribute__((cleanup(cleanup_string)))
7720
 
    char *helper_directory = NULL;
7721
 
  uid_t user = 0;
7722
 
  gid_t group = 0;
7723
 
  __attribute__((cleanup(cleanup_string)))
7724
 
    char *mandos_argz = NULL;
7725
 
  size_t mandos_argz_length = 0;
7726
 
 
7727
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7728
 
                                &helper_directory, &user, &group,
7729
 
                                &mandos_argz, &mandos_argz_length));
7730
 
 
7731
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7732
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7733
 
  g_assert_true(user == 1);
7734
 
  g_assert_true(group == 2);
7735
 
  char *marg = mandos_argz;
7736
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7737
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7738
 
  g_assert_cmpstr(marg, ==, "one");
7739
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7740
 
  g_assert_cmpstr(marg, ==, "two");
7741
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7742
 
  g_assert_cmpstr(marg, ==, "three");
7743
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7744
 
                                            mandos_argz_length),
7745
 
                   ==, 4);
7746
 
 
7747
 
  for(char **arg = argv; *arg != NULL; arg++){
7748
 
    free(*arg);
7749
 
  }
7750
 
}
7751
 
 
7752
 
static void test_parse_arguments_mixed(__attribute__((unused))
7753
 
                                       test_fixture *fixture,
7754
 
                                       __attribute__((unused))
7755
 
                                       gconstpointer user_data){
7756
 
  char *argv[] = {
7757
 
    strdup("prgname"),
7758
 
    strdup("mandos-client"),
7759
 
    strdup("--user"),
7760
 
    strdup("1"),
7761
 
    strdup("one"),
7762
 
    strdup("--agent-directory"),
7763
 
    strdup("/tmp"),
7764
 
    strdup("two"),
7765
 
    strdup("three"),
7766
 
    strdup("--helper-directory=/var/tmp"),
7767
 
    NULL };
7768
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7769
 
 
7770
 
  __attribute__((cleanup(cleanup_string)))
7771
 
    char *agent_directory = NULL;
7772
 
  __attribute__((cleanup(cleanup_string)))
7773
 
    char *helper_directory = NULL;
7774
 
  uid_t user = 0;
7775
 
  gid_t group = 0;
7776
 
  __attribute__((cleanup(cleanup_string)))
7777
 
    char *mandos_argz = NULL;
7778
 
  size_t mandos_argz_length = 0;
7779
 
 
7780
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7781
 
                                &helper_directory, &user, &group,
7782
 
                                &mandos_argz, &mandos_argz_length));
7783
 
 
7784
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7785
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7786
 
  g_assert_true(user == 1);
7787
 
  g_assert_true(group == 0);
7788
 
  char *marg = mandos_argz;
7789
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7790
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7791
 
  g_assert_cmpstr(marg, ==, "one");
7792
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7793
 
  g_assert_cmpstr(marg, ==, "two");
7794
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7795
 
  g_assert_cmpstr(marg, ==, "three");
7796
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7797
 
                                            mandos_argz_length),
7798
 
                   ==, 4);
7799
 
 
7800
 
  for(char **arg = argv; *arg != NULL; arg++){
7801
 
    free(*arg);
7802
 
  }
7803
 
}
7804
 
 
7805
 
/* End of tests section */
7806
 
 
7807
 
/* Test boilerplate section; New tests should be added to the test
7808
 
   suite definition here, in the "run_tests" function.
7809
 
 
7810
 
   Finally, this section also contains the should_only_run_tests()
7811
 
   function used by main() for deciding if tests should be run or to
7812
 
   start normally. */
7813
 
 
7814
 
__attribute__((cold))
7815
 
static bool run_tests(int argc, char *argv[]){
7816
 
  g_test_init(&argc, &argv, NULL);
7817
 
 
7818
 
  /* A macro to add a test with no setup or teardown functions */
7819
 
#define test_add(testpath, testfunc)                    \
7820
 
  do {                                                  \
7821
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
7822
 
               (testfunc), NULL);                       \
7823
 
  } while(false)
7824
 
 
7825
 
  /* Test the signal-related functions first, since some other tests
7826
 
     depend on these functions in their setups and teardowns */
7827
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
7828
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
7829
 
  test_add("/signal-handling/block", test_block_sigchld);
7830
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7831
 
 
7832
 
  /* Regular non-signal-related tests; these use no setups or
7833
 
     teardowns */
7834
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7835
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7836
 
  test_add("/parse_arguments/long-dir",
7837
 
           test_parse_arguments_long_dir);
7838
 
  test_add("/parse_arguments/short-dir",
7839
 
           test_parse_arguments_short_dir);
7840
 
  test_add("/parse_arguments/helper-directory",
7841
 
           test_parse_arguments_helper_directory);
7842
 
  test_add("/parse_arguments/plugin-helper-dir",
7843
 
           test_parse_arguments_plugin_helper_dir);
7844
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
7845
 
  test_add("/parse_arguments/user-invalid",
7846
 
           test_parse_arguments_user_invalid);
7847
 
  test_add("/parse_arguments/user-zero-invalid",
7848
 
           test_parse_arguments_user_zero_invalid);
7849
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
7850
 
  test_add("/parse_arguments/group-invalid",
7851
 
           test_parse_arguments_group_invalid);
7852
 
  test_add("/parse_arguments/group-zero-invalid",
7853
 
           test_parse_arguments_group_zero_invalid);
7854
 
  test_add("/parse_arguments/mandos-noargs",
7855
 
           test_parse_arguments_mandos_noargs);
7856
 
  test_add("/parse_arguments/mandos-args",
7857
 
           test_parse_arguments_mandos_args);
7858
 
  test_add("/parse_arguments/all-args",
7859
 
           test_parse_arguments_all_args);
7860
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7861
 
  test_add("/queue/create", test_create_queue);
7862
 
  test_add("/queue/add", test_add_to_queue);
7863
 
  test_add("/queue/has_question/empty",
7864
 
           test_queue_has_question_empty);
7865
 
  test_add("/queue/has_question/false",
7866
 
           test_queue_has_question_false);
7867
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
7868
 
  test_add("/queue/has_question/false2",
7869
 
           test_queue_has_question_false2);
7870
 
  test_add("/queue/has_question/true2",
7871
 
           test_queue_has_question_true2);
7872
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
7873
 
  test_add("/string_set/net-set-contains-nothing",
7874
 
           test_string_set_new_set_contains_nothing);
7875
 
  test_add("/string_set/with-added-string-contains-it",
7876
 
           test_string_set_with_added_string_contains_it);
7877
 
  test_add("/string_set/cleared-does-not-contain-string",
7878
 
           test_string_set_cleared_does_not_contain_str);
7879
 
  test_add("/string_set/swap/one-with-empty",
7880
 
           test_string_set_swap_one_with_empty);
7881
 
  test_add("/string_set/swap/empty-with-one",
7882
 
           test_string_set_swap_empty_with_one);
7883
 
  test_add("/string_set/swap/one-with-one",
7884
 
           test_string_set_swap_one_with_one);
7885
 
 
7886
 
  /* A macro to add a test using the setup and teardown functions */
7887
 
#define test_add_st(path, func)                                 \
7888
 
  do {                                                          \
7889
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
7890
 
               test_teardown);                                  \
7891
 
  } while(false)
7892
 
 
7893
 
  /* Signal-related tests; these use setups and teardowns which
7894
 
     establish, during each test run, a signal handler for, and a
7895
 
     signal mask blocking, the SIGCHLD signal, just like main() */
7896
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7897
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
7898
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7899
 
  test_add_st("/run_queue/zeroes-next-run",
7900
 
              test_run_queue_zeroes_next_run);
7901
 
  test_add_st("/run_queue/clears-cancelled_filenames",
7902
 
              test_run_queue_clears_cancelled_filenames);
7903
 
  test_add_st("/run_queue/skips-cancelled-filenames",
7904
 
              test_run_queue_skips_cancelled_filenames);
7905
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
7906
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7907
 
  test_add_st("/run_queue/two-tasks/quit",
7908
 
              test_run_queue_two_tasks_quit);
7909
 
  test_add_st("/run_queue/two-tasks-cleanup",
7910
 
              test_run_queue_two_tasks_cleanup);
7911
 
  test_add_st("/task-creators/start_mandos_client",
7912
 
              test_start_mandos_client);
7913
 
  test_add_st("/task-creators/start_mandos_client/execv",
7914
 
              test_start_mandos_client_execv);
7915
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
7916
 
              test_start_mandos_client_suid_euid);
7917
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
7918
 
              test_start_mandos_client_suid_egid);
7919
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
7920
 
              test_start_mandos_client_suid_ruid);
7921
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
7922
 
              test_start_mandos_client_suid_rgid);
7923
 
  test_add_st("/task-creators/start_mandos_client/read",
7924
 
              test_start_mandos_client_read);
7925
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
7926
 
              test_start_mandos_client_helper_directory);
7927
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
7928
 
              test_start_mandos_client_sigmask);
7929
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
7930
 
              test_wait_for_mandos_client_exit_badpid);
7931
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
7932
 
              test_wait_for_mandos_client_exit_noexit);
7933
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
7934
 
              test_wait_for_mandos_client_exit_success);
7935
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
7936
 
              test_wait_for_mandos_client_exit_failure);
7937
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
7938
 
              test_wait_for_mandos_client_exit_killed);
7939
 
  test_add_st("/task/read_mandos_client_output/readerror",
7940
 
              test_read_mandos_client_output_readerror);
7941
 
  test_add_st("/task/read_mandos_client_output/nodata",
7942
 
              test_read_mandos_client_output_nodata);
7943
 
  test_add_st("/task/read_mandos_client_output/eof",
7944
 
              test_read_mandos_client_output_eof);
7945
 
  test_add_st("/task/read_mandos_client_output/once",
7946
 
              test_read_mandos_client_output_once);
7947
 
  test_add_st("/task/read_mandos_client_output/malloc",
7948
 
              test_read_mandos_client_output_malloc);
7949
 
  test_add_st("/task/read_mandos_client_output/append",
7950
 
              test_read_mandos_client_output_append);
7951
 
  test_add_st("/task-creators/add_inotify_dir_watch",
7952
 
              test_add_inotify_dir_watch);
7953
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7954
 
              test_add_inotify_dir_watch_fail);
7955
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7956
 
              test_add_inotify_dir_watch_nondir);
7957
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7958
 
              test_add_inotify_dir_watch_EAGAIN);
7959
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7960
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7961
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7962
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
7963
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7964
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
7965
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7966
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7967
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7968
 
              test_add_inotify_dir_watch_IN_DELETE);
7969
 
  test_add_st("/task/read_inotify_event/readerror",
7970
 
              test_read_inotify_event_readerror);
7971
 
  test_add_st("/task/read_inotify_event/bad-epoll",
7972
 
              test_read_inotify_event_bad_epoll);
7973
 
  test_add_st("/task/read_inotify_event/nodata",
7974
 
              test_read_inotify_event_nodata);
7975
 
  test_add_st("/task/read_inotify_event/eof",
7976
 
              test_read_inotify_event_eof);
7977
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7978
 
              test_read_inotify_event_IN_CLOSE_WRITE);
7979
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7980
 
              test_read_inotify_event_IN_MOVED_TO);
7981
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7982
 
              test_read_inotify_event_IN_MOVED_FROM);
7983
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
7984
 
              test_read_inotify_event_IN_DELETE);
7985
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7986
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7987
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7988
 
              test_read_inotify_event_IN_MOVED_TO_badname);
7989
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7990
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
7991
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7992
 
              test_read_inotify_event_IN_DELETE_badname);
7993
 
  test_add_st("/task/open_and_parse_question/ENOENT",
7994
 
              test_open_and_parse_question_ENOENT);
7995
 
  test_add_st("/task/open_and_parse_question/EIO",
7996
 
              test_open_and_parse_question_EIO);
7997
 
  test_add_st("/task/open_and_parse_question/parse-error",
7998
 
              test_open_and_parse_question_parse_error);
7999
 
  test_add_st("/task/open_and_parse_question/nosocket",
8000
 
              test_open_and_parse_question_nosocket);
8001
 
  test_add_st("/task/open_and_parse_question/badsocket",
8002
 
              test_open_and_parse_question_badsocket);
8003
 
  test_add_st("/task/open_and_parse_question/nopid",
8004
 
              test_open_and_parse_question_nopid);
8005
 
  test_add_st("/task/open_and_parse_question/badpid",
8006
 
              test_open_and_parse_question_badpid);
8007
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
8008
 
              test_open_and_parse_question_noexist_pid);
8009
 
  test_add_st("/task/open_and_parse_question/no-notafter",
8010
 
              test_open_and_parse_question_no_notafter);
8011
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
8012
 
              test_open_and_parse_question_bad_notafter);
8013
 
  test_add_st("/task/open_and_parse_question/notafter-0",
8014
 
              test_open_and_parse_question_notafter_0);
8015
 
  test_add_st("/task/open_and_parse_question/notafter-1",
8016
 
              test_open_and_parse_question_notafter_1);
8017
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
8018
 
              test_open_and_parse_question_notafter_1_1);
8019
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
8020
 
              test_open_and_parse_question_notafter_1_2);
8021
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
8022
 
              test_open_and_parse_question_equal_notafter);
8023
 
  test_add_st("/task/open_and_parse_question/late-notafter",
8024
 
              test_open_and_parse_question_late_notafter);
8025
 
  test_add_st("/task/cancel_old_question/0-1-2",
8026
 
              test_cancel_old_question_0_1_2);
8027
 
  test_add_st("/task/cancel_old_question/0-2-1",
8028
 
              test_cancel_old_question_0_2_1);
8029
 
  test_add_st("/task/cancel_old_question/1-2-3",
8030
 
              test_cancel_old_question_1_2_3);
8031
 
  test_add_st("/task/cancel_old_question/1-3-2",
8032
 
              test_cancel_old_question_1_3_2);
8033
 
  test_add_st("/task/cancel_old_question/2-1-3",
8034
 
              test_cancel_old_question_2_1_3);
8035
 
  test_add_st("/task/cancel_old_question/2-3-1",
8036
 
              test_cancel_old_question_2_3_1);
8037
 
  test_add_st("/task/cancel_old_question/3-1-2",
8038
 
              test_cancel_old_question_3_1_2);
8039
 
  test_add_st("/task/cancel_old_question/3-2-1",
8040
 
              test_cancel_old_question_3_2_1);
8041
 
  test_add_st("/task/connect_question_socket/name-too-long",
8042
 
              test_connect_question_socket_name_too_long);
8043
 
  test_add_st("/task/connect_question_socket/connect-fail",
8044
 
              test_connect_question_socket_connect_fail);
8045
 
  test_add_st("/task/connect_question_socket/bad-epoll",
8046
 
              test_connect_question_socket_bad_epoll);
8047
 
  test_add_st("/task/connect_question_socket/usable",
8048
 
              test_connect_question_socket_usable);
8049
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
8050
 
              test_send_password_to_socket_client_not_exited);
8051
 
  test_add_st("/task/send_password_to_socket/password-not-read",
8052
 
              test_send_password_to_socket_password_not_read);
8053
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
8054
 
              test_send_password_to_socket_EMSGSIZE);
8055
 
  test_add_st("/task/send_password_to_socket/retry",
8056
 
              test_send_password_to_socket_retry);
8057
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
8058
 
              test_send_password_to_socket_bad_epoll);
8059
 
  test_add_st("/task/send_password_to_socket/null-password",
8060
 
              test_send_password_to_socket_null_password);
8061
 
  test_add_st("/task/send_password_to_socket/empty-password",
8062
 
              test_send_password_to_socket_empty_password);
8063
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
8064
 
              test_send_password_to_socket_empty_str_pass);
8065
 
  test_add_st("/task/send_password_to_socket/text-password",
8066
 
              test_send_password_to_socket_text_password);
8067
 
  test_add_st("/task/send_password_to_socket/binary-password",
8068
 
              test_send_password_to_socket_binary_password);
8069
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
8070
 
              test_send_password_to_socket_nuls_in_password);
8071
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
8072
 
              test_add_existing_questions_ENOENT);
8073
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
8074
 
              test_add_existing_questions_no_questions);
8075
 
  test_add_st("/task-creators/add_existing_questions/one-question",
8076
 
              test_add_existing_questions_one_question);
8077
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
8078
 
              test_add_existing_questions_two_questions);
8079
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
8080
 
              test_add_existing_questions_non_questions);
8081
 
  test_add_st("/task-creators/add_existing_questions/both-types",
8082
 
              test_add_existing_questions_both_types);
8083
 
 
8084
 
  return g_test_run() == 0;
8085
 
}
8086
 
 
8087
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8088
 
  GOptionContext *context = g_option_context_new("");
8089
 
 
8090
 
  g_option_context_set_help_enabled(context, FALSE);
8091
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
8092
 
 
8093
 
  gboolean run_tests = FALSE;
8094
 
  GOptionEntry entries[] = {
8095
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
8096
 
      &run_tests, "Run tests", NULL },
8097
 
    { NULL }
8098
 
  };
8099
 
  g_option_context_add_main_entries(context, entries, NULL);
8100
 
 
8101
 
  GError *error = NULL;
8102
 
 
8103
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8104
 
    g_option_context_free(context);
8105
 
    g_error("Failed to parse options: %s", error->message);
8106
 
  }
8107
 
 
8108
 
  g_option_context_free(context);
8109
 
  return run_tests != FALSE;
8110
 
}