/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: 2016-03-19 03:51:23 UTC
  • Revision ID: teddy@recompile.se-20160319035123-53w4dbzdyjef29m1
Server: New tmpfiles.d file for persistent state directory

Provide a tmpfiles.d(5) file for systemd to create persistent state
directory on so-called "volatile" systems.

* Makefile (TMPFILES): New.
  (install-server): Also install "tmpfiles.d-mandos.conf" file as
                    "/usr/lib/tmpfiles.d/mandos.conf".
* tmpfiles.d-mandos.conf: New.

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