/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: 2008-08-01 06:33:15 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080801063315-4k33q3ek28hjc3tu
* plugins.d/plugbasedclient.c: Update include file comments.
  (struct process.disable): Renamed to "disabled".  All users changed.
  (addarguments): Renamed to "addargument".  All callers changed.
  (doc, args_doc): Removed.
  (main): Changed default "plugindir" to
          "/conf/conf.d/mandos/plugins.d".  Renamed "rfds_orig" to
           "rfds_all"; all users changed.  New "debug" and
           "exitstatus" variables.  New "--debug" option.  Removed
           unnecessary ".flags = 0" from all options.  Changed so that
           "--disable-plugin" only takes one plugin.  Check for
           non-existence of ":" in argument to "--options-for".  Added
           some debugging outputs.  Set the FD_CLOEXEC flag on the
           directory FD.  More capable code for dealing with
           disallowed plugin file name prefixes and suffixes.  Bug
           fix: check for some malloc failures.  Moved allocating the
           "struct process *new_process" to after the fork.  Do
           _exit() instead of exit() in the child.

* plugins.d/mandosclient.c: Updated contact information.

Show diffs side-by-side

added added

removed removed

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