/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: 2019-03-03 01:08:58 UTC
  • Revision ID: teddy@recompile.se-20190303010858-c2l0sr6ekvzo7rlb
mandos-ctl: Separate determining what to do and actually doing it

* mandos-ctl (defaultkeywords): Removed; value moved into
                                PrintTableCmd.
  (Command): New abstract base class for commands to be run.
  (PrintCmd, PropertyCmd): New abstract classes for commands.
  (ValueArgumentMixIn, MillisecondsValueArgumentMixIn): New mixins for
                                                        commands.
  (PrintTableCmd, DumpJSONCmd, IsEnabledCmd, RemoveCmd, ApproveCmd,
  DenyCmd, EnableCmd, DisableCmd, BumpTimeoutCmd, StartCheckerCmd,
  StopCheckerCmd, ApproveByDefaultCmd, DenyByDefaultCmd,
  SetCheckerCmd, SetTimeoutCmd, SetExtendedTimeoutCmd,
  SetApprovalDelayCmd, SetApprovalDurationCmd): New commands.
  (main): Don't look directly at options and do things; instead go
          through all options and add commands to a list, then run all
          commands on clients.

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