/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-08-03 11:52:41 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190803115241-oe9726lcojsaij33
dracut-module/password-agent.c: Require agent directory

Require the --agent-directory (by default "/run/systemd/ask-password")
to be an actual directory; fail otherwise.

* dracut-module/password-agent.c (main): Make ENOTDIR from
  add_inotify_dir_watch() result in EX_OSFILE.
  (add_inotify_dir_watch): Add "IN_ONLYDIR" flag to
                           inotify_add_watch().
  (test_add_inotify_dir_watch_nondir): New test.
  (run_tests): Add new test.

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
}