/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2015-10-24 17:48:13 UTC
  • mto: This revision was merged to the branch mainline in revision 791.
  • Revision ID: teddy@recompile.se-20151024174813-56h6prp05qt30fx0
Tags: version-1.7.1-1
* Makefile (version): Changed to "1.7.1".
* NEWS (Version 1.7.1): New entry.
* debian/changelog (1.7.1-1): - '' -

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