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)))))); -*- */
 
3
 
 * Mandos password agent - Simple password agent to run Mandos client
 
5
 
 * Copyright © 2019 Teddy Hogeborn
 
6
 
 * Copyright © 2019 Björn Påhlsson
 
8
 
 * This file is part of Mandos.
 
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.
 
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.
 
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/>.
 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
 
28
 
                                   intmax_t, uint32_t, SCNx32,
 
30
 
#include <stddef.h>             /* size_t */
 
31
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
 
33
 
#include <stdbool.h>            /* bool, true, false */
 
34
 
#include <signal.h>             /* struct sigaction, sigset_t,
 
35
 
                                   sigemptyset(), sigaddset(),
 
36
 
                                   SIGCHLD, pthread_sigmask(),
 
37
 
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
 
38
 
                                   SA_NOCLDSTOP, sigfillset(), kill(),
 
39
 
                                   SIGTERM, sigdelset(), SIGKILL,
 
40
 
                                   NSIG, sigismember(), SA_ONSTACK,
 
41
 
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
 
42
 
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
43
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
44
 
                                   malloc(), free(), strtoumax(),
 
45
 
                                   realloc(), setenv(), calloc(),
 
46
 
                                   mkdtemp(), mkostemp() */
 
47
 
#include <iso646.h>             /* not, or, and, xor */
 
48
 
#include <error.h>              /* error() */
 
49
 
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
 
50
 
#include <errno.h>              /* errno, error_t, EACCES,
 
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
 
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
 
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
 
54
 
                                   ECONNREFUSED, ECONNRESET,
 
55
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
 
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(),
 
64
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
 
65
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
 
66
 
                                   struct epoll_event, EPOLLIN,
 
69
 
#include <time.h>               /* struct timespec, clock_gettime(),
 
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(),
 
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
77
 
                                   fork(), _exit(), dup2(),
 
78
 
                                   STDOUT_FILENO, setresgid(),
 
79
 
                                   setresuid(), execv(), ssize_t,
 
80
 
                                   read(), dup3(), getuid(), dup(),
 
81
 
                                   STDERR_FILENO, pause(), write(),
 
82
 
                                   rmdir(), unlink(), getpid() */
 
83
 
#include <sys/mman.h>           /* munlock(), mlock() */
 
84
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
85
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
86
 
                                   open(), O_WRONLY, O_RDONLY */
 
87
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
89
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
90
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
91
 
                                   IN_CLOEXEC, inotify_add_watch(),
 
92
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
93
 
                                   IN_DELETE, struct inotify_event */
 
94
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
95
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
96
 
                                   getline(), sscanf(), feof(),
 
97
 
                                   ferror(), fclose(), stderr,
 
98
 
                                   rename(), fdopen(), fprintf(),
 
100
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
 
101
 
                        GError, g_key_file_load_from_file(),
 
102
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
 
103
 
                        g_key_file_get_string(), guint64,
 
104
 
                        g_key_file_get_uint64(),
 
105
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
 
106
 
                        g_assert_true(), g_assert_nonnull(),
 
107
 
                        g_assert_null(), g_assert_false(),
 
108
 
                        g_assert_cmpint(), g_assert_cmpuint(),
 
109
 
                        g_test_skip(), g_assert_cmpstr(),
 
110
 
                        g_test_init(), g_test_add(), g_test_run(),
 
111
 
                        GOptionContext, g_option_context_new(),
 
112
 
                        g_option_context_set_help_enabled(), FALSE,
 
113
 
                        g_option_context_set_ignore_unknown_options(),
 
114
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
 
115
 
                        g_option_context_add_main_entries(),
 
116
 
                        g_option_context_parse(),
 
117
 
                        g_option_context_free(), g_error() */
 
118
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
 
119
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
 
120
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
 
121
 
                                   SOCK_CLOEXEC, connect(),
 
122
 
                                   struct sockaddr, socklen_t,
 
123
 
                                   shutdown(), SHUT_RD, send(),
 
124
 
                                   MSG_NOSIGNAL, bind(), recv(),
 
126
 
#include <glob.h>               /* globfree(), glob_t, glob(),
 
127
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
 
128
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
 
131
 
/* End of includes */
 
133
 
/* Start of declarations of private types and functions */
 
135
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
 
136
 
typedef uintmax_t mono_microsecs;
 
138
 
/* "task_queue" - A queue of tasks to be run */
 
140
 
  struct task_struct *tasks;    /* Tasks in this queue */
 
141
 
  size_t length;                /* Number of tasks */
 
142
 
  /* Memory allocated for "tasks", in bytes */
 
144
 
  /* Time when this queue should be run, at the latest */
 
145
 
  mono_microsecs next_run;
 
146
 
} __attribute__((designated_init)) task_queue;
 
148
 
/* "func_type" - A function type for task functions
 
150
 
   I.e. functions for the code which runs when a task is run, all have
 
152
 
typedef void (task_func) (const struct task_struct,
 
154
 
  __attribute__((nonnull));
 
156
 
/* "buffer" - A data buffer for a growing array of bytes
 
158
 
   Used for the "password" variable */
 
163
 
} __attribute__((designated_init)) buffer;
 
165
 
/* "string_set" - A set type which can contain strings
 
167
 
   Used by the "cancelled_filenames" variable */
 
169
 
  char *argz;                   /* Do not access these except in */
 
170
 
  size_t argz_len;              /* the string_set_* functions */
 
171
 
} __attribute__((designated_init)) string_set;
 
173
 
/* "task_context" - local variables for tasks
 
175
 
   This data structure distinguishes between different tasks which are
 
176
 
   using the same function.  This data structure is passed to every
 
177
 
   task function when each task is run.
 
179
 
   Note that not every task uses every struct member. */
 
180
 
typedef struct task_struct {
 
181
 
  task_func *const func;         /* The function run by this task */
 
182
 
  char *const question_filename; /* The question file */
 
183
 
  const pid_t pid;               /* Mandos client process ID */
 
184
 
  const int epoll_fd;            /* The epoll set file descriptor */
 
185
 
  bool *const quit_now;          /* Set to true on fatal errors */
 
186
 
  const int fd;                  /* General purpose file descriptor */
 
187
 
  bool *const mandos_client_exited; /* Set true when client exits */
 
188
 
  buffer *const password;           /* As read from client process */
 
189
 
  bool *const password_is_read;     /* "password" is done growing */
 
190
 
  char *filename;                   /* General purpose file name */
 
191
 
  /* A set of strings of all the file names of questions which have
 
192
 
     been cancelled for any reason; tasks pertaining to these question
 
193
 
     files should not be run */
 
194
 
  string_set *const cancelled_filenames;
 
195
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
 
196
 
  /* Updated before each queue run; is compared with queue.next_run */
 
197
 
  const mono_microsecs *const current_time;
 
198
 
} __attribute__((designated_init)) task_context;
 
200
 
/* Declare all our functions here so we can define them in any order
 
201
 
   below.  Note: test functions are *not* declared here, they are
 
202
 
   declared in the test section. */
 
203
 
__attribute__((warn_unused_result))
 
204
 
static bool should_only_run_tests(int *, char **[]);
 
205
 
__attribute__((warn_unused_result, cold))
 
206
 
static bool run_tests(int, char *[]);
 
207
 
static void handle_sigchld(__attribute__((unused)) int sig){}
 
208
 
__attribute__((warn_unused_result, malloc))
 
209
 
task_queue *create_queue(void);
 
210
 
__attribute__((nonnull, warn_unused_result))
 
211
 
bool add_to_queue(task_queue *const, const task_context);
 
212
 
__attribute__((nonnull))
 
213
 
void cleanup_task(const task_context *const);
 
214
 
__attribute__((nonnull))
 
215
 
void cleanup_queue(task_queue *const *const);
 
216
 
__attribute__((pure, nonnull, warn_unused_result))
 
217
 
bool queue_has_question(const task_queue *const);
 
218
 
__attribute__((nonnull))
 
219
 
void cleanup_close(const int *const);
 
220
 
__attribute__((nonnull))
 
221
 
void cleanup_string(char *const *const);
 
222
 
__attribute__((nonnull))
 
223
 
void cleanup_buffer(buffer *const);
 
224
 
__attribute__((pure, nonnull, warn_unused_result))
 
225
 
bool string_set_contains(const string_set, const char *const);
 
226
 
__attribute__((nonnull, warn_unused_result))
 
227
 
bool string_set_add(string_set *const, const char *const);
 
228
 
__attribute__((nonnull))
 
229
 
void string_set_clear(string_set *);
 
230
 
void string_set_swap(string_set *const, string_set *const);
 
231
 
__attribute__((nonnull, warn_unused_result))
 
232
 
bool start_mandos_client(task_queue *const, const int, bool *const,
 
233
 
                         bool *const, buffer *const, bool *const,
 
234
 
                         const struct sigaction *const,
 
235
 
                         const sigset_t, const char *const,
 
236
 
                         const uid_t, const gid_t,
 
237
 
                         const char *const *const);
 
238
 
__attribute__((nonnull))
 
239
 
task_func wait_for_mandos_client_exit;
 
240
 
__attribute__((nonnull))
 
241
 
task_func read_mandos_client_output;
 
242
 
__attribute__((warn_unused_result))
 
243
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
 
244
 
                           buffer *const, const char *const,
 
245
 
                           string_set *, const mono_microsecs *const,
 
246
 
                           bool *const, bool *const);
 
247
 
__attribute__((nonnull))
 
248
 
task_func read_inotify_event;
 
249
 
__attribute__((nonnull))
 
250
 
task_func open_and_parse_question;
 
251
 
__attribute__((nonnull))
 
252
 
task_func cancel_old_question;
 
253
 
__attribute__((nonnull))
 
254
 
task_func connect_question_socket;
 
255
 
__attribute__((nonnull))
 
256
 
task_func send_password_to_socket;
 
257
 
__attribute__((warn_unused_result))
 
258
 
bool add_existing_questions(task_queue *const, const int,
 
259
 
                            buffer *const, string_set *,
 
260
 
                            const mono_microsecs *const,
 
261
 
                            bool *const, bool *const,
 
263
 
__attribute__((nonnull, warn_unused_result))
 
264
 
bool wait_for_event(const int, const mono_microsecs,
 
265
 
                    const mono_microsecs);
 
266
 
bool run_queue(task_queue **const, string_set *const, bool *const);
 
267
 
bool clear_all_fds_from_epoll_set(const int);
 
268
 
mono_microsecs get_current_time(void);
 
269
 
__attribute__((nonnull, warn_unused_result))
 
270
 
bool setup_signal_handler(struct sigaction *const);
 
271
 
__attribute__((nonnull))
 
272
 
bool restore_signal_handler(const struct sigaction *const);
 
273
 
__attribute__((nonnull, warn_unused_result))
 
274
 
bool block_sigchld(sigset_t *const);
 
275
 
__attribute__((nonnull))
 
276
 
bool restore_sigmask(const sigset_t *const);
 
277
 
__attribute__((nonnull))
 
278
 
bool parse_arguments(int, char *[], const bool, char **, char **,
 
279
 
                     uid_t *const , gid_t *const, char **, size_t *);
 
281
 
/* End of declarations of private types and functions */
 
283
 
/* Start of "main" section; this section LACKS TESTS!
 
285
 
   Code here should be as simple as possible. */
 
287
 
/* These are required to be global by Argp */
 
288
 
const char *argp_program_version = "password-agent " VERSION;
 
289
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
291
 
int main(int argc, char *argv[]){
 
293
 
  /* If the --test option is passed, skip all normal operations and
 
294
 
     instead only run the run_tests() function, which also does all
 
295
 
     its own option parsing, so we don't have to do anything here. */
 
296
 
  if(should_only_run_tests(&argc, &argv)){
 
297
 
    if(run_tests(argc, argv)){
 
298
 
      return EXIT_SUCCESS;      /* All tests successful */
 
300
 
    return EXIT_FAILURE;        /* Some test(s) failed */
 
303
 
  __attribute__((cleanup(cleanup_string)))
 
304
 
    char *agent_directory = NULL;
 
306
 
  __attribute__((cleanup(cleanup_string)))
 
307
 
    char *helper_directory = NULL;
 
312
 
  __attribute__((cleanup(cleanup_string)))
 
313
 
    char *mandos_argz = NULL;
 
314
 
  size_t mandos_argz_length = 0;
 
316
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
 
317
 
                         &helper_directory, &user, &group,
 
318
 
                         &mandos_argz, &mandos_argz_length)){
 
319
 
    /* This should never happen, since "true" is passed as the third
 
320
 
       argument to parse_arguments() above, which should make
 
321
 
       argp_parse() call exit() if any parsing error occurs. */
 
322
 
    error(EX_USAGE, errno, "Failed to parse arguments");
 
325
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
 
326
 
  const char default_helper_directory[]
 
327
 
    = "/lib/mandos/plugin-helpers";
 
328
 
  const char *const default_argv[]
 
329
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
 
331
 
  /* Set variables to default values if unset */
 
332
 
  if(agent_directory == NULL){
 
333
 
    agent_directory = strdup(default_agent_directory);
 
334
 
    if(agent_directory == NULL){
 
335
 
      error(EX_OSERR, errno, "Failed strdup()");
 
338
 
  if(helper_directory == NULL){
 
339
 
    helper_directory = strdup(default_helper_directory);
 
340
 
    if(helper_directory == NULL){
 
341
 
      error(EX_OSERR, errno, "Failed strdup()");
 
345
 
    user = 65534;               /* nobody */
 
348
 
    group = 65534;              /* nogroup */
 
350
 
  /* If parse_opt did not create an argz vector, create one with
 
352
 
  if(mandos_argz == NULL){
 
354
 
#pragma GCC diagnostic push
 
355
 
    /* argz_create() takes a non-const argv for some unknown reason -
 
356
 
       argz_create() isn't modifying the strings, just copying them.
 
357
 
       Therefore, this cast to non-const should be safe. */
 
358
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
360
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
 
361
 
                        &mandos_argz_length);
 
363
 
#pragma GCC diagnostic pop
 
366
 
      error(EX_OSERR, errno, "Failed argz_create()");
 
369
 
  /* Use argz vector to create a normal argv, usable by execv() */
 
371
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
 
373
 
                               + 1) * sizeof(char *));
 
374
 
  if(mandos_argv == NULL){
 
375
 
    error_t saved_errno = errno;
 
377
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
 
379
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
 
381
 
  sigset_t orig_sigmask;
 
382
 
  if(not block_sigchld(&orig_sigmask)){
 
386
 
  struct sigaction old_sigchld_action;
 
387
 
  if(not setup_signal_handler(&old_sigchld_action)){
 
391
 
  mono_microsecs current_time = 0;
 
393
 
  bool mandos_client_exited = false;
 
394
 
  bool quit_now = false;
 
395
 
  __attribute__((cleanup(cleanup_close)))
 
396
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
398
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
 
400
 
  __attribute__((cleanup(cleanup_queue)))
 
401
 
    task_queue *queue = create_queue();
 
403
 
    error(EX_OSERR, errno, "Failed to create task queue");
 
406
 
  __attribute__((cleanup(cleanup_buffer)))
 
407
 
    buffer password = {};
 
408
 
  bool password_is_read = false;
 
410
 
  __attribute__((cleanup(string_set_clear)))
 
411
 
    string_set cancelled_filenames = {};
 
413
 
  /* Add tasks to queue */
 
414
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
 
415
 
                             &quit_now, &password, &password_is_read,
 
416
 
                             &old_sigchld_action, orig_sigmask,
 
417
 
                             helper_directory, user, group,
 
418
 
                             (const char *const *)mandos_argv)){
 
419
 
    return EX_OSERR;            /* Error has already been printed */
 
421
 
  /* These variables were only for start_mandos_client() and are not
 
426
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
 
427
 
                               agent_directory, &cancelled_filenames,
 
428
 
                               ¤t_time, &mandos_client_exited,
 
430
 
    switch(errno){              /* Error has already been printed */
 
439
 
  if(not add_existing_questions(queue, epoll_fd, &password,
 
440
 
                                &cancelled_filenames, ¤t_time,
 
441
 
                                &mandos_client_exited,
 
442
 
                                &password_is_read, agent_directory)){
 
443
 
    return EXIT_FAILURE;        /* Error has already been printed */
 
448
 
    current_time = get_current_time();
 
449
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
 
450
 
      const error_t saved_errno = errno;
 
451
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
 
455
 
    current_time = get_current_time();
 
456
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
 
457
 
      const error_t saved_errno = errno;
 
458
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
 
461
 
    /*  When no tasks about questions are left in the queue, break out
 
462
 
        of the loop (and implicitly exit the program) */
 
463
 
  } while(queue_has_question(queue));
 
465
 
  restore_signal_handler(&old_sigchld_action);
 
466
 
  restore_sigmask(&orig_sigmask);
 
471
 
__attribute__((warn_unused_result))
 
472
 
mono_microsecs get_current_time(void){
 
473
 
  struct timespec currtime;
 
474
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
 
475
 
    error(0, errno, "Failed to get current time");
 
478
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
 
479
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
 
482
 
/* End of "main" section */
 
484
 
/* Start of regular code section; ALL this code has tests */
 
486
 
__attribute__((nonnull))
 
487
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
 
488
 
                     char **agent_directory, char **helper_directory,
 
489
 
                     uid_t *const user, gid_t *const group,
 
490
 
                     char **mandos_argz, size_t *mandos_argz_length){
 
492
 
  const struct argp_option options[] = {
 
493
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
 
494
 
      .doc="Systemd password agent directory" },
 
495
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
 
496
 
      .doc="Mandos Client password helper directory" },
 
497
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
 
498
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
499
 
    { .name="user", .key='u', .arg="USERID",
 
500
 
      .doc="User ID the Mandos Client will use as its unprivileged"
 
502
 
    { .name="userid", .key=130, /* From plugin--runner */
 
503
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
504
 
    { .name="group", .key='g', .arg="GROUPID",
 
505
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
 
507
 
    { .name="groupid", .key=131, /* From plugin--runner */
 
508
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
509
 
    { .name="test", .key=255, /* See should_only_run_tests() */
 
510
 
      .doc="Skip normal operation, and only run self-tests.  See"
 
511
 
      " --test --help.", .group=10, },
 
515
 
  __attribute__((nonnull(3)))
 
516
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
 
519
 
    case 'd':                   /* --agent-directory */
 
520
 
      *agent_directory = strdup(arg);
 
522
 
    case 128:                   /* --helper-directory */
 
523
 
    case 129:                   /* --plugin-helper-dir */
 
524
 
      *helper_directory = strdup(arg);
 
526
 
    case 'u':                   /* --user */
 
527
 
    case 130:                   /* --userid */
 
530
 
        uintmax_t tmp_id = 0;
 
532
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
533
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
534
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
 
535
 
          return ARGP_ERR_UNKNOWN;
 
537
 
        *user = (uid_t)tmp_id;
 
541
 
    case 'g':                   /* --group */
 
542
 
    case 131:                   /* --groupid */
 
545
 
        uintmax_t tmp_id = 0;
 
547
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
548
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
549
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
 
550
 
          return ARGP_ERR_UNKNOWN;
 
552
 
        *group = (gid_t)tmp_id;
 
557
 
      /* Copy arguments into argz vector */
 
558
 
      return argz_create(state->argv + state->next, mandos_argz,
 
561
 
      return ARGP_ERR_UNKNOWN;
 
566
 
  const struct argp argp = {
 
569
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
 
570
 
    .doc = "Mandos password agent -- runs Mandos client as a"
 
571
 
    " systemd password agent",
 
574
 
  errno = argp_parse(&argp, argc, argv,
 
575
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
 
580
 
__attribute__((nonnull, warn_unused_result))
 
581
 
bool block_sigchld(sigset_t *const orig_sigmask){
 
582
 
  sigset_t sigchld_sigmask;
 
583
 
  if(sigemptyset(&sigchld_sigmask) < 0){
 
584
 
    error(0, errno, "Failed to empty signal set");
 
587
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
 
588
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
 
591
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
 
592
 
    error(0, errno, "Failed to block SIGCHLD signal");
 
598
 
__attribute__((nonnull, warn_unused_result, const))
 
599
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
 
600
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
 
601
 
    error(0, errno, "Failed to restore blocked signals");
 
607
 
__attribute__((nonnull, warn_unused_result))
 
608
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
 
609
 
  struct sigaction sigchld_action = {
 
610
 
    .sa_handler=handle_sigchld,
 
611
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
613
 
  /* Set all signals in "sa_mask" struct member; this makes all
 
614
 
     signals automatically blocked during signal handler */
 
615
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
 
616
 
    error(0, errno, "Failed to do sigfillset()");
 
619
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
 
620
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
 
626
 
__attribute__((nonnull, warn_unused_result))
 
627
 
bool restore_signal_handler(const struct sigaction *const
 
629
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
 
630
 
    error(0, errno, "Failed to restore signal handler");
 
636
 
__attribute__((warn_unused_result, malloc))
 
637
 
task_queue *create_queue(void){
 
638
 
  task_queue *queue = malloc(sizeof(task_queue));
 
642
 
    queue->allocated = 0;
 
648
 
__attribute__((nonnull, warn_unused_result))
 
649
 
bool add_to_queue(task_queue *const queue, const task_context task){
 
650
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
651
 
  if(needed_size > (queue->allocated)){
 
652
 
    task_context *const new_tasks = realloc(queue->tasks,
 
654
 
    if(new_tasks == NULL){
 
655
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
656
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
659
 
    queue->tasks = new_tasks;
 
660
 
    queue->allocated = needed_size;
 
662
 
  /* Using memcpy here is necessary because doing */
 
663
 
  /* queue->tasks[queue->length++] = task; */
 
664
 
  /* would violate const-ness of task members */
 
665
 
  memcpy(&(queue->tasks[queue->length++]), &task,
 
666
 
         sizeof(task_context));
 
670
 
__attribute__((nonnull))
 
671
 
void cleanup_task(const task_context *const task){
 
672
 
  const error_t saved_errno = errno;
 
673
 
  /* free and close all task data */
 
674
 
  free(task->question_filename);
 
675
 
  if(task->filename != task->question_filename){
 
676
 
    free(task->filename);
 
679
 
    kill(task->pid, SIGTERM);
 
687
 
__attribute__((nonnull))
 
688
 
void free_queue(task_queue *const queue){
 
693
 
__attribute__((nonnull))
 
694
 
void cleanup_queue(task_queue *const *const queue){
 
698
 
  for(size_t i = 0; i < (*queue)->length; i++){
 
699
 
    const task_context *const task = ((*queue)->tasks)+i;
 
705
 
__attribute__((pure, nonnull, warn_unused_result))
 
706
 
bool queue_has_question(const task_queue *const queue){
 
707
 
  for(size_t i=0; i < queue->length; i++){
 
708
 
    if(queue->tasks[i].question_filename != NULL){
 
715
 
__attribute__((nonnull))
 
716
 
void cleanup_close(const int *const fd){
 
717
 
  const error_t saved_errno = errno;
 
722
 
__attribute__((nonnull))
 
723
 
void cleanup_string(char *const *const ptr){
 
727
 
__attribute__((nonnull))
 
728
 
void cleanup_buffer(buffer *buf){
 
729
 
  if(buf->allocated > 0){
 
730
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
731
 
    explicit_bzero(buf->data, buf->allocated);
 
733
 
    memset(buf->data, '\0', buf->allocated);
 
736
 
  if(buf->data != NULL){
 
737
 
    if(munlock(buf->data, buf->allocated) != 0){
 
738
 
      error(0, errno, "Failed to unlock memory of old buffer");
 
747
 
__attribute__((pure, nonnull, warn_unused_result))
 
748
 
bool string_set_contains(const string_set set, const char *const str){
 
749
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
750
 
      s = argz_next(set.argz, set.argz_len, s)){
 
751
 
    if(strcmp(s, str) == 0){
 
758
 
__attribute__((nonnull, warn_unused_result))
 
759
 
bool string_set_add(string_set *const set, const char *const str){
 
760
 
  if(string_set_contains(*set, str)){
 
763
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
771
 
__attribute__((nonnull))
 
772
 
void string_set_clear(string_set *set){
 
778
 
__attribute__((nonnull))
 
779
 
void string_set_swap(string_set *const set1, string_set *const set2){
 
780
 
  /* Swap contents of two string sets */
 
782
 
    char *const tmp_argz = set1->argz;
 
783
 
    set1->argz = set2->argz;
 
784
 
    set2->argz = tmp_argz;
 
787
 
    const size_t tmp_argz_len = set1->argz_len;
 
788
 
    set1->argz_len = set2->argz_len;
 
789
 
    set2->argz_len = tmp_argz_len;
 
793
 
__attribute__((nonnull, warn_unused_result))
 
794
 
bool start_mandos_client(task_queue *const queue,
 
796
 
                         bool *const mandos_client_exited,
 
797
 
                         bool *const quit_now, buffer *const password,
 
798
 
                         bool *const password_is_read,
 
799
 
                         const struct sigaction *const
 
800
 
                         old_sigchld_action, const sigset_t sigmask,
 
801
 
                         const char *const helper_directory,
 
802
 
                         const uid_t user, const gid_t group,
 
803
 
                         const char *const *const argv){
 
805
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
806
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
810
 
  const pid_t pid = fork();
 
812
 
    if(not restore_signal_handler(old_sigchld_action)){
 
815
 
    if(not restore_sigmask(&sigmask)){
 
818
 
    if(close(pipefds[0]) != 0){
 
819
 
      error(0, errno, "Failed to close() parent pipe fd");
 
822
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
823
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
826
 
    if(close(pipefds[1]) != 0){
 
827
 
      error(0, errno, "Failed to close() old child pipe fd");
 
830
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
831
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
832
 
            " \"%s\", 1)", helper_directory);
 
835
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
836
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
837
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
840
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
841
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
842
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
846
 
#pragma GCC diagnostic push
 
847
 
    /* For historical reasons, the "argv" argument to execv() is not
 
848
 
       const, but it is safe to override this. */
 
849
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
851
 
    execv(argv[0], (char **)argv);
 
853
 
#pragma GCC diagnostic pop
 
855
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
860
 
  if(not add_to_queue(queue, (task_context){
 
861
 
        .func=wait_for_mandos_client_exit,
 
863
 
        .mandos_client_exited=mandos_client_exited,
 
866
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
871
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
872
 
                            &(struct epoll_event)
 
873
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
874
 
  if(ret != 0 and errno != EEXIST){
 
875
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
880
 
  return add_to_queue(queue, (task_context){
 
881
 
      .func=read_mandos_client_output,
 
886
 
      .password_is_read=password_is_read,
 
890
 
__attribute__((nonnull))
 
891
 
void wait_for_mandos_client_exit(const task_context task,
 
892
 
                                 task_queue *const queue){
 
893
 
  const pid_t pid = task.pid;
 
894
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
895
 
  bool *const quit_now = task.quit_now;
 
898
 
  switch(waitpid(pid, &status, WNOHANG)){
 
899
 
  case 0:                       /* Not exited yet */
 
900
 
    if(not add_to_queue(queue, task)){
 
901
 
      error(0, errno, "Failed to add myself to queue");
 
906
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
912
 
  default:                      /* Has exited */
 
913
 
    *mandos_client_exited = true;
 
914
 
    if((not WIFEXITED(status))
 
915
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
916
 
      error(0, 0, "Mandos client failed or was killed");
 
922
 
__attribute__((nonnull))
 
923
 
void read_mandos_client_output(const task_context task,
 
924
 
                               task_queue *const queue){
 
925
 
  buffer *const password = task.password;
 
926
 
  bool *const quit_now = task.quit_now;
 
927
 
  bool *const password_is_read = task.password_is_read;
 
928
 
  const int fd = task.fd;
 
929
 
  const int epoll_fd = task.epoll_fd;
 
931
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
932
 
  if(password->allocated < new_potential_size){
 
933
 
    char *const new_buffer = calloc(new_potential_size, 1);
 
934
 
    if(new_buffer == NULL){
 
935
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
936
 
            " bytes for password", (uintmax_t)new_potential_size);
 
941
 
    if(mlock(new_buffer, new_potential_size) != 0){
 
942
 
      /* Warn but do not treat as fatal error */
 
943
 
      if(errno != EPERM and errno != ENOMEM){
 
944
 
        error(0, errno, "Failed to lock memory for password");
 
947
 
    if(password->length > 0){
 
948
 
      memcpy(new_buffer, password->data, password->length);
 
949
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
950
 
      explicit_bzero(password->data, password->allocated);
 
952
 
      memset(password->data, '\0', password->allocated);
 
955
 
    if(password->data != NULL){
 
956
 
      if(munlock(password->data, password->allocated) != 0){
 
957
 
        error(0, errno, "Failed to unlock memory of old buffer");
 
959
 
      free(password->data);
 
961
 
    password->data = new_buffer;
 
962
 
    password->allocated = new_potential_size;
 
965
 
  const ssize_t read_length = read(fd, password->data
 
966
 
                                   + password->length, PIPE_BUF);
 
968
 
  if(read_length == 0){ /* EOF */
 
969
 
    *password_is_read = true;
 
973
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
974
 
    error(0, errno, "Failed to read password from Mandos client");
 
979
 
  if(read_length > 0){          /* Data has been read */
 
980
 
    password->length += (size_t)read_length;
 
983
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
986
 
  /* Re-add the fd to the epoll set */
 
987
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
988
 
                            &(struct epoll_event)
 
989
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
990
 
  if(ret != 0 and errno != EEXIST){
 
991
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
997
 
  /* Re-add myself to the queue */
 
998
 
  if(not add_to_queue(queue, task)){
 
999
 
    error(0, errno, "Failed to add myself to queue");
 
1005
 
__attribute__((nonnull, warn_unused_result))
 
1006
 
bool add_inotify_dir_watch(task_queue *const queue,
 
1007
 
                           const int epoll_fd, bool *const quit_now,
 
1008
 
                           buffer *const password,
 
1009
 
                           const char *const dir,
 
1010
 
                           string_set *cancelled_filenames,
 
1011
 
                           const mono_microsecs *const current_time,
 
1012
 
                           bool *const mandos_client_exited,
 
1013
 
                           bool *const password_is_read){
 
1014
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
1016
 
    error(0, errno, "Failed to create inotify instance");
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
1023
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
1027
 
  /* Add the inotify fd to the epoll set */
 
1028
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1029
 
                            &(struct epoll_event)
 
1030
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1031
 
  if(ret != 0 and errno != EEXIST){
 
1032
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
1037
 
  const task_context read_inotify_event_task = {
 
1038
 
    .func=read_inotify_event,
 
1043
 
    .filename=strdup(dir),
 
1044
 
    .cancelled_filenames=cancelled_filenames,
 
1045
 
    .current_time=current_time,
 
1046
 
    .mandos_client_exited=mandos_client_exited,
 
1047
 
    .password_is_read=password_is_read,
 
1049
 
  if(read_inotify_event_task.filename == NULL){
 
1050
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
1055
 
  return add_to_queue(queue, read_inotify_event_task);
 
1058
 
__attribute__((nonnull))
 
1059
 
void read_inotify_event(const task_context task,
 
1060
 
                        task_queue *const queue){
 
1061
 
  const int fd = task.fd;
 
1062
 
  const int epoll_fd = task.epoll_fd;
 
1063
 
  char *const filename = task.filename;
 
1064
 
  bool *quit_now = task.quit_now;
 
1065
 
  buffer *const password = task.password;
 
1066
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1067
 
  const mono_microsecs *const current_time = task.current_time;
 
1068
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1069
 
  bool *const password_is_read = task.password_is_read;
 
1071
 
  /* "sufficient to read at least one event." - inotify(7) */
 
1072
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
1075
 
    struct inotify_event event;
 
1076
 
    char name_buffer[NAME_MAX + 1];
 
1078
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
1080
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
1081
 
  if(read_length == 0){ /* EOF */
 
1082
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
1084
 
    cleanup_task(&task);
 
1087
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
1088
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
1091
 
    cleanup_task(&task);
 
1094
 
  if(read_length > 0            /* Data has been read */
 
1095
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
1096
 
    char *question_filename = NULL;
 
1097
 
    const ssize_t question_filename_length
 
1098
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
1099
 
    if(question_filename_length < 0){
 
1100
 
      error(0, errno, "Failed to create file name from directory name"
 
1101
 
            " %s and file name %s", filename, ievent->name);
 
1103
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
1104
 
        if(not add_to_queue(queue, (task_context){
 
1105
 
              .func=open_and_parse_question,
 
1107
 
              .question_filename=question_filename,
 
1108
 
              .filename=question_filename,
 
1110
 
              .cancelled_filenames=cancelled_filenames,
 
1111
 
              .current_time=current_time,
 
1112
 
              .mandos_client_exited=mandos_client_exited,
 
1113
 
              .password_is_read=password_is_read,
 
1115
 
          error(0, errno, "Failed to add open_and_parse_question task"
 
1116
 
                " for file name %s to queue", filename);
 
1118
 
          /* Force the added task (open_and_parse_question) to run
 
1120
 
          queue->next_run = 1;
 
1122
 
      } else if(ievent->mask & IN_DELETE){
 
1123
 
        if(not string_set_add(cancelled_filenames,
 
1124
 
                              question_filename)){
 
1125
 
          error(0, errno, "Could not add question %s to"
 
1126
 
                " cancelled_questions", question_filename);
 
1128
 
          free(question_filename);
 
1129
 
          cleanup_task(&task);
 
1132
 
        free(question_filename);
 
1137
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1140
 
  /* Re-add myself to the queue */
 
1141
 
  if(not add_to_queue(queue, task)){
 
1142
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
1143
 
          " queue", filename);
 
1145
 
    cleanup_task(&task);
 
1149
 
  /* Re-add the fd to the epoll set */
 
1150
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1151
 
                            &(struct epoll_event)
 
1152
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1153
 
  if(ret != 0 and errno != EEXIST){
 
1154
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
1155
 
          " directory %s to epoll set", fd, filename);
 
1156
 
    /* Force the added task (read_inotify_event) to run again, at most
 
1157
 
       one second from now */
 
1158
 
    if((queue->next_run == 0)
 
1159
 
       or (queue->next_run > (*current_time + 1000000))){
 
1160
 
      queue->next_run = *current_time + 1000000;
 
1165
 
__attribute__((nonnull))
 
1166
 
void open_and_parse_question(const task_context task,
 
1167
 
                             task_queue *const queue){
 
1168
 
  __attribute__((cleanup(cleanup_string)))
 
1169
 
    char *question_filename = task.question_filename;
 
1170
 
  const int epoll_fd = task.epoll_fd;
 
1171
 
  buffer *const password = task.password;
 
1172
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1173
 
  const mono_microsecs *const current_time = task.current_time;
 
1174
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1175
 
  bool *const password_is_read = task.password_is_read;
 
1177
 
  /* We use the GLib "Key-value file parser" functions to parse the
 
1178
 
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1179
 
     /systemd/PasswordAgents/> for specification of contents */
 
1180
 
  __attribute__((nonnull))
 
1181
 
    void cleanup_g_key_file(GKeyFile **key_file){
 
1182
 
    if(*key_file != NULL){
 
1183
 
      g_key_file_free(*key_file);
 
1187
 
  __attribute__((cleanup(cleanup_g_key_file)))
 
1188
 
    GKeyFile *key_file = g_key_file_new();
 
1189
 
  if(key_file == NULL){
 
1190
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
1194
 
  GError *glib_error = NULL;
 
1195
 
  if(g_key_file_load_from_file(key_file, question_filename,
 
1196
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
1197
 
    /* If a file was removed, we should ignore it, so */
 
1198
 
    /* only show error message if file actually existed */
 
1199
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
1200
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
1201
 
            question_filename, glib_error->message);
 
1206
 
  __attribute__((cleanup(cleanup_string)))
 
1207
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
1210
 
  if(socket_name == NULL){
 
1211
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
1212
 
          question_filename, glib_error->message);
 
1216
 
  if(strlen(socket_name) == 0){
 
1217
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
1222
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
1224
 
  if(glib_error != NULL){
 
1225
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
1226
 
          question_filename, glib_error->message);
 
1230
 
  if((pid != (guint64)((pid_t)pid))
 
1231
 
     or (kill((pid_t)pid, 0) != 0)){
 
1232
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
1233
 
          " does not exist", (uintmax_t)pid, question_filename);
 
1237
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
1238
 
                                           "NotAfter", &glib_error);
 
1239
 
  if(glib_error != NULL){
 
1240
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
1241
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
1242
 
            " %s", question_filename, glib_error->message);
 
1247
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
1248
 
      queue->next_run = notafter;
 
1250
 
    if(*current_time >= notafter){
 
1255
 
  const task_context connect_question_socket_task = {
 
1256
 
    .func=connect_question_socket,
 
1257
 
    .question_filename=strdup(question_filename),
 
1260
 
    .filename=strdup(socket_name),
 
1261
 
    .cancelled_filenames=task.cancelled_filenames,
 
1262
 
    .mandos_client_exited=mandos_client_exited,
 
1263
 
    .password_is_read=password_is_read,
 
1264
 
    .current_time=current_time,
 
1266
 
  if(connect_question_socket_task.question_filename == NULL
 
1267
 
     or connect_question_socket_task.filename == NULL
 
1268
 
     or not add_to_queue(queue, connect_question_socket_task)){
 
1269
 
    error(0, errno, "Failed to add connect_question_socket for socket"
 
1270
 
          " %s (from \"%s\") to queue", socket_name,
 
1272
 
    cleanup_task(&connect_question_socket_task);
 
1275
 
  /* Force the added task (connect_question_socket) to run
 
1277
 
  queue->next_run = 1;
 
1280
 
    char *const dup_filename = strdup(question_filename);
 
1281
 
    const task_context cancel_old_question_task = {
 
1282
 
      .func=cancel_old_question,
 
1283
 
      .question_filename=dup_filename,
 
1285
 
      .filename=dup_filename,
 
1286
 
      .cancelled_filenames=cancelled_filenames,
 
1287
 
      .current_time=current_time,
 
1289
 
    if(cancel_old_question_task.question_filename == NULL
 
1290
 
       or not add_to_queue(queue, cancel_old_question_task)){
 
1291
 
      error(0, errno, "Failed to add cancel_old_question for file "
 
1292
 
            "\"%s\" to queue", question_filename);
 
1293
 
      cleanup_task(&cancel_old_question_task);
 
1299
 
__attribute__((nonnull))
 
1300
 
void cancel_old_question(const task_context task,
 
1301
 
                         task_queue *const queue){
 
1302
 
  char *const question_filename = task.question_filename;
 
1303
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1304
 
  const mono_microsecs notafter = task.notafter;
 
1305
 
  const mono_microsecs *const current_time = task.current_time;
 
1307
 
  if(*current_time >= notafter){
 
1308
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1309
 
      error(0, errno, "Failed to cancel question for file %s",
 
1312
 
    cleanup_task(&task);
 
1316
 
  if(not add_to_queue(queue, task)){
 
1317
 
    error(0, errno, "Failed to add cancel_old_question for file "
 
1318
 
          "%s to queue", question_filename);
 
1319
 
    cleanup_task(&task);
 
1323
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
1324
 
    queue->next_run = notafter;
 
1328
 
__attribute__((nonnull))
 
1329
 
void connect_question_socket(const task_context task,
 
1330
 
                             task_queue *const queue){
 
1331
 
  char *const question_filename = task.question_filename;
 
1332
 
  char *const filename = task.filename;
 
1333
 
  const int epoll_fd = task.epoll_fd;
 
1334
 
  buffer *const password = task.password;
 
1335
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1336
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1337
 
  bool *const password_is_read = task.password_is_read;
 
1338
 
  const mono_microsecs *const current_time = task.current_time;
 
1340
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
1342
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
1343
 
    error(0, 0, "Socket filename is larger than"
 
1344
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
1345
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
1346
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1347
 
      error(0, errno, "Failed to cancel question for file %s",
 
1350
 
    cleanup_task(&task);
 
1354
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
1355
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
1358
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
1359
 
    if(not add_to_queue(queue, task)){
 
1360
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1361
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1363
 
      cleanup_task(&task);
 
1365
 
      /* Force the added task (connect_question_socket) to run
 
1367
 
      queue->next_run = 1;
 
1372
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
1373
 
  if(connect(fd, (struct sockaddr *)&sock_name,
 
1374
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
1375
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
1376
 
    if(not add_to_queue(queue, task)){
 
1377
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1378
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1380
 
      cleanup_task(&task);
 
1382
 
      /* Force the added task (connect_question_socket) to run again,
 
1383
 
         at most one second from now */
 
1384
 
      if((queue->next_run == 0)
 
1385
 
         or (queue->next_run > (*current_time + 1000000))){
 
1386
 
        queue->next_run = *current_time + 1000000;
 
1392
 
  /* Not necessary, but we can try, and merely warn on failure */
 
1393
 
  if(shutdown(fd, SHUT_RD) != 0){
 
1394
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
1398
 
  /* Add the fd to the epoll set */
 
1399
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1400
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1402
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
1403
 
          " socket %s to epoll set", fd, filename);
 
1404
 
    if(not add_to_queue(queue, task)){
 
1405
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1406
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1408
 
      cleanup_task(&task);
 
1410
 
      /* Force the added task (connect_question_socket) to run again,
 
1411
 
         at most one second from now */
 
1412
 
      if((queue->next_run == 0)
 
1413
 
         or (queue->next_run > (*current_time + 1000000))){
 
1414
 
        queue->next_run = *current_time + 1000000;
 
1420
 
  /* add task send_password_to_socket to queue */
 
1421
 
  const task_context send_password_to_socket_task = {
 
1422
 
    .func=send_password_to_socket,
 
1423
 
    .question_filename=question_filename,
 
1428
 
    .cancelled_filenames=cancelled_filenames,
 
1429
 
    .mandos_client_exited=mandos_client_exited,
 
1430
 
    .password_is_read=password_is_read,
 
1431
 
    .current_time=current_time,
 
1434
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
1435
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1436
 
          " file \"%s\" and socket \"%s\" to queue",
 
1437
 
          question_filename, filename);
 
1438
 
    cleanup_task(&send_password_to_socket_task);
 
1442
 
__attribute__((nonnull))
 
1443
 
void send_password_to_socket(const task_context task,
 
1444
 
                             task_queue *const queue){
 
1445
 
  char *const question_filename=task.question_filename;
 
1446
 
  char *const filename=task.filename;
 
1447
 
  const int epoll_fd=task.epoll_fd;
 
1448
 
  const int fd=task.fd;
 
1449
 
  buffer *const password=task.password;
 
1450
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
1451
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1452
 
  bool *const password_is_read = task.password_is_read;
 
1453
 
  const mono_microsecs *const current_time = task.current_time;
 
1455
 
  if(*mandos_client_exited and *password_is_read){
 
1457
 
    const size_t send_buffer_length = password->length + 2;
 
1458
 
    char *send_buffer = malloc(send_buffer_length);
 
1459
 
    if(send_buffer == NULL){
 
1460
 
      error(0, errno, "Failed to allocate send_buffer");
 
1462
 
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1463
 
        /* Warn but do not treat as fatal error */
 
1464
 
        if(errno != EPERM and errno != ENOMEM){
 
1465
 
          error(0, errno, "Failed to lock memory for password"
 
1469
 
      /* “[…] send a single datagram to the socket consisting of the
 
1470
 
         password string either prefixed with "+" or with "-"
 
1471
 
         depending on whether the password entry was successful or
 
1472
 
         not. You may but don't have to include a final NUL byte in
 
1475
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1476
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1478
 
      send_buffer[0] = '+';     /* Prefix with "+" */
 
1479
 
      /* Always add an extra NUL */
 
1480
 
      send_buffer[password->length + 1] = '\0';
 
1481
 
      if(password->length > 0){
 
1482
 
        memcpy(send_buffer + 1, password->data, password->length);
 
1485
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
1487
 
      const error_t saved_errno = errno;
 
1488
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
1489
 
      explicit_bzero(send_buffer, send_buffer_length);
 
1491
 
      memset(send_buffer, '\0', send_buffer_length);
 
1493
 
      if(munlock(send_buffer, send_buffer_length) != 0){
 
1494
 
        error(0, errno, "Failed to unlock memory of send buffer");
 
1497
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
1498
 
        switch(saved_errno){
 
1511
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1512
 
                (uintmax_t)password->length);
 
1516
 
          __attribute__((fallthrough));
 
1519
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
1520
 
            error(0, 0, "Password only partially sent to socket");
 
1525
 
          __attribute__((fallthrough));
 
1528
 
          error(0, saved_errno, "Failed to send() to socket %s",
 
1530
 
          if(not string_set_add(cancelled_filenames,
 
1531
 
                                question_filename)){
 
1532
 
            error(0, errno, "Failed to cancel question for file %s",
 
1535
 
          cleanup_task(&task);
 
1540
 
        cleanup_task(&task);
 
1546
 
  /* We failed or are not ready yet; retry later */
 
1548
 
  if(not add_to_queue(queue, task)){
 
1549
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1550
 
          " file %s and socket %s to queue", question_filename,
 
1552
 
    cleanup_task(&task);
 
1555
 
  /* Add the fd to the epoll set */
 
1556
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1557
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1559
 
    error(0, errno, "Failed to add socket file descriptor %d for"
 
1560
 
          " socket %s to epoll set", fd, filename);
 
1561
 
    /* Force the added task (send_password_to_socket) to run again, at
 
1562
 
       most one second from now */
 
1563
 
    if((queue->next_run == 0)
 
1564
 
       or (queue->next_run > (*current_time + 1000000))){
 
1565
 
      queue->next_run = *current_time + 1000000;
 
1570
 
__attribute__((warn_unused_result))
 
1571
 
bool add_existing_questions(task_queue *const queue,
 
1573
 
                            buffer *const password,
 
1574
 
                            string_set *cancelled_filenames,
 
1575
 
                            const mono_microsecs *const current_time,
 
1576
 
                            bool *const mandos_client_exited,
 
1577
 
                            bool *const password_is_read,
 
1578
 
                            const char *const dirname){
 
1579
 
  __attribute__((cleanup(cleanup_string)))
 
1580
 
    char *dir_pattern = NULL;
 
1581
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
1582
 
  if(ret < 0 or dir_pattern == NULL){
 
1583
 
    error(0, errno, "Could not create glob pattern for directory %s",
 
1587
 
  __attribute__((cleanup(globfree)))
 
1588
 
    glob_t question_filenames = {};
 
1589
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
1590
 
              NULL, &question_filenames)){
 
1593
 
    error(0, errno, "Failed to open directory %s", dirname);
 
1596
 
    error(0, errno, "There are no question files in %s", dirname);
 
1599
 
    error(0, errno, "Could not allocate memory for question file"
 
1600
 
          " names in %s", dirname);
 
1604
 
    __attribute__((fallthrough));
 
1607
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
1608
 
      char *const question_filename = strdup(question_filenames
 
1610
 
      const task_context task = {
 
1611
 
        .func=open_and_parse_question,
 
1613
 
        .question_filename=question_filename,
 
1614
 
        .filename=question_filename,
 
1616
 
        .cancelled_filenames=cancelled_filenames,
 
1617
 
        .current_time=current_time,
 
1618
 
        .mandos_client_exited=mandos_client_exited,
 
1619
 
        .password_is_read=password_is_read,
 
1622
 
      if(question_filename == NULL
 
1623
 
         or not add_to_queue(queue, task)){
 
1624
 
        error(0, errno, "Failed to add open_and_parse_question for"
 
1625
 
              " file %s to queue",
 
1626
 
              question_filenames.gl_pathv[i]);
 
1627
 
        free(question_filename);
 
1629
 
        queue->next_run = 1;
 
1636
 
__attribute__((nonnull, warn_unused_result))
 
1637
 
bool wait_for_event(const int epoll_fd,
 
1638
 
                    const mono_microsecs queue_next_run,
 
1639
 
                    const mono_microsecs current_time){
 
1640
 
  __attribute__((const))
 
1641
 
    int milliseconds_to_wait(const mono_microsecs currtime,
 
1642
 
                             const mono_microsecs nextrun){
 
1643
 
    if(currtime >= nextrun){
 
1646
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
1647
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
1650
 
    return (int)wait_time_ms;
 
1653
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
1656
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
1657
 
  sigset_t temporary_unblocked_sigmask;
 
1658
 
  /* Get current signal mask */
 
1659
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
1662
 
  /* Remove SIGCHLD from the signal mask */
 
1663
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
1666
 
  struct epoll_event events[8]; /* Ignored */
 
1667
 
  int ret = epoll_pwait(epoll_fd, events,
 
1668
 
                        sizeof(events) / sizeof(struct epoll_event),
 
1669
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
1670
 
                        &temporary_unblocked_sigmask);
 
1671
 
  if(ret < 0 and errno != EINTR){
 
1672
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
1674
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
1677
 
  return clear_all_fds_from_epoll_set(epoll_fd);
 
1680
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
1681
 
  /* Create a new empty epoll set */
 
1682
 
  __attribute__((cleanup(cleanup_close)))
 
1683
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
1684
 
  if(new_epoll_fd < 0){
 
1687
 
  /* dup3() the new epoll set fd over the old one, replacing it */
 
1688
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
1694
 
__attribute__((nonnull, warn_unused_result))
 
1695
 
bool run_queue(task_queue **const queue,
 
1696
 
               string_set *const cancelled_filenames,
 
1697
 
               bool *const quit_now){
 
1699
 
  task_queue *new_queue = create_queue();
 
1700
 
  if(new_queue == NULL){
 
1704
 
  __attribute__((cleanup(string_set_clear)))
 
1705
 
    string_set old_cancelled_filenames = {};
 
1706
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
1708
 
  /* Declare i outside the for loop, since we might need i after the
 
1709
 
     loop in case we aborted in the middle */
 
1711
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
1712
 
    task_context *const task = &((*queue)->tasks[i]);
 
1713
 
    const char *const question_filename = task->question_filename;
 
1714
 
    /* Skip any task referencing a cancelled question filename */
 
1715
 
    if(question_filename != NULL
 
1716
 
       and string_set_contains(old_cancelled_filenames,
 
1717
 
                               question_filename)){
 
1721
 
    task->func(*task, new_queue);
 
1725
 
    /* we might be in the middle of the queue, so clean up any
 
1726
 
       remaining tasks in the current queue */
 
1727
 
    for(; i < (*queue)->length; i++){
 
1728
 
      cleanup_task(&((*queue)->tasks[i]));
 
1742
 
/* End of regular code section */
 
1744
 
/* Start of tests section; here are the tests for the above code */
 
1746
 
/* This "fixture" data structure is used by the test setup and
 
1747
 
   teardown functions */
 
1749
 
  struct sigaction orig_sigaction;
 
1750
 
  sigset_t orig_sigmask;
 
1753
 
static void test_setup(test_fixture *fixture,
 
1754
 
                       __attribute__((unused))
 
1755
 
                       gconstpointer user_data){
 
1756
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
1757
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
1760
 
static void test_teardown(test_fixture *fixture,
 
1761
 
                          __attribute__((unused))
 
1762
 
                          gconstpointer user_data){
 
1763
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
1764
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
1767
 
/* Utility function used by tests to search queue for matching task */
 
1768
 
__attribute__((pure, nonnull, warn_unused_result))
 
1769
 
static task_context *find_matching_task(const task_queue *const queue,
 
1770
 
                                        const task_context task){
 
1771
 
  /* The argument "task" structure is a pattern to match; 0 in any
 
1772
 
     member means any value matches, otherwise the value must match.
 
1773
 
     The filename strings are compared by strcmp(), not by pointer. */
 
1774
 
  for(size_t i = 0; i < queue->length; i++){
 
1775
 
    task_context *const current_task = queue->tasks+i;
 
1776
 
    /* Check all members of task_context, if set to a non-zero value.
 
1777
 
       If a member does not match, continue to next task in queue */
 
1779
 
    /* task_func *const func */
 
1780
 
    if(task.func != NULL and current_task->func != task.func){
 
1783
 
    /* char *const question_filename; */
 
1784
 
    if(task.question_filename != NULL
 
1785
 
       and (current_task->question_filename == NULL
 
1786
 
            or strcmp(current_task->question_filename,
 
1787
 
                      task.question_filename) != 0)){
 
1790
 
    /* const pid_t pid; */
 
1791
 
    if(task.pid != 0 and current_task->pid != task.pid){
 
1794
 
    /* const int epoll_fd; */
 
1795
 
    if(task.epoll_fd != 0
 
1796
 
       and current_task->epoll_fd != task.epoll_fd){
 
1799
 
    /* bool *const quit_now; */
 
1800
 
    if(task.quit_now != NULL
 
1801
 
       and current_task->quit_now != task.quit_now){
 
1805
 
    if(task.fd != 0 and current_task->fd != task.fd){
 
1808
 
    /* bool *const mandos_client_exited; */
 
1809
 
    if(task.mandos_client_exited != NULL
 
1810
 
       and current_task->mandos_client_exited
 
1811
 
       != task.mandos_client_exited){
 
1814
 
    /* buffer *const password; */
 
1815
 
    if(task.password != NULL
 
1816
 
       and current_task->password != task.password){
 
1819
 
    /* bool *const password_is_read; */
 
1820
 
    if(task.password_is_read != NULL
 
1821
 
       and current_task->password_is_read != task.password_is_read){
 
1824
 
    /* char *filename; */
 
1825
 
    if(task.filename != NULL
 
1826
 
       and (current_task->filename == NULL
 
1827
 
            or strcmp(current_task->filename, task.filename) != 0)){
 
1830
 
    /* string_set *const cancelled_filenames; */
 
1831
 
    if(task.cancelled_filenames != NULL
 
1832
 
       and current_task->cancelled_filenames
 
1833
 
       != task.cancelled_filenames){
 
1836
 
    /* const mono_microsecs notafter; */
 
1837
 
    if(task.notafter != 0
 
1838
 
       and current_task->notafter != task.notafter){
 
1841
 
    /* const mono_microsecs *const current_time; */
 
1842
 
    if(task.current_time != NULL
 
1843
 
       and current_task->current_time != task.current_time){
 
1846
 
    /* Current task matches all members; return it */
 
1847
 
    return current_task;
 
1849
 
  /* No task in queue matches passed pattern task */
 
1853
 
static void test_create_queue(__attribute__((unused))
 
1854
 
                              test_fixture *fixture,
 
1855
 
                              __attribute__((unused))
 
1856
 
                              gconstpointer user_data){
 
1857
 
  __attribute__((cleanup(cleanup_queue)))
 
1858
 
    task_queue *const queue = create_queue();
 
1859
 
  g_assert_nonnull(queue);
 
1860
 
  g_assert_null(queue->tasks);
 
1861
 
  g_assert_true(queue->length == 0);
 
1862
 
  g_assert_true(queue->next_run == 0);
 
1865
 
static task_func dummy_func;
 
1867
 
static void test_add_to_queue(__attribute__((unused))
 
1868
 
                              test_fixture *fixture,
 
1869
 
                              __attribute__((unused))
 
1870
 
                              gconstpointer user_data){
 
1871
 
  __attribute__((cleanup(cleanup_queue)))
 
1872
 
    task_queue *queue = create_queue();
 
1873
 
  g_assert_nonnull(queue);
 
1875
 
  g_assert_true(add_to_queue(queue,
 
1876
 
                             (task_context){ .func=dummy_func }));
 
1877
 
  g_assert_true(queue->length == 1);
 
1878
 
  g_assert_nonnull(queue->tasks);
 
1879
 
  g_assert_true(queue->tasks[0].func == dummy_func);
 
1882
 
static void dummy_func(__attribute__((unused))
 
1883
 
                       const task_context task,
 
1884
 
                       __attribute__((unused))
 
1885
 
                       task_queue *const queue){
 
1888
 
static void test_queue_has_question_empty(__attribute__((unused))
 
1889
 
                                          test_fixture *fixture,
 
1890
 
                                          __attribute__((unused))
 
1891
 
                                          gconstpointer user_data){
 
1892
 
  __attribute__((cleanup(cleanup_queue)))
 
1893
 
    task_queue *queue = create_queue();
 
1894
 
  g_assert_nonnull(queue);
 
1895
 
  g_assert_false(queue_has_question(queue));
 
1898
 
static void test_queue_has_question_false(__attribute__((unused))
 
1899
 
                                          test_fixture *fixture,
 
1900
 
                                          __attribute__((unused))
 
1901
 
                                          gconstpointer user_data){
 
1902
 
  __attribute__((cleanup(cleanup_queue)))
 
1903
 
    task_queue *queue = create_queue();
 
1904
 
  g_assert_nonnull(queue);
 
1905
 
  g_assert_true(add_to_queue(queue,
 
1906
 
                             (task_context){ .func=dummy_func }));
 
1907
 
  g_assert_false(queue_has_question(queue));
 
1910
 
static void test_queue_has_question_true(__attribute__((unused))
 
1911
 
                                         test_fixture *fixture,
 
1912
 
                                         __attribute__((unused))
 
1913
 
                                         gconstpointer user_data){
 
1914
 
  __attribute__((cleanup(cleanup_queue)))
 
1915
 
    task_queue *queue = create_queue();
 
1916
 
  g_assert_nonnull(queue);
 
1917
 
  char *const question_filename
 
1918
 
    = strdup("/nonexistent/question_filename");
 
1919
 
  g_assert_nonnull(question_filename);
 
1920
 
  task_context task = {
 
1922
 
    .question_filename=question_filename,
 
1924
 
  g_assert_true(add_to_queue(queue, task));
 
1925
 
  g_assert_true(queue_has_question(queue));
 
1928
 
static void test_queue_has_question_false2(__attribute__((unused))
 
1929
 
                                           test_fixture *fixture,
 
1930
 
                                           __attribute__((unused))
 
1931
 
                                           gconstpointer user_data){
 
1932
 
  __attribute__((cleanup(cleanup_queue)))
 
1933
 
    task_queue *queue = create_queue();
 
1934
 
  g_assert_nonnull(queue);
 
1935
 
  task_context task = { .func=dummy_func };
 
1936
 
  g_assert_true(add_to_queue(queue, task));
 
1937
 
  g_assert_true(add_to_queue(queue, task));
 
1938
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1939
 
  g_assert_false(queue_has_question(queue));
 
1942
 
static void test_queue_has_question_true2(__attribute__((unused))
 
1943
 
                                          test_fixture *fixture,
 
1944
 
                                          __attribute__((unused))
 
1945
 
                                          gconstpointer user_data){
 
1946
 
  __attribute__((cleanup(cleanup_queue)))
 
1947
 
    task_queue *queue = create_queue();
 
1948
 
  g_assert_nonnull(queue);
 
1949
 
  task_context task1 = { .func=dummy_func };
 
1950
 
  g_assert_true(add_to_queue(queue, task1));
 
1951
 
  char *const question_filename
 
1952
 
    = strdup("/nonexistent/question_filename");
 
1953
 
  g_assert_nonnull(question_filename);
 
1954
 
  task_context task2 = {
 
1956
 
    .question_filename=question_filename,
 
1958
 
  g_assert_true(add_to_queue(queue, task2));
 
1959
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1960
 
  g_assert_true(queue_has_question(queue));
 
1963
 
static void test_cleanup_buffer(__attribute__((unused))
 
1964
 
                                test_fixture *fixture,
 
1965
 
                                __attribute__((unused))
 
1966
 
                                gconstpointer user_data){
 
1969
 
  const size_t buffersize = 10;
 
1971
 
  buf.data = malloc(buffersize);
 
1972
 
  g_assert_nonnull(buf.data);
 
1973
 
  if(mlock(buf.data, buffersize) != 0){
 
1974
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
1977
 
  cleanup_buffer(&buf);
 
1978
 
  g_assert_null(buf.data);
 
1982
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
1983
 
                                              test_fixture *fixture,
 
1984
 
                                              __attribute__((unused))
 
1987
 
  __attribute__((cleanup(string_set_clear)))
 
1988
 
    string_set set = {};
 
1989
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
1990
 
  g_assert_false(string_set_contains(set, "test_string"));
 
1994
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
1995
 
                                              test_fixture *fixture,
 
1996
 
                                              __attribute__((unused))
 
1999
 
  __attribute__((cleanup(string_set_clear)))
 
2000
 
    string_set set = {};
 
2001
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2002
 
  g_assert_true(string_set_contains(set, "test_string"));
 
2006
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
2007
 
                                             test_fixture *fixture,
 
2008
 
                                             __attribute__((unused))
 
2009
 
                                             gconstpointer user_data){
 
2010
 
  __attribute__((cleanup(string_set_clear)))
 
2011
 
    string_set set = {};
 
2012
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2013
 
  string_set_clear(&set);
 
2014
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2018
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
2019
 
                                         test_fixture *fixture,
 
2020
 
                                         __attribute__((unused))
 
2021
 
                                         gconstpointer user_data){
 
2022
 
  __attribute__((cleanup(string_set_clear)))
 
2023
 
    string_set set1 = {};
 
2024
 
  __attribute__((cleanup(string_set_clear)))
 
2025
 
    string_set set2 = {};
 
2026
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2027
 
  string_set_swap(&set1, &set2);
 
2028
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2029
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2033
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
2034
 
                                         test_fixture *fixture,
 
2035
 
                                         __attribute__((unused))
 
2036
 
                                         gconstpointer user_data){
 
2037
 
  __attribute__((cleanup(string_set_clear)))
 
2038
 
    string_set set1 = {};
 
2039
 
  __attribute__((cleanup(string_set_clear)))
 
2040
 
    string_set set2 = {};
 
2041
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2042
 
  string_set_swap(&set1, &set2);
 
2043
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2044
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2047
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
2048
 
                                              test_fixture *fixture,
 
2049
 
                                              __attribute__((unused))
 
2052
 
  __attribute__((cleanup(string_set_clear)))
 
2053
 
    string_set set1 = {};
 
2054
 
  __attribute__((cleanup(string_set_clear)))
 
2055
 
    string_set set2 = {};
 
2056
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2057
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2058
 
  string_set_swap(&set1, &set2);
 
2059
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2060
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2061
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2062
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2065
 
static bool fd_has_cloexec_and_nonblock(const int);
 
2067
 
static bool epoll_set_contains(int, int, uint32_t);
 
2069
 
static void test_start_mandos_client(test_fixture *fixture,
 
2070
 
                                     __attribute__((unused))
 
2071
 
                                     gconstpointer user_data){
 
2073
 
  bool mandos_client_exited = false;
 
2074
 
  bool quit_now = false;
 
2075
 
  __attribute__((cleanup(cleanup_close)))
 
2076
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2077
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2078
 
  __attribute__((cleanup(cleanup_queue)))
 
2079
 
    task_queue *queue = create_queue();
 
2080
 
  g_assert_nonnull(queue);
 
2081
 
  buffer password = {};
 
2082
 
  bool password_is_read = false;
 
2083
 
  const char helper_directory[] = "/nonexistent";
 
2084
 
  const char *const argv[] = { "/bin/true", NULL };
 
2086
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2087
 
                                    &mandos_client_exited, &quit_now,
 
2088
 
                                    &password, &password_is_read,
 
2089
 
                                    &fixture->orig_sigaction,
 
2090
 
                                    fixture->orig_sigmask,
 
2091
 
                                    helper_directory, 0, 0, argv));
 
2093
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
2095
 
  const task_context *const added_wait_task
 
2096
 
    = find_matching_task(queue, (task_context){
 
2097
 
        .func=wait_for_mandos_client_exit,
 
2098
 
        .mandos_client_exited=&mandos_client_exited,
 
2099
 
        .quit_now=&quit_now,
 
2101
 
  g_assert_nonnull(added_wait_task);
 
2102
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
2103
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
2104
 
  waitpid(added_wait_task->pid, NULL, 0);
 
2106
 
  const task_context *const added_read_task
 
2107
 
    = find_matching_task(queue, (task_context){
 
2108
 
        .func=read_mandos_client_output,
 
2110
 
        .password=&password,
 
2111
 
        .password_is_read=&password_is_read,
 
2112
 
        .quit_now=&quit_now,
 
2114
 
  g_assert_nonnull(added_read_task);
 
2115
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
2116
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
2117
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
2118
 
                                   EPOLLIN | EPOLLRDHUP));
 
2121
 
static bool fd_has_cloexec_and_nonblock(const int fd){
 
2122
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
2123
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
2124
 
  return ((socket_fd_flags >= 0)
 
2125
 
          and (socket_fd_flags & FD_CLOEXEC)
 
2126
 
          and (socket_file_flags >= 0)
 
2127
 
          and (socket_file_flags & O_NONBLOCK));
 
2130
 
__attribute__((const))
 
2131
 
bool is_privileged(void){
 
2132
 
  uid_t user = getuid() + 1;
 
2133
 
  if(user == 0){                /* Overflow check */
 
2136
 
  gid_t group = getuid() + 1;
 
2137
 
  if(group == 0){               /* Overflow check */
 
2140
 
  const pid_t pid = fork();
 
2141
 
  if(pid == 0){                 /* Child */
 
2142
 
    if(setresgid((uid_t)-1, group, group) == -1){
 
2144
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
2145
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
2149
 
    if(setresuid((uid_t)-1, user, user) == -1){
 
2151
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
2152
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
2159
 
  waitpid(pid, &status, 0);
 
2160
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
2166
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
2167
 
  /* Only scan for events in this eventmask */
 
2168
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
2169
 
  __attribute__((cleanup(cleanup_string)))
 
2170
 
    char *fdinfo_name = NULL;
 
2171
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
2172
 
  g_assert_cmpint(ret, >, 0);
 
2173
 
  g_assert_nonnull(fdinfo_name);
 
2175
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
2176
 
  g_assert_nonnull(fdinfo);
 
2177
 
  uint32_t reported_events;
 
2182
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
2185
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
2186
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
2187
 
              &found_fd, &reported_events) == 2){
 
2192
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
2193
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
2200
 
    /* Don't check events if none are given */
 
2203
 
  return (reported_events & eventmask) == (events & eventmask);
 
2206
 
static void test_start_mandos_client_execv(test_fixture *fixture,
 
2207
 
                                           __attribute__((unused))
 
2208
 
                                           gconstpointer user_data){
 
2209
 
  bool mandos_client_exited = false;
 
2210
 
  bool quit_now = false;
 
2211
 
  __attribute__((cleanup(cleanup_close)))
 
2212
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2213
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2214
 
  __attribute__((cleanup(cleanup_queue)))
 
2215
 
    task_queue *queue = create_queue();
 
2216
 
  g_assert_nonnull(queue);
 
2217
 
  __attribute__((cleanup(cleanup_buffer)))
 
2218
 
    buffer password = {};
 
2219
 
  const char helper_directory[] = "/nonexistent";
 
2220
 
  /* Can't execv("/", ...), so this should fail */
 
2221
 
  const char *const argv[] = { "/", NULL };
 
2224
 
    __attribute__((cleanup(cleanup_close)))
 
2225
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
2226
 
    g_assert_cmpint(devnull_fd, >=, 0);
 
2227
 
    __attribute__((cleanup(cleanup_close)))
 
2228
 
      const int real_stderr_fd = dup(STDERR_FILENO);
 
2229
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
2230
 
    dup2(devnull_fd, STDERR_FILENO);
 
2232
 
    const bool success = start_mandos_client(queue, epoll_fd,
 
2233
 
                                             &mandos_client_exited,
 
2237
 
                                             &fixture->orig_sigaction,
 
2238
 
                                             fixture->orig_sigmask,
 
2239
 
                                             helper_directory, 0, 0,
 
2241
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2242
 
    g_assert_true(success);
 
2244
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
2246
 
  struct timespec starttime, currtime;
 
2247
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2249
 
    queue->next_run = 0;
 
2250
 
    string_set cancelled_filenames = {};
 
2253
 
      __attribute__((cleanup(cleanup_close)))
 
2254
 
        const int devnull_fd = open("/dev/null",
 
2255
 
                                    O_WRONLY | O_CLOEXEC);
 
2256
 
      g_assert_cmpint(devnull_fd, >=, 0);
 
2257
 
      __attribute__((cleanup(cleanup_close)))
 
2258
 
        const int real_stderr_fd = dup(STDERR_FILENO);
 
2259
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
2260
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2261
 
      dup2(devnull_fd, STDERR_FILENO);
 
2262
 
      const bool success = run_queue(&queue, &cancelled_filenames,
 
2264
 
      dup2(real_stderr_fd, STDERR_FILENO);
 
2269
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2270
 
  } while(((queue->length) > 0)
 
2272
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2274
 
  g_assert_true(quit_now);
 
2275
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2276
 
  g_assert_true(mandos_client_exited);
 
2279
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
2280
 
                                               __attribute__((unused))
 
2283
 
  if(not is_privileged()){
 
2284
 
    g_test_skip("Not privileged");
 
2288
 
  bool mandos_client_exited = false;
 
2289
 
  bool quit_now = false;
 
2290
 
  __attribute__((cleanup(cleanup_close)))
 
2291
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2292
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2293
 
  __attribute__((cleanup(cleanup_queue)))
 
2294
 
    task_queue *queue = create_queue();
 
2295
 
  g_assert_nonnull(queue);
 
2296
 
  __attribute__((cleanup(cleanup_buffer)))
 
2297
 
    buffer password = {};
 
2298
 
  bool password_is_read = false;
 
2299
 
  const char helper_directory[] = "/nonexistent";
 
2300
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
2304
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2305
 
                                           &mandos_client_exited,
 
2306
 
                                           &quit_now, &password,
 
2308
 
                                           &fixture->orig_sigaction,
 
2309
 
                                           fixture->orig_sigmask,
 
2310
 
                                           helper_directory, user,
 
2312
 
  g_assert_true(success);
 
2313
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2315
 
  struct timespec starttime, currtime;
 
2316
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2318
 
    queue->next_run = 0;
 
2319
 
    string_set cancelled_filenames = {};
 
2320
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2321
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2322
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2323
 
  } while(((queue->length) > 0)
 
2325
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2327
 
  g_assert_false(quit_now);
 
2328
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2329
 
  g_assert_true(mandos_client_exited);
 
2331
 
  g_assert_true(password_is_read);
 
2332
 
  g_assert_nonnull(password.data);
 
2335
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2337
 
  g_assert_true((uid_t)id == id);
 
2339
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2342
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
2343
 
                                               __attribute__((unused))
 
2346
 
  if(not is_privileged()){
 
2347
 
    g_test_skip("Not privileged");
 
2351
 
  bool mandos_client_exited = false;
 
2352
 
  bool quit_now = false;
 
2353
 
  __attribute__((cleanup(cleanup_close)))
 
2354
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2355
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2356
 
  __attribute__((cleanup(cleanup_queue)))
 
2357
 
    task_queue *queue = create_queue();
 
2358
 
  g_assert_nonnull(queue);
 
2359
 
  __attribute__((cleanup(cleanup_buffer)))
 
2360
 
    buffer password = {};
 
2361
 
  bool password_is_read = false;
 
2362
 
  const char helper_directory[] = "/nonexistent";
 
2363
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
2367
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2368
 
                                           &mandos_client_exited,
 
2369
 
                                           &quit_now, &password,
 
2371
 
                                           &fixture->orig_sigaction,
 
2372
 
                                           fixture->orig_sigmask,
 
2373
 
                                           helper_directory, user,
 
2375
 
  g_assert_true(success);
 
2376
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2378
 
  struct timespec starttime, currtime;
 
2379
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2381
 
    queue->next_run = 0;
 
2382
 
    string_set cancelled_filenames = {};
 
2383
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2384
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2385
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2386
 
  } while(((queue->length) > 0)
 
2388
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2390
 
  g_assert_false(quit_now);
 
2391
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2392
 
  g_assert_true(mandos_client_exited);
 
2394
 
  g_assert_true(password_is_read);
 
2395
 
  g_assert_nonnull(password.data);
 
2398
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2400
 
  g_assert_true((gid_t)id == id);
 
2402
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2405
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
2406
 
                                               __attribute__((unused))
 
2409
 
  if(not is_privileged()){
 
2410
 
    g_test_skip("Not privileged");
 
2414
 
  bool mandos_client_exited = false;
 
2415
 
  bool quit_now = false;
 
2416
 
  __attribute__((cleanup(cleanup_close)))
 
2417
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2418
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2419
 
  __attribute__((cleanup(cleanup_queue)))
 
2420
 
    task_queue *queue = create_queue();
 
2421
 
  g_assert_nonnull(queue);
 
2422
 
  __attribute__((cleanup(cleanup_buffer)))
 
2423
 
    buffer password = {};
 
2424
 
  bool password_is_read = false;
 
2425
 
  const char helper_directory[] = "/nonexistent";
 
2426
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
2431
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2432
 
                                           &mandos_client_exited,
 
2433
 
                                           &quit_now, &password,
 
2435
 
                                           &fixture->orig_sigaction,
 
2436
 
                                           fixture->orig_sigmask,
 
2437
 
                                           helper_directory, user,
 
2439
 
  g_assert_true(success);
 
2440
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2442
 
  struct timespec starttime, currtime;
 
2443
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2445
 
    queue->next_run = 0;
 
2446
 
    string_set cancelled_filenames = {};
 
2447
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2448
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2449
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2450
 
  } while(((queue->length) > 0)
 
2452
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2454
 
  g_assert_false(quit_now);
 
2455
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2456
 
  g_assert_true(mandos_client_exited);
 
2458
 
  g_assert_true(password_is_read);
 
2459
 
  g_assert_nonnull(password.data);
 
2462
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2464
 
  g_assert_true((uid_t)id == id);
 
2466
 
  g_assert_cmpuint((unsigned int)id, ==, user);
 
2469
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
2470
 
                                               __attribute__((unused))
 
2473
 
  if(not is_privileged()){
 
2474
 
    g_test_skip("Not privileged");
 
2478
 
  bool mandos_client_exited = false;
 
2479
 
  bool quit_now = false;
 
2480
 
  __attribute__((cleanup(cleanup_close)))
 
2481
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2482
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2483
 
  __attribute__((cleanup(cleanup_queue)))
 
2484
 
    task_queue *queue = create_queue();
 
2485
 
  g_assert_nonnull(queue);
 
2486
 
  __attribute__((cleanup(cleanup_buffer)))
 
2487
 
    buffer password = {};
 
2488
 
  bool password_is_read = false;
 
2489
 
  const char helper_directory[] = "/nonexistent";
 
2490
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
2495
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2496
 
                                           &mandos_client_exited,
 
2497
 
                                           &quit_now, &password,
 
2499
 
                                           &fixture->orig_sigaction,
 
2500
 
                                           fixture->orig_sigmask,
 
2501
 
                                           helper_directory, user,
 
2503
 
  g_assert_true(success);
 
2504
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2506
 
  struct timespec starttime, currtime;
 
2507
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2509
 
    queue->next_run = 0;
 
2510
 
    string_set cancelled_filenames = {};
 
2511
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2512
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2513
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2514
 
  } while(((queue->length) > 0)
 
2516
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2518
 
  g_assert_false(quit_now);
 
2519
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2520
 
  g_assert_true(mandos_client_exited);
 
2522
 
  g_assert_true(password_is_read);
 
2523
 
  g_assert_nonnull(password.data);
 
2526
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2528
 
  g_assert_true((gid_t)id == id);
 
2530
 
  g_assert_cmpuint((unsigned int)id, ==, group);
 
2533
 
static void test_start_mandos_client_read(test_fixture *fixture,
 
2534
 
                                          __attribute__((unused))
 
2535
 
                                          gconstpointer user_data){
 
2536
 
  bool mandos_client_exited = false;
 
2537
 
  bool quit_now = false;
 
2538
 
  __attribute__((cleanup(cleanup_close)))
 
2539
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2540
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2541
 
  __attribute__((cleanup(cleanup_queue)))
 
2542
 
    task_queue *queue = create_queue();
 
2543
 
  g_assert_nonnull(queue);
 
2544
 
  __attribute__((cleanup(cleanup_buffer)))
 
2545
 
    buffer password = {};
 
2546
 
  bool password_is_read = false;
 
2547
 
  const char dummy_test_password[] = "dummy test password";
 
2548
 
  const char helper_directory[] = "/nonexistent";
 
2549
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
2552
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2553
 
                                           &mandos_client_exited,
 
2554
 
                                           &quit_now, &password,
 
2556
 
                                           &fixture->orig_sigaction,
 
2557
 
                                           fixture->orig_sigmask,
 
2558
 
                                           helper_directory, 0, 0,
 
2560
 
  g_assert_true(success);
 
2561
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2563
 
  struct timespec starttime, currtime;
 
2564
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2566
 
    queue->next_run = 0;
 
2567
 
    string_set cancelled_filenames = {};
 
2568
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2569
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2570
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2571
 
  } while(((queue->length) > 0)
 
2573
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2575
 
  g_assert_false(quit_now);
 
2576
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2577
 
  g_assert_true(mandos_client_exited);
 
2579
 
  g_assert_true(password_is_read);
 
2580
 
  g_assert_cmpint((int)password.length, ==,
 
2581
 
                  sizeof(dummy_test_password)-1);
 
2582
 
  g_assert_nonnull(password.data);
 
2583
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
2584
 
                         sizeof(dummy_test_password)-1), ==, 0);
 
2588
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
2589
 
                                               __attribute__((unused))
 
2592
 
  bool mandos_client_exited = false;
 
2593
 
  bool quit_now = false;
 
2594
 
  __attribute__((cleanup(cleanup_close)))
 
2595
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2596
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2597
 
  __attribute__((cleanup(cleanup_queue)))
 
2598
 
    task_queue *queue = create_queue();
 
2599
 
  g_assert_nonnull(queue);
 
2600
 
  __attribute__((cleanup(cleanup_buffer)))
 
2601
 
    buffer password = {};
 
2602
 
  bool password_is_read = false;
 
2603
 
  const char helper_directory[] = "/nonexistent";
 
2604
 
  const char *const argv[] = { "/bin/sh", "-c",
 
2605
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
2607
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2608
 
                                           &mandos_client_exited,
 
2609
 
                                           &quit_now, &password,
 
2611
 
                                           &fixture->orig_sigaction,
 
2612
 
                                           fixture->orig_sigmask,
 
2613
 
                                           helper_directory, 0, 0,
 
2615
 
  g_assert_true(success);
 
2616
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2618
 
  struct timespec starttime, currtime;
 
2619
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2621
 
    queue->next_run = 0;
 
2622
 
    string_set cancelled_filenames = {};
 
2623
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2624
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2625
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2626
 
  } while(((queue->length) > 0)
 
2628
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2630
 
  g_assert_false(quit_now);
 
2631
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2632
 
  g_assert_true(mandos_client_exited);
 
2634
 
  g_assert_true(password_is_read);
 
2635
 
  g_assert_cmpint((int)password.length, ==,
 
2636
 
                  sizeof(helper_directory)-1);
 
2637
 
  g_assert_nonnull(password.data);
 
2638
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
2639
 
                         sizeof(helper_directory)-1), ==, 0);
 
2642
 
__attribute__((nonnull, warn_unused_result))
 
2643
 
static bool proc_status_sigblk_to_sigset(const char *const,
 
2646
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
2647
 
                                             __attribute__((unused))
 
2648
 
                                             gconstpointer user_data){
 
2649
 
  bool mandos_client_exited = false;
 
2650
 
  bool quit_now = false;
 
2651
 
  __attribute__((cleanup(cleanup_close)))
 
2652
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2653
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2654
 
  __attribute__((cleanup(cleanup_queue)))
 
2655
 
    task_queue *queue = create_queue();
 
2656
 
  g_assert_nonnull(queue);
 
2657
 
  __attribute__((cleanup(cleanup_buffer)))
 
2658
 
    buffer password = {};
 
2659
 
  bool password_is_read = false;
 
2660
 
  const char helper_directory[] = "/nonexistent";
 
2661
 
  /* see proc(5) for format of /proc/self/status */
 
2662
 
  const char *const argv[] = { "/usr/bin/awk",
 
2663
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
2665
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2666
 
                                    &mandos_client_exited, &quit_now,
 
2667
 
                                    &password, &password_is_read,
 
2668
 
                                    &fixture->orig_sigaction,
 
2669
 
                                    fixture->orig_sigmask,
 
2670
 
                                    helper_directory, 0, 0, argv));
 
2672
 
  struct timespec starttime, currtime;
 
2673
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2675
 
    queue->next_run = 0;
 
2676
 
    string_set cancelled_filenames = {};
 
2677
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2678
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2679
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2680
 
  } while((not (mandos_client_exited and password_is_read))
 
2682
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2683
 
  g_assert_true(mandos_client_exited);
 
2684
 
  g_assert_true(password_is_read);
 
2686
 
  sigset_t parsed_sigmask;
 
2687
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
2690
 
  for(int signum = 1; signum < NSIG; signum++){
 
2691
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
2692
 
    if(sigismember(&fixture->orig_sigmask, signum)){
 
2693
 
      g_assert_true(has_signal);
 
2695
 
      g_assert_false(has_signal);
 
2700
 
__attribute__((nonnull, warn_unused_result))
 
2701
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
2702
 
                                         sigset_t *const sigmask){
 
2703
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
2704
 
  uintmax_t scanned_sigmask;
 
2705
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
2708
 
  if(sigemptyset(sigmask) != 0){
 
2711
 
  for(int signum = 1; signum < NSIG; signum++){
 
2712
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
2713
 
      if(sigaddset(sigmask, signum) != 0){
 
2721
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2722
 
                                             task_queue *const queue);
 
2725
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
2726
 
                                             test_fixture *fixture,
 
2727
 
                                             __attribute__((unused))
 
2728
 
                                             gconstpointer user_data){
 
2730
 
  bool mandos_client_exited = false;
 
2731
 
  bool quit_now = false;
 
2733
 
  __attribute__((cleanup(cleanup_queue)))
 
2734
 
    task_queue *queue = create_queue();
 
2735
 
  g_assert_nonnull(queue);
 
2736
 
  const task_context task = {
 
2737
 
    .func=wait_for_mandos_client_exit,
 
2739
 
    .mandos_client_exited=&mandos_client_exited,
 
2740
 
    .quit_now=&quit_now,
 
2742
 
  run_task_with_stderr_to_dev_null(task, queue);
 
2744
 
  g_assert_false(mandos_client_exited);
 
2745
 
  g_assert_true(quit_now);
 
2746
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2749
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2750
 
                                             task_queue *const queue){
 
2751
 
  FILE *real_stderr = stderr;
 
2752
 
  FILE *devnull = fopen("/dev/null", "we");
 
2753
 
  g_assert_nonnull(devnull);
 
2756
 
  task.func(task, queue);
 
2757
 
  stderr = real_stderr;
 
2759
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
2763
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
2764
 
                                             __attribute__((unused))
 
2765
 
                                             gconstpointer user_data){
 
2766
 
  bool mandos_client_exited = false;
 
2767
 
  bool quit_now = false;
 
2769
 
  pid_t create_eternal_process(void){
 
2770
 
    const pid_t pid = fork();
 
2771
 
    if(pid == 0){               /* Child */
 
2772
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2773
 
        _exit(EXIT_FAILURE);
 
2775
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2776
 
        _exit(EXIT_FAILURE);
 
2784
 
  pid_t pid = create_eternal_process();
 
2785
 
  g_assert_true(pid != -1);
 
2787
 
  __attribute__((cleanup(cleanup_queue)))
 
2788
 
    task_queue *queue = create_queue();
 
2789
 
  g_assert_nonnull(queue);
 
2790
 
  const task_context task = {
 
2791
 
    .func=wait_for_mandos_client_exit,
 
2793
 
    .mandos_client_exited=&mandos_client_exited,
 
2794
 
    .quit_now=&quit_now,
 
2796
 
  task.func(task, queue);
 
2798
 
  g_assert_false(mandos_client_exited);
 
2799
 
  g_assert_false(quit_now);
 
2800
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
2802
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
2803
 
        .func=wait_for_mandos_client_exit,
 
2805
 
        .mandos_client_exited=&mandos_client_exited,
 
2806
 
        .quit_now=&quit_now,
 
2811
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
2812
 
                                              __attribute__((unused))
 
2815
 
  bool mandos_client_exited = false;
 
2816
 
  bool quit_now = false;
 
2818
 
  pid_t create_successful_process(void){
 
2819
 
    const pid_t pid = fork();
 
2820
 
    if(pid == 0){               /* Child */
 
2821
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2822
 
        _exit(EXIT_FAILURE);
 
2824
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2825
 
        _exit(EXIT_FAILURE);
 
2831
 
  const pid_t pid = create_successful_process();
 
2832
 
  g_assert_true(pid != -1);
 
2834
 
  __attribute__((cleanup(cleanup_queue)))
 
2835
 
    task_queue *queue = create_queue();
 
2836
 
  g_assert_nonnull(queue);
 
2837
 
  const task_context initial_task = {
 
2838
 
    .func=wait_for_mandos_client_exit,
 
2840
 
    .mandos_client_exited=&mandos_client_exited,
 
2841
 
    .quit_now=&quit_now,
 
2843
 
  g_assert_true(add_to_queue(queue, initial_task));
 
2845
 
  struct timespec starttime, currtime;
 
2846
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2847
 
  __attribute__((cleanup(cleanup_close)))
 
2848
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2850
 
    queue->next_run = 0;
 
2851
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2852
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
2853
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2854
 
  } while((not mandos_client_exited)
 
2856
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2858
 
  g_assert_true(mandos_client_exited);
 
2859
 
  g_assert_false(quit_now);
 
2860
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2864
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
2865
 
                                              __attribute__((unused))
 
2868
 
  bool mandos_client_exited = false;
 
2869
 
  bool quit_now = false;
 
2871
 
  pid_t create_failing_process(void){
 
2872
 
    const pid_t pid = fork();
 
2873
 
    if(pid == 0){               /* Child */
 
2874
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2875
 
        _exit(EXIT_FAILURE);
 
2877
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2878
 
        _exit(EXIT_FAILURE);
 
2884
 
  const pid_t pid = create_failing_process();
 
2885
 
  g_assert_true(pid != -1);
 
2887
 
  __attribute__((cleanup(string_set_clear)))
 
2888
 
    string_set cancelled_filenames = {};
 
2889
 
  __attribute__((cleanup(cleanup_close)))
 
2890
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2891
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2892
 
  __attribute__((cleanup(cleanup_queue)))
 
2893
 
    task_queue *queue = create_queue();
 
2894
 
  g_assert_nonnull(queue);
 
2895
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2896
 
        .func=wait_for_mandos_client_exit,
 
2898
 
        .mandos_client_exited=&mandos_client_exited,
 
2899
 
        .quit_now=&quit_now,
 
2902
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
2904
 
  __attribute__((cleanup(cleanup_close)))
 
2905
 
    const int devnull_fd = open("/dev/null",
 
2906
 
                                O_WRONLY | O_CLOEXEC);
 
2907
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2908
 
  __attribute__((cleanup(cleanup_close)))
 
2909
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2910
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2912
 
  struct timespec starttime, currtime;
 
2913
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2915
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2916
 
    dup2(devnull_fd, STDERR_FILENO);
 
2917
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2919
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2924
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2925
 
  } while((not mandos_client_exited)
 
2927
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2929
 
  g_assert_true(quit_now);
 
2930
 
  g_assert_true(mandos_client_exited);
 
2931
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2935
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
2936
 
                                             __attribute__((unused))
 
2937
 
                                             gconstpointer user_data){
 
2938
 
  bool mandos_client_exited = false;
 
2939
 
  bool quit_now = false;
 
2941
 
  pid_t create_killed_process(void){
 
2942
 
    const pid_t pid = fork();
 
2943
 
    if(pid == 0){               /* Child */
 
2944
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2945
 
        _exit(EXIT_FAILURE);
 
2947
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2948
 
        _exit(EXIT_FAILURE);
 
2957
 
  const pid_t pid = create_killed_process();
 
2958
 
  g_assert_true(pid != -1);
 
2960
 
  __attribute__((cleanup(string_set_clear)))
 
2961
 
    string_set cancelled_filenames = {};
 
2962
 
  __attribute__((cleanup(cleanup_close)))
 
2963
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2964
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2965
 
  __attribute__((cleanup(cleanup_queue)))
 
2966
 
    task_queue *queue = create_queue();
 
2967
 
  g_assert_nonnull(queue);
 
2968
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2969
 
        .func=wait_for_mandos_client_exit,
 
2971
 
        .mandos_client_exited=&mandos_client_exited,
 
2972
 
        .quit_now=&quit_now,
 
2975
 
  __attribute__((cleanup(cleanup_close)))
 
2976
 
    const int devnull_fd = open("/dev/null",
 
2977
 
                                O_WRONLY | O_CLOEXEC);
 
2978
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2979
 
  __attribute__((cleanup(cleanup_close)))
 
2980
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2981
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2983
 
  struct timespec starttime, currtime;
 
2984
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2986
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2987
 
    dup2(devnull_fd, STDERR_FILENO);
 
2988
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2990
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2995
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2996
 
  } while((not mandos_client_exited)
 
2998
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
3000
 
  g_assert_true(mandos_client_exited);
 
3001
 
  g_assert_true(quit_now);
 
3002
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3005
 
static bool epoll_set_does_not_contain(int, int);
 
3008
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
3009
 
                                              test_fixture *fixture,
 
3010
 
                                              __attribute__((unused))
 
3013
 
  __attribute__((cleanup(cleanup_close)))
 
3014
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3015
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3017
 
  __attribute__((cleanup(cleanup_buffer)))
 
3018
 
    buffer password = {};
 
3020
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
3021
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3023
 
  bool password_is_read = false;
 
3024
 
  bool quit_now = false;
 
3025
 
  __attribute__((cleanup(cleanup_queue)))
 
3026
 
    task_queue *queue = create_queue();
 
3027
 
  g_assert_nonnull(queue);
 
3029
 
  task_context task = {
 
3030
 
    .func=read_mandos_client_output,
 
3033
 
    .password=&password,
 
3034
 
    .password_is_read=&password_is_read,
 
3035
 
    .quit_now=&quit_now,
 
3037
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3038
 
  g_assert_false(password_is_read);
 
3039
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3040
 
  g_assert_true(quit_now);
 
3041
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3043
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3045
 
  g_assert_cmpint(close(fd), ==, -1);
 
3048
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
3049
 
  return not epoll_set_contains(epoll_fd, fd, 0);
 
3053
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
3054
 
                                           test_fixture *fixture,
 
3055
 
                                           __attribute__((unused))
 
3056
 
                                           gconstpointer user_data){
 
3057
 
  __attribute__((cleanup(cleanup_close)))
 
3058
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3059
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3062
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3064
 
  __attribute__((cleanup(cleanup_buffer)))
 
3065
 
    buffer password = {};
 
3067
 
  bool password_is_read = false;
 
3068
 
  bool quit_now = false;
 
3069
 
  __attribute__((cleanup(cleanup_queue)))
 
3070
 
    task_queue *queue = create_queue();
 
3071
 
  g_assert_nonnull(queue);
 
3073
 
  task_context task = {
 
3074
 
    .func=read_mandos_client_output,
 
3077
 
    .password=&password,
 
3078
 
    .password_is_read=&password_is_read,
 
3079
 
    .quit_now=&quit_now,
 
3081
 
  task.func(task, queue);
 
3082
 
  g_assert_false(password_is_read);
 
3083
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3084
 
  g_assert_false(quit_now);
 
3085
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3087
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3088
 
        .func=read_mandos_client_output,
 
3091
 
        .password=&password,
 
3092
 
        .password_is_read=&password_is_read,
 
3093
 
        .quit_now=&quit_now,
 
3096
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3097
 
                                   EPOLLIN | EPOLLRDHUP));
 
3099
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3102
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
3103
 
                                               test_fixture *fixture,
 
3104
 
                                               __attribute__((unused))
 
3107
 
  __attribute__((cleanup(cleanup_close)))
 
3108
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3109
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3112
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3113
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3115
 
  __attribute__((cleanup(cleanup_buffer)))
 
3116
 
    buffer password = {};
 
3118
 
  bool password_is_read = false;
 
3119
 
  bool quit_now = false;
 
3120
 
  __attribute__((cleanup(cleanup_queue)))
 
3121
 
    task_queue *queue = create_queue();
 
3122
 
  g_assert_nonnull(queue);
 
3124
 
  task_context task = {
 
3125
 
    .func=read_mandos_client_output,
 
3128
 
    .password=&password,
 
3129
 
    .password_is_read=&password_is_read,
 
3130
 
    .quit_now=&quit_now,
 
3132
 
  task.func(task, queue);
 
3133
 
  g_assert_true(password_is_read);
 
3134
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3135
 
  g_assert_false(quit_now);
 
3136
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3138
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3140
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3144
 
void test_read_mandos_client_output_once(__attribute__((unused))
 
3145
 
                                         test_fixture *fixture,
 
3146
 
                                         __attribute__((unused))
 
3147
 
                                         gconstpointer user_data){
 
3148
 
  __attribute__((cleanup(cleanup_close)))
 
3149
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3150
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3153
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3155
 
  const char dummy_test_password[] = "dummy test password";
 
3156
 
  /* Start with a pre-allocated buffer */
 
3157
 
  __attribute__((cleanup(cleanup_buffer)))
 
3159
 
    .data=malloc(sizeof(dummy_test_password)),
 
3161
 
    .allocated=sizeof(dummy_test_password),
 
3163
 
  g_assert_nonnull(password.data);
 
3164
 
  if(mlock(password.data, password.allocated) != 0){
 
3165
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3168
 
  bool password_is_read = false;
 
3169
 
  bool quit_now = false;
 
3170
 
  __attribute__((cleanup(cleanup_queue)))
 
3171
 
    task_queue *queue = create_queue();
 
3172
 
  g_assert_nonnull(queue);
 
3174
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3175
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3176
 
                             sizeof(dummy_test_password)),
 
3177
 
                  ==, (int)sizeof(dummy_test_password));
 
3179
 
  task_context task = {
 
3180
 
    .func=read_mandos_client_output,
 
3183
 
    .password=&password,
 
3184
 
    .password_is_read=&password_is_read,
 
3185
 
    .quit_now=&quit_now,
 
3187
 
  task.func(task, queue);
 
3189
 
  g_assert_false(password_is_read);
 
3190
 
  g_assert_cmpint((int)password.length, ==,
 
3191
 
                  (int)sizeof(dummy_test_password));
 
3192
 
  g_assert_nonnull(password.data);
 
3193
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3194
 
                         sizeof(dummy_test_password)), ==, 0);
 
3196
 
  g_assert_false(quit_now);
 
3197
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3199
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3200
 
        .func=read_mandos_client_output,
 
3203
 
        .password=&password,
 
3204
 
        .password_is_read=&password_is_read,
 
3205
 
        .quit_now=&quit_now,
 
3208
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3209
 
                                   EPOLLIN | EPOLLRDHUP));
 
3211
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3215
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
3216
 
                                           test_fixture *fixture,
 
3217
 
                                           __attribute__((unused))
 
3218
 
                                           gconstpointer user_data){
 
3219
 
  __attribute__((cleanup(cleanup_close)))
 
3220
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3221
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3224
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3226
 
  const char dummy_test_password[] = "dummy test password";
 
3227
 
  /* Start with an empty buffer */
 
3228
 
  __attribute__((cleanup(cleanup_buffer)))
 
3229
 
    buffer password = {};
 
3231
 
  bool password_is_read = false;
 
3232
 
  bool quit_now = false;
 
3233
 
  __attribute__((cleanup(cleanup_queue)))
 
3234
 
    task_queue *queue = create_queue();
 
3235
 
  g_assert_nonnull(queue);
 
3237
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3238
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3239
 
                             sizeof(dummy_test_password)),
 
3240
 
                  ==, (int)sizeof(dummy_test_password));
 
3242
 
  task_context task = {
 
3243
 
    .func=read_mandos_client_output,
 
3246
 
    .password=&password,
 
3247
 
    .password_is_read=&password_is_read,
 
3248
 
    .quit_now=&quit_now,
 
3250
 
  task.func(task, queue);
 
3252
 
  g_assert_false(password_is_read);
 
3253
 
  g_assert_cmpint((int)password.length, ==,
 
3254
 
                  (int)sizeof(dummy_test_password));
 
3255
 
  g_assert_nonnull(password.data);
 
3256
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3257
 
                         sizeof(dummy_test_password)), ==, 0);
 
3259
 
  g_assert_false(quit_now);
 
3260
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3262
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3263
 
        .func=read_mandos_client_output,
 
3266
 
        .password=&password,
 
3267
 
        .password_is_read=&password_is_read,
 
3268
 
        .quit_now=&quit_now,
 
3271
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3272
 
                                   EPOLLIN | EPOLLRDHUP));
 
3274
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3278
 
void test_read_mandos_client_output_append(__attribute__((unused))
 
3279
 
                                           test_fixture *fixture,
 
3280
 
                                           __attribute__((unused))
 
3281
 
                                           gconstpointer user_data){
 
3282
 
  __attribute__((cleanup(cleanup_close)))
 
3283
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3284
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3287
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3289
 
  const char dummy_test_password[] = "dummy test password";
 
3290
 
  __attribute__((cleanup(cleanup_buffer)))
 
3292
 
    .data=malloc(PIPE_BUF),
 
3294
 
    .allocated=PIPE_BUF,
 
3296
 
  g_assert_nonnull(password.data);
 
3297
 
  if(mlock(password.data, password.allocated) != 0){
 
3298
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3301
 
  memset(password.data, 'x', PIPE_BUF);
 
3302
 
  char password_expected[PIPE_BUF];
 
3303
 
  memcpy(password_expected, password.data, PIPE_BUF);
 
3305
 
  bool password_is_read = false;
 
3306
 
  bool quit_now = false;
 
3307
 
  __attribute__((cleanup(cleanup_queue)))
 
3308
 
    task_queue *queue = create_queue();
 
3309
 
  g_assert_nonnull(queue);
 
3311
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3312
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3313
 
                             sizeof(dummy_test_password)),
 
3314
 
                  ==, (int)sizeof(dummy_test_password));
 
3316
 
  task_context task = {
 
3317
 
    .func=read_mandos_client_output,
 
3320
 
    .password=&password,
 
3321
 
    .password_is_read=&password_is_read,
 
3322
 
    .quit_now=&quit_now,
 
3324
 
  task.func(task, queue);
 
3326
 
  g_assert_false(password_is_read);
 
3327
 
  g_assert_cmpint((int)password.length, ==,
 
3328
 
                  PIPE_BUF + sizeof(dummy_test_password));
 
3329
 
  g_assert_nonnull(password.data);
 
3330
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
3332
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
3333
 
                         dummy_test_password,
 
3334
 
                         sizeof(dummy_test_password)), ==, 0);
 
3335
 
  g_assert_false(quit_now);
 
3336
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3338
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3339
 
        .func=read_mandos_client_output,
 
3342
 
        .password=&password,
 
3343
 
        .password_is_read=&password_is_read,
 
3344
 
        .quit_now=&quit_now,
 
3347
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3348
 
                                   EPOLLIN | EPOLLRDHUP));
 
3351
 
static char *make_temporary_directory(void);
 
3353
 
static void test_add_inotify_dir_watch(__attribute__((unused))
 
3354
 
                                       test_fixture *fixture,
 
3355
 
                                       __attribute__((unused))
 
3356
 
                                       gconstpointer user_data){
 
3357
 
  __attribute__((cleanup(cleanup_close)))
 
3358
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3359
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3360
 
  __attribute__((cleanup(cleanup_queue)))
 
3361
 
    task_queue *queue = create_queue();
 
3362
 
  g_assert_nonnull(queue);
 
3363
 
  __attribute__((cleanup(string_set_clear)))
 
3364
 
    string_set cancelled_filenames = {};
 
3365
 
  const mono_microsecs current_time = 0;
 
3367
 
  bool quit_now = false;
 
3368
 
  buffer password = {};
 
3369
 
  bool mandos_client_exited = false;
 
3370
 
  bool password_is_read = false;
 
3372
 
  __attribute__((cleanup(cleanup_string)))
 
3373
 
    char *tempdir = make_temporary_directory();
 
3374
 
  g_assert_nonnull(tempdir);
 
3376
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3378
 
                                      &cancelled_filenames,
 
3380
 
                                      &mandos_client_exited,
 
3381
 
                                      &password_is_read));
 
3383
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3385
 
  const task_context *const added_read_task
 
3386
 
    = find_matching_task(queue, (task_context){
 
3387
 
        .func=read_inotify_event,
 
3389
 
        .quit_now=&quit_now,
 
3390
 
        .password=&password,
 
3392
 
        .cancelled_filenames=&cancelled_filenames,
 
3393
 
        .current_time=¤t_time,
 
3394
 
        .mandos_client_exited=&mandos_client_exited,
 
3395
 
        .password_is_read=&password_is_read,
 
3397
 
  g_assert_nonnull(added_read_task);
 
3399
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3400
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3401
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
3402
 
                                   added_read_task->fd,
 
3403
 
                                   EPOLLIN | EPOLLRDHUP));
 
3405
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3408
 
static char *make_temporary_directory(void){
 
3409
 
  char *name = strdup("/tmp/mandosXXXXXX");
 
3410
 
  g_assert_nonnull(name);
 
3411
 
  char *result = mkdtemp(name);
 
3418
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
3419
 
                                            test_fixture *fixture,
 
3420
 
                                            __attribute__((unused))
 
3421
 
                                            gconstpointer user_data){
 
3422
 
  __attribute__((cleanup(cleanup_close)))
 
3423
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3424
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3425
 
  __attribute__((cleanup(cleanup_queue)))
 
3426
 
    task_queue *queue = create_queue();
 
3427
 
  g_assert_nonnull(queue);
 
3428
 
  __attribute__((cleanup(string_set_clear)))
 
3429
 
    string_set cancelled_filenames = {};
 
3430
 
  const mono_microsecs current_time = 0;
 
3432
 
  bool quit_now = false;
 
3433
 
  buffer password = {};
 
3434
 
  bool mandos_client_exited = false;
 
3435
 
  bool password_is_read = false;
 
3437
 
  const char nonexistent_dir[] = "/nonexistent";
 
3439
 
  FILE *real_stderr = stderr;
 
3440
 
  FILE *devnull = fopen("/dev/null", "we");
 
3441
 
  g_assert_nonnull(devnull);
 
3443
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3444
 
                                       &password, nonexistent_dir,
 
3445
 
                                       &cancelled_filenames,
 
3447
 
                                       &mandos_client_exited,
 
3448
 
                                       &password_is_read));
 
3449
 
  stderr = real_stderr;
 
3450
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3452
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3455
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
3456
 
                                              test_fixture *fixture,
 
3457
 
                                              __attribute__((unused))
 
3460
 
  __attribute__((cleanup(cleanup_close)))
 
3461
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3462
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3463
 
  __attribute__((cleanup(cleanup_queue)))
 
3464
 
    task_queue *queue = create_queue();
 
3465
 
  g_assert_nonnull(queue);
 
3466
 
  __attribute__((cleanup(string_set_clear)))
 
3467
 
    string_set cancelled_filenames = {};
 
3468
 
  const mono_microsecs current_time = 0;
 
3470
 
  bool quit_now = false;
 
3471
 
  buffer password = {};
 
3472
 
  bool mandos_client_exited = false;
 
3473
 
  bool password_is_read = false;
 
3475
 
  __attribute__((cleanup(cleanup_string)))
 
3476
 
    char *tempdir = make_temporary_directory();
 
3477
 
  g_assert_nonnull(tempdir);
 
3479
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3481
 
                                      &cancelled_filenames,
 
3483
 
                                      &mandos_client_exited,
 
3484
 
                                      &password_is_read));
 
3486
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3488
 
  const task_context *const added_read_task
 
3489
 
    = find_matching_task(queue,
 
3490
 
                         (task_context){ .func=read_inotify_event });
 
3491
 
  g_assert_nonnull(added_read_task);
 
3493
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3494
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3496
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3497
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3499
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3500
 
  g_assert_nonnull(ievent);
 
3502
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
3504
 
  g_assert_cmpint(errno, ==, EAGAIN);
 
3508
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3511
 
static char *make_temporary_file_in_directory(const char
 
3515
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
3516
 
                                               test_fixture *fixture,
 
3517
 
                                               __attribute__((unused))
 
3520
 
  __attribute__((cleanup(cleanup_close)))
 
3521
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3522
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3523
 
  __attribute__((cleanup(cleanup_queue)))
 
3524
 
    task_queue *queue = create_queue();
 
3525
 
  g_assert_nonnull(queue);
 
3526
 
  __attribute__((cleanup(string_set_clear)))
 
3527
 
    string_set cancelled_filenames = {};
 
3528
 
  const mono_microsecs current_time = 0;
 
3530
 
  bool quit_now = false;
 
3531
 
  buffer password = {};
 
3532
 
  bool mandos_client_exited = false;
 
3533
 
  bool password_is_read = false;
 
3535
 
  __attribute__((cleanup(cleanup_string)))
 
3536
 
    char *tempdir = make_temporary_directory();
 
3537
 
  g_assert_nonnull(tempdir);
 
3539
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3541
 
                                      &cancelled_filenames,
 
3543
 
                                      &mandos_client_exited,
 
3544
 
                                      &password_is_read));
 
3546
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3548
 
  const task_context *const added_read_task
 
3549
 
    = find_matching_task(queue,
 
3550
 
                         (task_context){ .func=read_inotify_event });
 
3551
 
  g_assert_nonnull(added_read_task);
 
3553
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3554
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3556
 
  __attribute__((cleanup(cleanup_string)))
 
3557
 
    char *filename = make_temporary_file_in_directory(tempdir);
 
3558
 
  g_assert_nonnull(filename);
 
3560
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3561
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3563
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3564
 
  g_assert_nonnull(ievent);
 
3566
 
  ssize_t read_size = 0;
 
3567
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3569
 
  g_assert_cmpint((int)read_size, >, 0);
 
3570
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
3571
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
3575
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
3576
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3579
 
static char *make_temporary_prefixed_file_in_directory(const char
 
3583
 
  char *filename = NULL;
 
3584
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
3586
 
  g_assert_nonnull(filename);
 
3587
 
  const int fd = mkostemp(filename, O_CLOEXEC);
 
3588
 
  g_assert_cmpint(fd, >=, 0);
 
3589
 
  g_assert_cmpint(close(fd), ==, 0);
 
3593
 
static char *make_temporary_file_in_directory(const char
 
3595
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
3599
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
3600
 
                                            test_fixture *fixture,
 
3601
 
                                            __attribute__((unused))
 
3602
 
                                            gconstpointer user_data){
 
3603
 
  __attribute__((cleanup(cleanup_close)))
 
3604
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3605
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3606
 
  __attribute__((cleanup(cleanup_queue)))
 
3607
 
    task_queue *queue = create_queue();
 
3608
 
  g_assert_nonnull(queue);
 
3609
 
  __attribute__((cleanup(string_set_clear)))
 
3610
 
    string_set cancelled_filenames = {};
 
3611
 
  const mono_microsecs current_time = 0;
 
3613
 
  bool quit_now = false;
 
3614
 
  buffer password = {};
 
3615
 
  bool mandos_client_exited = false;
 
3616
 
  bool password_is_read = false;
 
3618
 
  __attribute__((cleanup(cleanup_string)))
 
3619
 
    char *watchdir = make_temporary_directory();
 
3620
 
  g_assert_nonnull(watchdir);
 
3622
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3623
 
                                      &password, watchdir,
 
3624
 
                                      &cancelled_filenames,
 
3626
 
                                      &mandos_client_exited,
 
3627
 
                                      &password_is_read));
 
3629
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3631
 
  const task_context *const added_read_task
 
3632
 
    = find_matching_task(queue,
 
3633
 
                         (task_context){ .func=read_inotify_event });
 
3634
 
  g_assert_nonnull(added_read_task);
 
3636
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3637
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3639
 
  char *sourcedir = make_temporary_directory();
 
3640
 
  g_assert_nonnull(sourcedir);
 
3642
 
  __attribute__((cleanup(cleanup_string)))
 
3643
 
    char *filename = make_temporary_file_in_directory(sourcedir);
 
3644
 
  g_assert_nonnull(filename);
 
3646
 
  __attribute__((cleanup(cleanup_string)))
 
3647
 
    char *targetfilename = NULL;
 
3648
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
3649
 
                           basename(filename)), >, 0);
 
3650
 
  g_assert_nonnull(targetfilename);
 
3652
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
3653
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
3656
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3657
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3659
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3660
 
  g_assert_nonnull(ievent);
 
3662
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3664
 
  g_assert_cmpint((int)read_size, >, 0);
 
3665
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
3666
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
3670
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3671
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
3675
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
3676
 
                                          test_fixture *fixture,
 
3677
 
                                          __attribute__((unused))
 
3678
 
                                          gconstpointer user_data){
 
3679
 
  __attribute__((cleanup(cleanup_close)))
 
3680
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3681
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3682
 
  __attribute__((cleanup(cleanup_queue)))
 
3683
 
    task_queue *queue = create_queue();
 
3684
 
  g_assert_nonnull(queue);
 
3685
 
  __attribute__((cleanup(string_set_clear)))
 
3686
 
    string_set cancelled_filenames = {};
 
3687
 
  const mono_microsecs current_time = 0;
 
3689
 
  bool quit_now = false;
 
3690
 
  buffer password = {};
 
3691
 
  bool mandos_client_exited = false;
 
3692
 
  bool password_is_read = false;
 
3694
 
  __attribute__((cleanup(cleanup_string)))
 
3695
 
    char *tempdir = make_temporary_directory();
 
3696
 
  g_assert_nonnull(tempdir);
 
3698
 
  __attribute__((cleanup(cleanup_string)))
 
3699
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3700
 
  g_assert_nonnull(tempfile);
 
3702
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3704
 
                                      &cancelled_filenames,
 
3706
 
                                      &mandos_client_exited,
 
3707
 
                                      &password_is_read));
 
3708
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3710
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3712
 
  const task_context *const added_read_task
 
3713
 
    = find_matching_task(queue,
 
3714
 
                         (task_context){ .func=read_inotify_event });
 
3715
 
  g_assert_nonnull(added_read_task);
 
3717
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3718
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3720
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3721
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3723
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3724
 
  g_assert_nonnull(ievent);
 
3726
 
  ssize_t read_size = 0;
 
3727
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3729
 
  g_assert_cmpint((int)read_size, >, 0);
 
3730
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3731
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3735
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3738
 
static void test_read_inotify_event_readerror(__attribute__((unused))
 
3739
 
                                              test_fixture *fixture,
 
3740
 
                                              __attribute__((unused))
 
3743
 
  __attribute__((cleanup(cleanup_close)))
 
3744
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3745
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3746
 
  const mono_microsecs current_time = 0;
 
3748
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
3749
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3751
 
  bool quit_now = false;
 
3752
 
  __attribute__((cleanup(cleanup_queue)))
 
3753
 
    task_queue *queue = create_queue();
 
3754
 
  g_assert_nonnull(queue);
 
3756
 
  task_context task = {
 
3757
 
    .func=read_inotify_event,
 
3760
 
    .quit_now=&quit_now,
 
3761
 
    .filename=strdup("/nonexistent"),
 
3762
 
    .cancelled_filenames = &(string_set){},
 
3764
 
    .current_time=¤t_time,
 
3766
 
  g_assert_nonnull(task.filename);
 
3767
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3768
 
  g_assert_true(quit_now);
 
3769
 
  g_assert_true(queue->next_run == 0);
 
3770
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3772
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3774
 
  g_assert_cmpint(close(fd), ==, -1);
 
3777
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
3778
 
                                              test_fixture *fixture,
 
3779
 
                                              __attribute__((unused))
 
3782
 
  const mono_microsecs current_time = 17;
 
3785
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3786
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
3788
 
  bool quit_now = false;
 
3789
 
  buffer password = {};
 
3790
 
  bool mandos_client_exited = false;
 
3791
 
  bool password_is_read = false;
 
3792
 
  __attribute__((cleanup(cleanup_queue)))
 
3793
 
    task_queue *queue = create_queue();
 
3794
 
  g_assert_nonnull(queue);
 
3796
 
  task_context task = {
 
3797
 
    .func=read_inotify_event,
 
3800
 
    .quit_now=&quit_now,
 
3801
 
    .password=&password,
 
3802
 
    .filename=strdup("/nonexistent"),
 
3803
 
    .cancelled_filenames = &(string_set){},
 
3805
 
    .current_time=¤t_time,
 
3806
 
    .mandos_client_exited=&mandos_client_exited,
 
3807
 
    .password_is_read=&password_is_read,
 
3809
 
  g_assert_nonnull(task.filename);
 
3810
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3812
 
  g_assert_nonnull(find_matching_task(queue, task));
 
3813
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
3815
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
3816
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3819
 
static void test_read_inotify_event_nodata(__attribute__((unused))
 
3820
 
                                           test_fixture *fixture,
 
3821
 
                                           __attribute__((unused))
 
3822
 
                                           gconstpointer user_data){
 
3823
 
  __attribute__((cleanup(cleanup_close)))
 
3824
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3825
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3826
 
  const mono_microsecs current_time = 0;
 
3829
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3831
 
  bool quit_now = false;
 
3832
 
  buffer password = {};
 
3833
 
  bool mandos_client_exited = false;
 
3834
 
  bool password_is_read = false;
 
3835
 
  __attribute__((cleanup(cleanup_queue)))
 
3836
 
    task_queue *queue = create_queue();
 
3837
 
  g_assert_nonnull(queue);
 
3839
 
  task_context task = {
 
3840
 
    .func=read_inotify_event,
 
3843
 
    .quit_now=&quit_now,
 
3844
 
    .password=&password,
 
3845
 
    .filename=strdup("/nonexistent"),
 
3846
 
    .cancelled_filenames = &(string_set){},
 
3848
 
    .current_time=¤t_time,
 
3849
 
    .mandos_client_exited=&mandos_client_exited,
 
3850
 
    .password_is_read=&password_is_read,
 
3852
 
  g_assert_nonnull(task.filename);
 
3853
 
  task.func(task, queue);
 
3854
 
  g_assert_false(quit_now);
 
3855
 
  g_assert_true(queue->next_run == 0);
 
3856
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3858
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3859
 
        .func=read_inotify_event,
 
3862
 
        .quit_now=&quit_now,
 
3863
 
        .password=&password,
 
3864
 
        .filename=task.filename,
 
3865
 
        .cancelled_filenames=task.cancelled_filenames,
 
3866
 
        .current_time=¤t_time,
 
3867
 
        .mandos_client_exited=&mandos_client_exited,
 
3868
 
        .password_is_read=&password_is_read,
 
3871
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3872
 
                                   EPOLLIN | EPOLLRDHUP));
 
3874
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3877
 
static void test_read_inotify_event_eof(__attribute__((unused))
 
3878
 
                                        test_fixture *fixture,
 
3879
 
                                        __attribute__((unused))
 
3880
 
                                        gconstpointer user_data){
 
3881
 
  __attribute__((cleanup(cleanup_close)))
 
3882
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3883
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3884
 
  const mono_microsecs current_time = 0;
 
3887
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3888
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3890
 
  bool quit_now = false;
 
3891
 
  buffer password = {};
 
3892
 
  __attribute__((cleanup(cleanup_queue)))
 
3893
 
    task_queue *queue = create_queue();
 
3894
 
  g_assert_nonnull(queue);
 
3896
 
  task_context task = {
 
3897
 
    .func=read_inotify_event,
 
3900
 
    .quit_now=&quit_now,
 
3901
 
    .password=&password,
 
3902
 
    .filename=strdup("/nonexistent"),
 
3903
 
    .cancelled_filenames = &(string_set){},
 
3905
 
    .current_time=¤t_time,
 
3907
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3908
 
  g_assert_true(quit_now);
 
3909
 
  g_assert_true(queue->next_run == 0);
 
3910
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3912
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3914
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3918
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
3919
 
                                            test_fixture *fixture,
 
3920
 
                                            __attribute__((unused))
 
3921
 
                                            gconstpointer user_data){
 
3922
 
  __attribute__((cleanup(cleanup_close)))
 
3923
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3924
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3925
 
  const mono_microsecs current_time = 0;
 
3928
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3930
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3931
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
3933
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
3935
 
    struct inotify_event event;
 
3936
 
    char name_buffer[NAME_MAX + 1];
 
3938
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
3940
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
3941
 
  ievent->mask = IN_CLOSE_WRITE;
 
3942
 
  ievent->len = sizeof(dummy_file_name);
 
3943
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
3944
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3945
 
                              + sizeof(dummy_file_name));
 
3946
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
3948
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3950
 
  bool quit_now = false;
 
3951
 
  buffer password = {};
 
3952
 
  bool mandos_client_exited = false;
 
3953
 
  bool password_is_read = false;
 
3954
 
  __attribute__((cleanup(cleanup_queue)))
 
3955
 
    task_queue *queue = create_queue();
 
3956
 
  g_assert_nonnull(queue);
 
3958
 
  task_context task = {
 
3959
 
    .func=read_inotify_event,
 
3962
 
    .quit_now=&quit_now,
 
3963
 
    .password=&password,
 
3964
 
    .filename=strdup("/nonexistent"),
 
3965
 
    .cancelled_filenames = &(string_set){},
 
3967
 
    .current_time=¤t_time,
 
3968
 
    .mandos_client_exited=&mandos_client_exited,
 
3969
 
    .password_is_read=&password_is_read,
 
3971
 
  task.func(task, queue);
 
3972
 
  g_assert_false(quit_now);
 
3973
 
  g_assert_true(queue->next_run != 0);
 
3974
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
3976
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3977
 
        .func=read_inotify_event,
 
3980
 
        .quit_now=&quit_now,
 
3981
 
        .password=&password,
 
3982
 
        .filename=task.filename,
 
3983
 
        .cancelled_filenames=task.cancelled_filenames,
 
3984
 
        .current_time=¤t_time,
 
3985
 
        .mandos_client_exited=&mandos_client_exited,
 
3986
 
        .password_is_read=&password_is_read,
 
3989
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3990
 
                                   EPOLLIN | EPOLLRDHUP));
 
3992
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
3994
 
  __attribute__((cleanup(cleanup_string)))
 
3995
 
    char *filename = NULL;
 
3996
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
3997
 
                           dummy_file_name), >, 0);
 
3998
 
  g_assert_nonnull(filename);
 
3999
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4000
 
        .func=open_and_parse_question,
 
4003
 
        .question_filename=filename,
 
4004
 
        .password=&password,
 
4005
 
        .cancelled_filenames=task.cancelled_filenames,
 
4006
 
        .current_time=¤t_time,
 
4007
 
        .mandos_client_exited=&mandos_client_exited,
 
4008
 
        .password_is_read=&password_is_read,
 
4013
 
void test_read_inotify_event_IN_MOVED_TO(__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;
 
4023
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4025
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4026
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4028
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4030
 
    struct inotify_event event;
 
4031
 
    char name_buffer[NAME_MAX + 1];
 
4033
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4035
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4036
 
  ievent->mask = IN_MOVED_TO;
 
4037
 
  ievent->len = sizeof(dummy_file_name);
 
4038
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4039
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4040
 
                              + sizeof(dummy_file_name));
 
4041
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4043
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4045
 
  bool quit_now = false;
 
4046
 
  buffer password = {};
 
4047
 
  bool mandos_client_exited = false;
 
4048
 
  bool password_is_read = false;
 
4049
 
  __attribute__((cleanup(cleanup_queue)))
 
4050
 
    task_queue *queue = create_queue();
 
4051
 
  g_assert_nonnull(queue);
 
4053
 
  task_context task = {
 
4054
 
    .func=read_inotify_event,
 
4057
 
    .quit_now=&quit_now,
 
4058
 
    .password=&password,
 
4059
 
    .filename=strdup("/nonexistent"),
 
4060
 
    .cancelled_filenames = &(string_set){},
 
4062
 
    .current_time=¤t_time,
 
4063
 
    .mandos_client_exited=&mandos_client_exited,
 
4064
 
    .password_is_read=&password_is_read,
 
4066
 
  task.func(task, queue);
 
4067
 
  g_assert_false(quit_now);
 
4068
 
  g_assert_true(queue->next_run != 0);
 
4069
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4071
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4072
 
        .func=read_inotify_event,
 
4075
 
        .quit_now=&quit_now,
 
4076
 
        .password=&password,
 
4077
 
        .filename=task.filename,
 
4078
 
        .cancelled_filenames=task.cancelled_filenames,
 
4079
 
        .current_time=¤t_time,
 
4080
 
        .mandos_client_exited=&mandos_client_exited,
 
4081
 
        .password_is_read=&password_is_read,
 
4084
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4085
 
                                   EPOLLIN | EPOLLRDHUP));
 
4087
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4089
 
  __attribute__((cleanup(cleanup_string)))
 
4090
 
    char *filename = NULL;
 
4091
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4092
 
                           dummy_file_name), >, 0);
 
4093
 
  g_assert_nonnull(filename);
 
4094
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4095
 
        .func=open_and_parse_question,
 
4098
 
        .question_filename=filename,
 
4099
 
        .password=&password,
 
4100
 
        .cancelled_filenames=task.cancelled_filenames,
 
4101
 
        .current_time=¤t_time,
 
4102
 
        .mandos_client_exited=&mandos_client_exited,
 
4103
 
        .password_is_read=&password_is_read,
 
4107
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
4108
 
                                              test_fixture *fixture,
 
4109
 
                                              __attribute__((unused))
 
4112
 
  __attribute__((cleanup(cleanup_close)))
 
4113
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4114
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4115
 
  __attribute__((cleanup(string_set_clear)))
 
4116
 
    string_set cancelled_filenames = {};
 
4117
 
  const mono_microsecs current_time = 0;
 
4120
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4122
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4123
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4125
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4127
 
    struct inotify_event event;
 
4128
 
    char name_buffer[NAME_MAX + 1];
 
4130
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4132
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4133
 
  ievent->mask = IN_DELETE;
 
4134
 
  ievent->len = sizeof(dummy_file_name);
 
4135
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4136
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4137
 
                              + sizeof(dummy_file_name));
 
4138
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4140
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4142
 
  bool quit_now = false;
 
4143
 
  buffer password = {};
 
4144
 
  bool mandos_client_exited = false;
 
4145
 
  bool password_is_read = false;
 
4146
 
  __attribute__((cleanup(cleanup_queue)))
 
4147
 
    task_queue *queue = create_queue();
 
4148
 
  g_assert_nonnull(queue);
 
4150
 
  task_context task = {
 
4151
 
    .func=read_inotify_event,
 
4154
 
    .quit_now=&quit_now,
 
4155
 
    .password=&password,
 
4156
 
    .filename=strdup("/nonexistent"),
 
4157
 
    .cancelled_filenames=&cancelled_filenames,
 
4158
 
    .current_time=¤t_time,
 
4159
 
    .mandos_client_exited=&mandos_client_exited,
 
4160
 
    .password_is_read=&password_is_read,
 
4162
 
  task.func(task, queue);
 
4163
 
  g_assert_false(quit_now);
 
4164
 
  g_assert_true(queue->next_run == 0);
 
4165
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4167
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4168
 
        .func=read_inotify_event,
 
4171
 
        .quit_now=&quit_now,
 
4172
 
        .password=&password,
 
4173
 
        .filename=task.filename,
 
4174
 
        .cancelled_filenames=&cancelled_filenames,
 
4175
 
        .current_time=¤t_time,
 
4176
 
        .mandos_client_exited=&mandos_client_exited,
 
4177
 
        .password_is_read=&password_is_read,
 
4180
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4181
 
                                   EPOLLIN | EPOLLRDHUP));
 
4183
 
  __attribute__((cleanup(cleanup_string)))
 
4184
 
    char *filename = NULL;
 
4185
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4186
 
                           dummy_file_name), >, 0);
 
4187
 
  g_assert_nonnull(filename);
 
4188
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4193
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
4194
 
                                               test_fixture *fixture,
 
4195
 
                                               __attribute__((unused))
 
4198
 
  __attribute__((cleanup(cleanup_close)))
 
4199
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4200
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4201
 
  const mono_microsecs current_time = 0;
 
4204
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4206
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4207
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4209
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4211
 
    struct inotify_event event;
 
4212
 
    char name_buffer[NAME_MAX + 1];
 
4214
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4216
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4217
 
  ievent->mask = IN_CLOSE_WRITE;
 
4218
 
  ievent->len = sizeof(dummy_file_name);
 
4219
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4220
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4221
 
                              + sizeof(dummy_file_name));
 
4222
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4224
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4226
 
  bool quit_now = false;
 
4227
 
  buffer password = {};
 
4228
 
  bool mandos_client_exited = false;
 
4229
 
  bool password_is_read = false;
 
4230
 
  __attribute__((cleanup(cleanup_queue)))
 
4231
 
    task_queue *queue = create_queue();
 
4232
 
  g_assert_nonnull(queue);
 
4234
 
  task_context task = {
 
4235
 
    .func=read_inotify_event,
 
4238
 
    .quit_now=&quit_now,
 
4239
 
    .password=&password,
 
4240
 
    .filename=strdup("/nonexistent"),
 
4241
 
    .cancelled_filenames = &(string_set){},
 
4243
 
    .current_time=¤t_time,
 
4244
 
    .mandos_client_exited=&mandos_client_exited,
 
4245
 
    .password_is_read=&password_is_read,
 
4247
 
  task.func(task, queue);
 
4248
 
  g_assert_false(quit_now);
 
4249
 
  g_assert_true(queue->next_run == 0);
 
4250
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4252
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4253
 
        .func=read_inotify_event,
 
4256
 
        .quit_now=&quit_now,
 
4257
 
        .password=&password,
 
4258
 
        .filename=task.filename,
 
4259
 
        .cancelled_filenames=task.cancelled_filenames,
 
4260
 
        .current_time=¤t_time,
 
4261
 
        .mandos_client_exited=&mandos_client_exited,
 
4262
 
        .password_is_read=&password_is_read,
 
4265
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4266
 
                                   EPOLLIN | EPOLLRDHUP));
 
4270
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
4271
 
                                            test_fixture *fixture,
 
4272
 
                                            __attribute__((unused))
 
4273
 
                                            gconstpointer user_data){
 
4274
 
  __attribute__((cleanup(cleanup_close)))
 
4275
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4276
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4277
 
  const mono_microsecs current_time = 0;
 
4280
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4282
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4283
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4285
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4287
 
    struct inotify_event event;
 
4288
 
    char name_buffer[NAME_MAX + 1];
 
4290
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4292
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4293
 
  ievent->mask = IN_MOVED_TO;
 
4294
 
  ievent->len = sizeof(dummy_file_name);
 
4295
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4296
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4297
 
                              + sizeof(dummy_file_name));
 
4298
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4300
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4302
 
  bool quit_now = false;
 
4303
 
  buffer password = {};
 
4304
 
  bool mandos_client_exited = false;
 
4305
 
  bool password_is_read = false;
 
4306
 
  __attribute__((cleanup(cleanup_queue)))
 
4307
 
    task_queue *queue = create_queue();
 
4308
 
  g_assert_nonnull(queue);
 
4310
 
  task_context task = {
 
4311
 
    .func=read_inotify_event,
 
4314
 
    .quit_now=&quit_now,
 
4315
 
    .password=&password,
 
4316
 
    .filename=strdup("/nonexistent"),
 
4317
 
    .cancelled_filenames = &(string_set){},
 
4319
 
    .current_time=¤t_time,
 
4320
 
    .mandos_client_exited=&mandos_client_exited,
 
4321
 
    .password_is_read=&password_is_read,
 
4323
 
  task.func(task, queue);
 
4324
 
  g_assert_false(quit_now);
 
4325
 
  g_assert_true(queue->next_run == 0);
 
4326
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4328
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4329
 
        .func=read_inotify_event,
 
4332
 
        .quit_now=&quit_now,
 
4333
 
        .password=&password,
 
4334
 
        .filename=task.filename,
 
4335
 
        .cancelled_filenames=task.cancelled_filenames,
 
4336
 
        .current_time=¤t_time,
 
4337
 
        .mandos_client_exited=&mandos_client_exited,
 
4338
 
        .password_is_read=&password_is_read,
 
4341
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4342
 
                                   EPOLLIN | EPOLLRDHUP));
 
4346
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
4347
 
                                               test_fixture *fixture,
 
4348
 
                                               __attribute__((unused))
 
4351
 
  __attribute__((cleanup(cleanup_close)))
 
4352
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4353
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4354
 
  __attribute__((cleanup(string_set_clear)))
 
4355
 
    string_set cancelled_filenames = {};
 
4356
 
  const mono_microsecs current_time = 0;
 
4359
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4361
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4362
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4364
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4366
 
    struct inotify_event event;
 
4367
 
    char name_buffer[NAME_MAX + 1];
 
4369
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4371
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4372
 
  ievent->mask = IN_DELETE;
 
4373
 
  ievent->len = sizeof(dummy_file_name);
 
4374
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4375
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4376
 
                              + sizeof(dummy_file_name));
 
4377
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4379
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4381
 
  bool quit_now = false;
 
4382
 
  buffer password = {};
 
4383
 
  bool mandos_client_exited = false;
 
4384
 
  bool password_is_read = false;
 
4385
 
  __attribute__((cleanup(cleanup_queue)))
 
4386
 
    task_queue *queue = create_queue();
 
4387
 
  g_assert_nonnull(queue);
 
4389
 
  task_context task = {
 
4390
 
    .func=read_inotify_event,
 
4393
 
    .quit_now=&quit_now,
 
4394
 
    .password=&password,
 
4395
 
    .filename=strdup("/nonexistent"),
 
4396
 
    .cancelled_filenames=&cancelled_filenames,
 
4397
 
    .current_time=¤t_time,
 
4398
 
    .mandos_client_exited=&mandos_client_exited,
 
4399
 
    .password_is_read=&password_is_read,
 
4401
 
  task.func(task, queue);
 
4402
 
  g_assert_false(quit_now);
 
4403
 
  g_assert_true(queue->next_run == 0);
 
4404
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4406
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4407
 
        .func=read_inotify_event,
 
4410
 
        .quit_now=&quit_now,
 
4411
 
        .password=&password,
 
4412
 
        .filename=task.filename,
 
4413
 
        .cancelled_filenames=&cancelled_filenames,
 
4414
 
        .current_time=¤t_time,
 
4415
 
        .mandos_client_exited=&mandos_client_exited,
 
4416
 
        .password_is_read=&password_is_read,
 
4419
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4420
 
                                   EPOLLIN | EPOLLRDHUP));
 
4422
 
  __attribute__((cleanup(cleanup_string)))
 
4423
 
    char *filename = NULL;
 
4424
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4425
 
                           dummy_file_name), >, 0);
 
4426
 
  g_assert_nonnull(filename);
 
4427
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4431
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
4432
 
                                         test_fixture *fixture,
 
4433
 
                                         __attribute__((unused))
 
4434
 
                                         gconstpointer user_data){
 
4435
 
  __attribute__((cleanup(cleanup_close)))
 
4436
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4437
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4438
 
  __attribute__((cleanup(string_set_clear)))
 
4439
 
    string_set cancelled_filenames = {};
 
4440
 
  bool mandos_client_exited = false;
 
4441
 
  bool password_is_read = false;
 
4442
 
  __attribute__((cleanup(cleanup_queue)))
 
4443
 
    task_queue *queue = create_queue();
 
4444
 
  g_assert_nonnull(queue);
 
4446
 
  char *const filename = strdup("/nonexistent");
 
4447
 
  g_assert_nonnull(filename);
 
4448
 
  task_context task = {
 
4449
 
    .func=open_and_parse_question,
 
4450
 
    .question_filename=filename,
 
4452
 
    .password=(buffer[]){{}},
 
4454
 
    .cancelled_filenames=&cancelled_filenames,
 
4455
 
    .current_time=(mono_microsecs[]){0},
 
4456
 
    .mandos_client_exited=&mandos_client_exited,
 
4457
 
    .password_is_read=&password_is_read,
 
4459
 
  task.func(task, queue);
 
4460
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4463
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
4464
 
                                             test_fixture *fixture,
 
4465
 
                                             __attribute__((unused))
 
4466
 
                                             gconstpointer user_data){
 
4467
 
  __attribute__((cleanup(cleanup_close)))
 
4468
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4469
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4470
 
  __attribute__((cleanup(string_set_clear)))
 
4471
 
    string_set cancelled_filenames = {};
 
4472
 
  buffer password = {};
 
4473
 
  bool mandos_client_exited = false;
 
4474
 
  bool password_is_read = false;
 
4475
 
  __attribute__((cleanup(cleanup_queue)))
 
4476
 
    task_queue *queue = create_queue();
 
4477
 
  g_assert_nonnull(queue);
 
4478
 
  const mono_microsecs current_time = 0;
 
4480
 
  char *filename = strdup("/proc/self/mem");
 
4481
 
  task_context task = {
 
4482
 
    .func=open_and_parse_question,
 
4483
 
    .question_filename=filename,
 
4485
 
    .password=&password,
 
4487
 
    .cancelled_filenames=&cancelled_filenames,
 
4488
 
    .current_time=¤t_time,
 
4489
 
    .mandos_client_exited=&mandos_client_exited,
 
4490
 
    .password_is_read=&password_is_read,
 
4492
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4493
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4497
 
test_open_and_parse_question_parse_error(__attribute__((unused))
 
4498
 
                                         test_fixture *fixture,
 
4499
 
                                         __attribute__((unused))
 
4500
 
                                         gconstpointer user_data){
 
4501
 
  __attribute__((cleanup(cleanup_close)))
 
4502
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4503
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4504
 
  __attribute__((cleanup(string_set_clear)))
 
4505
 
    string_set cancelled_filenames = {};
 
4506
 
  __attribute__((cleanup(cleanup_queue)))
 
4507
 
    task_queue *queue = create_queue();
 
4508
 
  g_assert_nonnull(queue);
 
4510
 
  __attribute__((cleanup(cleanup_string)))
 
4511
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4512
 
  g_assert_nonnull(tempfilename);
 
4513
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
4514
 
  g_assert_cmpint(tempfile, >, 0);
 
4515
 
  const char bad_data[] = "this is bad syntax\n";
 
4516
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
4517
 
                  ==, sizeof(bad_data));
 
4518
 
  g_assert_cmpint(close(tempfile), ==, 0);
 
4520
 
  char *const filename = strdup(tempfilename);
 
4521
 
  g_assert_nonnull(filename);
 
4522
 
  task_context task = {
 
4523
 
    .func=open_and_parse_question,
 
4524
 
    .question_filename=filename,
 
4526
 
    .password=(buffer[]){{}},
 
4528
 
    .cancelled_filenames=&cancelled_filenames,
 
4529
 
    .current_time=(mono_microsecs[]){0},
 
4530
 
    .mandos_client_exited=(bool[]){false},
 
4531
 
    .password_is_read=(bool[]){false},
 
4533
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4535
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4537
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4541
 
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
4542
 
                                           test_fixture *fixture,
 
4543
 
                                           __attribute__((unused))
 
4544
 
                                           gconstpointer user_data){
 
4545
 
  __attribute__((cleanup(cleanup_close)))
 
4546
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4547
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4548
 
  __attribute__((cleanup(string_set_clear)))
 
4549
 
    string_set cancelled_filenames = {};
 
4550
 
  __attribute__((cleanup(cleanup_queue)))
 
4551
 
    task_queue *queue = create_queue();
 
4552
 
  g_assert_nonnull(queue);
 
4554
 
  __attribute__((cleanup(cleanup_string)))
 
4555
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4556
 
  g_assert_nonnull(tempfilename);
 
4557
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4558
 
  g_assert_cmpint(questionfile, >, 0);
 
4559
 
  FILE *qf = fdopen(questionfile, "w");
 
4560
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
4561
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4563
 
  char *const filename = strdup(tempfilename);
 
4564
 
  g_assert_nonnull(filename);
 
4565
 
  task_context task = {
 
4566
 
    .func=open_and_parse_question,
 
4567
 
    .question_filename=filename,
 
4569
 
    .password=(buffer[]){{}},
 
4571
 
    .cancelled_filenames=&cancelled_filenames,
 
4572
 
    .current_time=(mono_microsecs[]){0},
 
4573
 
    .mandos_client_exited=(bool[]){false},
 
4574
 
    .password_is_read=(bool[]){false},
 
4576
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4577
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4579
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4583
 
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
4584
 
                                            test_fixture *fixture,
 
4585
 
                                            __attribute__((unused))
 
4586
 
                                            gconstpointer user_data){
 
4587
 
  __attribute__((cleanup(cleanup_close)))
 
4588
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4589
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4590
 
  __attribute__((cleanup(string_set_clear)))
 
4591
 
    string_set cancelled_filenames = {};
 
4592
 
  __attribute__((cleanup(cleanup_queue)))
 
4593
 
    task_queue *queue = create_queue();
 
4594
 
  g_assert_nonnull(queue);
 
4596
 
  __attribute__((cleanup(cleanup_string)))
 
4597
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4598
 
  g_assert_nonnull(tempfilename);
 
4599
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4600
 
  g_assert_cmpint(questionfile, >, 0);
 
4601
 
  FILE *qf = fdopen(questionfile, "w");
 
4602
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
4603
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4605
 
  char *const filename = strdup(tempfilename);
 
4606
 
  g_assert_nonnull(filename);
 
4607
 
  task_context task = {
 
4608
 
    .func=open_and_parse_question,
 
4609
 
    .question_filename=filename,
 
4611
 
    .password=(buffer[]){{}},
 
4613
 
    .cancelled_filenames=&cancelled_filenames,
 
4614
 
    .current_time=(mono_microsecs[]){0},
 
4615
 
    .mandos_client_exited=(bool[]){false},
 
4616
 
    .password_is_read=(bool[]){false},
 
4618
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4619
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4621
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4625
 
void test_open_and_parse_question_nopid(__attribute__((unused))
 
4626
 
                                        test_fixture *fixture,
 
4627
 
                                        __attribute__((unused))
 
4628
 
                                        gconstpointer user_data){
 
4629
 
  __attribute__((cleanup(cleanup_close)))
 
4630
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4631
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4632
 
  __attribute__((cleanup(string_set_clear)))
 
4633
 
    string_set cancelled_filenames = {};
 
4634
 
  __attribute__((cleanup(cleanup_queue)))
 
4635
 
    task_queue *queue = create_queue();
 
4636
 
  g_assert_nonnull(queue);
 
4638
 
  __attribute__((cleanup(cleanup_string)))
 
4639
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4640
 
  g_assert_nonnull(tempfilename);
 
4641
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4642
 
  g_assert_cmpint(questionfile, >, 0);
 
4643
 
  FILE *qf = fdopen(questionfile, "w");
 
4644
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
4645
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4647
 
  char *const filename = strdup(tempfilename);
 
4648
 
  g_assert_nonnull(filename);
 
4649
 
  task_context task = {
 
4650
 
    .func=open_and_parse_question,
 
4651
 
    .question_filename=filename,
 
4653
 
    .password=(buffer[]){{}},
 
4655
 
    .cancelled_filenames=&cancelled_filenames,
 
4656
 
    .current_time=(mono_microsecs[]){0},
 
4657
 
    .mandos_client_exited=(bool[]){false},
 
4658
 
    .password_is_read=(bool[]){false},
 
4660
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4661
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4663
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4667
 
void test_open_and_parse_question_badpid(__attribute__((unused))
 
4668
 
                                         test_fixture *fixture,
 
4669
 
                                         __attribute__((unused))
 
4670
 
                                         gconstpointer user_data){
 
4671
 
  __attribute__((cleanup(cleanup_close)))
 
4672
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4673
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4674
 
  __attribute__((cleanup(string_set_clear)))
 
4675
 
    string_set cancelled_filenames = {};
 
4676
 
  __attribute__((cleanup(cleanup_queue)))
 
4677
 
    task_queue *queue = create_queue();
 
4678
 
  g_assert_nonnull(queue);
 
4680
 
  __attribute__((cleanup(cleanup_string)))
 
4681
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4682
 
  g_assert_nonnull(tempfilename);
 
4683
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4684
 
  g_assert_cmpint(questionfile, >, 0);
 
4685
 
  FILE *qf = fdopen(questionfile, "w");
 
4686
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
4688
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4690
 
  char *const filename = strdup(tempfilename);
 
4691
 
  g_assert_nonnull(filename);
 
4692
 
  task_context task = {
 
4693
 
    .func=open_and_parse_question,
 
4694
 
    .question_filename=filename,
 
4696
 
    .password=(buffer[]){{}},
 
4698
 
    .cancelled_filenames=&cancelled_filenames,
 
4699
 
    .current_time=(mono_microsecs[]){0},
 
4700
 
    .mandos_client_exited=(bool[]){false},
 
4701
 
    .password_is_read=(bool[]){false},
 
4703
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4704
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4706
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4710
 
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
4711
 
                                         test_fixture *fixture,
 
4712
 
                                         __attribute__((unused))
 
4713
 
                                         gconstpointer user_data){
 
4714
 
  __attribute__((cleanup(cleanup_close)))
 
4715
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4716
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4717
 
  __attribute__((cleanup(string_set_clear)))
 
4718
 
    string_set cancelled_filenames = {};
 
4719
 
  buffer password = {};
 
4720
 
  bool mandos_client_exited = false;
 
4721
 
  bool password_is_read = false;
 
4722
 
  __attribute__((cleanup(cleanup_queue)))
 
4723
 
    task_queue *queue = create_queue();
 
4724
 
  g_assert_nonnull(queue);
 
4725
 
  const mono_microsecs current_time = 0;
 
4727
 
  /* Find value of sysctl kernel.pid_max */
 
4728
 
  uintmax_t pid_max = 0;
 
4729
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
4730
 
  g_assert_nonnull(sysctl_pid_max);
 
4731
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
4733
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
4735
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
4736
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
4738
 
  __attribute__((cleanup(cleanup_string)))
 
4739
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4740
 
  g_assert_nonnull(tempfilename);
 
4741
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4742
 
  g_assert_cmpint(questionfile, >, 0);
 
4743
 
  FILE *qf = fdopen(questionfile, "w");
 
4744
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4745
 
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
4747
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4749
 
  char *const question_filename = strdup(tempfilename);
 
4750
 
  g_assert_nonnull(question_filename);
 
4751
 
  task_context task = {
 
4752
 
    .func=open_and_parse_question,
 
4753
 
    .question_filename=question_filename,
 
4755
 
    .password=&password,
 
4756
 
    .filename=question_filename,
 
4757
 
    .cancelled_filenames=&cancelled_filenames,
 
4758
 
    .current_time=¤t_time,
 
4759
 
    .mandos_client_exited=&mandos_client_exited,
 
4760
 
    .password_is_read=&password_is_read,
 
4762
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4763
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4765
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4769
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
4770
 
                                         test_fixture *fixture,
 
4771
 
                                         __attribute__((unused))
 
4772
 
                                         gconstpointer user_data){
 
4773
 
  __attribute__((cleanup(cleanup_close)))
 
4774
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4775
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4776
 
  __attribute__((cleanup(string_set_clear)))
 
4777
 
    string_set cancelled_filenames = {};
 
4778
 
  buffer password = {};
 
4779
 
  bool mandos_client_exited = false;
 
4780
 
  bool password_is_read = false;
 
4781
 
  __attribute__((cleanup(cleanup_queue)))
 
4782
 
    task_queue *queue = create_queue();
 
4783
 
  g_assert_nonnull(queue);
 
4784
 
  const mono_microsecs current_time = 0;
 
4786
 
  __attribute__((cleanup(cleanup_string)))
 
4787
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4788
 
  g_assert_nonnull(tempfilename);
 
4789
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4790
 
  g_assert_cmpint(questionfile, >, 0);
 
4791
 
  FILE *qf = fdopen(questionfile, "w");
 
4792
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4793
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
4794
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4796
 
  char *const filename = strdup(tempfilename);
 
4797
 
  g_assert_nonnull(filename);
 
4798
 
  task_context task = {
 
4799
 
    .func=open_and_parse_question,
 
4800
 
    .question_filename=filename,
 
4802
 
    .password=&password,
 
4804
 
    .cancelled_filenames=&cancelled_filenames,
 
4805
 
    .current_time=¤t_time,
 
4806
 
    .mandos_client_exited=&mandos_client_exited,
 
4807
 
    .password_is_read=&password_is_read,
 
4809
 
  task.func(task, queue);
 
4810
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4812
 
  __attribute__((cleanup(cleanup_string)))
 
4813
 
    char *socket_filename = strdup("/nonexistent");
 
4814
 
  g_assert_nonnull(socket_filename);
 
4815
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4816
 
        .func=connect_question_socket,
 
4817
 
        .question_filename=tempfilename,
 
4818
 
        .filename=socket_filename,
 
4820
 
        .password=&password,
 
4821
 
        .current_time=¤t_time,
 
4822
 
        .mandos_client_exited=&mandos_client_exited,
 
4823
 
        .password_is_read=&password_is_read,
 
4826
 
  g_assert_true(queue->next_run != 0);
 
4828
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4832
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
4833
 
                                          test_fixture *fixture,
 
4834
 
                                          __attribute__((unused))
 
4835
 
                                          gconstpointer user_data){
 
4836
 
  __attribute__((cleanup(cleanup_close)))
 
4837
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4838
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4839
 
  __attribute__((cleanup(string_set_clear)))
 
4840
 
    string_set cancelled_filenames = {};
 
4841
 
  buffer password = {};
 
4842
 
  bool mandos_client_exited = false;
 
4843
 
  bool password_is_read = false;
 
4844
 
  __attribute__((cleanup(cleanup_queue)))
 
4845
 
    task_queue *queue = create_queue();
 
4846
 
  g_assert_nonnull(queue);
 
4847
 
  const mono_microsecs current_time = 0;
 
4849
 
  __attribute__((cleanup(cleanup_string)))
 
4850
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4851
 
  g_assert_nonnull(tempfilename);
 
4852
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4853
 
  g_assert_cmpint(questionfile, >, 0);
 
4854
 
  FILE *qf = fdopen(questionfile, "w");
 
4855
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4856
 
                          PRIuMAX "\nNotAfter=\n",
 
4857
 
                          (uintmax_t)getpid()), >, 0);
 
4858
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4860
 
  char *const filename = strdup(tempfilename);
 
4861
 
  g_assert_nonnull(filename);
 
4862
 
  task_context task = {
 
4863
 
    .func=open_and_parse_question,
 
4864
 
    .question_filename=filename,
 
4866
 
    .password=&password,
 
4868
 
    .cancelled_filenames=&cancelled_filenames,
 
4869
 
    .current_time=¤t_time,
 
4870
 
    .mandos_client_exited=&mandos_client_exited,
 
4871
 
    .password_is_read=&password_is_read,
 
4873
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4874
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4876
 
  __attribute__((cleanup(cleanup_string)))
 
4877
 
    char *socket_filename = strdup("/nonexistent");
 
4878
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4879
 
        .func=connect_question_socket,
 
4880
 
        .question_filename=tempfilename,
 
4881
 
        .filename=socket_filename,
 
4883
 
        .password=&password,
 
4884
 
        .current_time=¤t_time,
 
4885
 
        .mandos_client_exited=&mandos_client_exited,
 
4886
 
        .password_is_read=&password_is_read,
 
4888
 
  g_assert_true(queue->next_run != 0);
 
4890
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4894
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
4896
 
                                                  const mono_microsecs
 
4898
 
                                                  const mono_microsecs
 
4900
 
  __attribute__((cleanup(cleanup_close)))
 
4901
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4902
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4903
 
  __attribute__((cleanup(string_set_clear)))
 
4904
 
    string_set cancelled_filenames = {};
 
4905
 
  buffer password = {};
 
4906
 
  bool mandos_client_exited = false;
 
4907
 
  bool password_is_read = false;
 
4908
 
  __attribute__((cleanup(cleanup_queue)))
 
4909
 
    task_queue *queue = create_queue();
 
4910
 
  g_assert_nonnull(queue);
 
4911
 
  queue->next_run = next_queue_run;
 
4913
 
  __attribute__((cleanup(cleanup_string)))
 
4914
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4915
 
  g_assert_nonnull(tempfilename);
 
4916
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4917
 
  g_assert_cmpint(questionfile, >, 0);
 
4918
 
  FILE *qf = fdopen(questionfile, "w");
 
4919
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
4920
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
4921
 
                          (uintmax_t)getpid(), notafter), >, 0);
 
4922
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4924
 
  char *const filename = strdup(tempfilename);
 
4925
 
  g_assert_nonnull(filename);
 
4926
 
  task_context task = {
 
4927
 
    .func=open_and_parse_question,
 
4928
 
    .question_filename=filename,
 
4930
 
    .password=&password,
 
4932
 
    .cancelled_filenames=&cancelled_filenames,
 
4933
 
    .current_time=¤t_time,
 
4934
 
    .mandos_client_exited=&mandos_client_exited,
 
4935
 
    .password_is_read=&password_is_read,
 
4937
 
  task.func(task, queue);
 
4939
 
  if(queue->length >= 1){
 
4940
 
    __attribute__((cleanup(cleanup_string)))
 
4941
 
      char *socket_filename = strdup("/nonexistent");
 
4942
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
4943
 
          .func=connect_question_socket,
 
4944
 
          .filename=socket_filename,
 
4946
 
          .password=&password,
 
4947
 
          .current_time=¤t_time,
 
4948
 
          .cancelled_filenames=&cancelled_filenames,
 
4949
 
          .mandos_client_exited=&mandos_client_exited,
 
4950
 
          .password_is_read=&password_is_read,
 
4952
 
    g_assert_true(queue->next_run != 0);
 
4956
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4957
 
  } else if(current_time >= notafter) {
 
4958
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4960
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
4961
 
          .func=cancel_old_question,
 
4962
 
          .question_filename=tempfilename,
 
4963
 
          .filename=tempfilename,
 
4965
 
          .cancelled_filenames=&cancelled_filenames,
 
4966
 
          .current_time=¤t_time,
 
4969
 
  g_assert_true(queue->next_run == 1);
 
4971
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4975
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
4976
 
                                        test_fixture *fixture,
 
4977
 
                                        __attribute__((unused))
 
4978
 
                                        gconstpointer user_data){
 
4979
 
  /* current_time, notafter, next_queue_run */
 
4980
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
4984
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
4985
 
                                        test_fixture *fixture,
 
4986
 
                                        __attribute__((unused))
 
4987
 
                                        gconstpointer user_data){
 
4988
 
  /* current_time, notafter, next_queue_run */
 
4989
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
4993
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
4994
 
                                          test_fixture *fixture,
 
4995
 
                                          __attribute__((unused))
 
4996
 
                                          gconstpointer user_data){
 
4997
 
  /* current_time, notafter, next_queue_run */
 
4998
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
5002
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
5003
 
                                          test_fixture *fixture,
 
5004
 
                                          __attribute__((unused))
 
5005
 
                                          gconstpointer user_data){
 
5006
 
  /* current_time, notafter, next_queue_run */
 
5007
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
5011
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
5012
 
                                            test_fixture *fixture,
 
5013
 
                                            __attribute__((unused))
 
5014
 
                                            gconstpointer user_data){
 
5015
 
  /* current_time, notafter, next_queue_run */
 
5016
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
5020
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
5021
 
                                           test_fixture *fixture,
 
5022
 
                                           __attribute__((unused))
 
5023
 
                                           gconstpointer user_data){
 
5024
 
  /* current_time, notafter, next_queue_run */
 
5025
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
5028
 
static void assert_cancel_old_question_param(const mono_microsecs
 
5030
 
                                             const mono_microsecs
 
5032
 
                                             const mono_microsecs
 
5034
 
                                             const mono_microsecs
 
5036
 
  __attribute__((cleanup(string_set_clear)))
 
5037
 
    string_set cancelled_filenames = {};
 
5038
 
  __attribute__((cleanup(cleanup_queue)))
 
5039
 
    task_queue *queue = create_queue();
 
5040
 
  g_assert_nonnull(queue);
 
5041
 
  queue->next_run = next_queue_run;
 
5043
 
  char *const question_filename = strdup("/nonexistent");
 
5044
 
  g_assert_nonnull(question_filename);
 
5045
 
  task_context task = {
 
5046
 
    .func=cancel_old_question,
 
5047
 
    .question_filename=question_filename,
 
5048
 
    .filename=question_filename,
 
5050
 
    .cancelled_filenames=&cancelled_filenames,
 
5051
 
    .current_time=¤t_time,
 
5053
 
  task.func(task, queue);
 
5055
 
  if(current_time >= notafter){
 
5056
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5057
 
    g_assert_true(string_set_contains(cancelled_filenames,
 
5060
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5061
 
          .func=cancel_old_question,
 
5062
 
          .question_filename=question_filename,
 
5063
 
          .filename=question_filename,
 
5065
 
          .cancelled_filenames=&cancelled_filenames,
 
5066
 
          .current_time=¤t_time,
 
5069
 
    g_assert_false(string_set_contains(cancelled_filenames,
 
5070
 
                                       question_filename));
 
5072
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
5073
 
                   (unsigned int)next_set_to);
 
5076
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
5077
 
                                           test_fixture *fixture,
 
5078
 
                                           __attribute__((unused))
 
5079
 
                                           gconstpointer user_data){
 
5080
 
  /* next_queue_run unset,
 
5081
 
     cancellation should happen because time has come,
 
5082
 
     next_queue_run should be unchanged */
 
5083
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5084
 
  assert_cancel_old_question_param(0, 1, 2, 0);
 
5087
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
5088
 
                                           test_fixture *fixture,
 
5089
 
                                           __attribute__((unused))
 
5090
 
                                           gconstpointer user_data){
 
5091
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
5092
 
     and current_time is not yet notafter or greater,
 
5093
 
     update value of next_queue_run to value of notafter */
 
5094
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5095
 
  assert_cancel_old_question_param(0, 2, 1, 2);
 
5098
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
5099
 
                                           test_fixture *fixture,
 
5100
 
                                           __attribute__((unused))
 
5101
 
                                           gconstpointer user_data){
 
5102
 
  /* next_queue_run 1,
 
5103
 
     cancellation should happen because time has come,
 
5104
 
     next_queue_run should be unchanged */
 
5105
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5106
 
  assert_cancel_old_question_param(1, 2, 3, 1);
 
5109
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
5110
 
                                           test_fixture *fixture,
 
5111
 
                                           __attribute__((unused))
 
5112
 
                                           gconstpointer user_data){
 
5113
 
  /* If next_queue_run is set,
 
5114
 
     and current_time is not yet notafter or greater,
 
5115
 
     and notafter is larger than next_queue_run
 
5116
 
     next_queue_run should be unchanged */
 
5117
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5118
 
  assert_cancel_old_question_param(1, 3, 2, 1);
 
5121
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
5122
 
                                           test_fixture *fixture,
 
5123
 
                                           __attribute__((unused))
 
5124
 
                                           gconstpointer user_data){
 
5125
 
  /* next_queue_run 2,
 
5126
 
     cancellation should happen because time has come,
 
5127
 
     next_queue_run should be unchanged */
 
5128
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5129
 
  assert_cancel_old_question_param(2, 1, 3, 2);
 
5132
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
5133
 
                                           test_fixture *fixture,
 
5134
 
                                           __attribute__((unused))
 
5135
 
                                           gconstpointer user_data){
 
5136
 
  /* If next_queue_run is set,
 
5137
 
     and current_time is not yet notafter or greater,
 
5138
 
     and notafter is larger than next_queue_run
 
5139
 
     next_queue_run should be unchanged */
 
5140
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5141
 
  assert_cancel_old_question_param(2, 3, 1, 2);
 
5144
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
5145
 
                                           test_fixture *fixture,
 
5146
 
                                           __attribute__((unused))
 
5147
 
                                           gconstpointer user_data){
 
5148
 
  /* next_queue_run 3,
 
5149
 
     cancellation should happen because time has come,
 
5150
 
     next_queue_run should be unchanged */
 
5151
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5152
 
  assert_cancel_old_question_param(3, 1, 2, 3);
 
5155
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
5156
 
                                           test_fixture *fixture,
 
5157
 
                                           __attribute__((unused))
 
5158
 
                                           gconstpointer user_data){
 
5159
 
  /* If next_queue_run is set,
 
5160
 
     and current_time is not yet notafter or greater,
 
5161
 
     and notafter is smaller than next_queue_run
 
5162
 
     update value of next_queue_run to value of notafter */
 
5163
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5164
 
  assert_cancel_old_question_param(3, 2, 1, 2);
 
5168
 
test_connect_question_socket_name_too_long(__attribute__((unused))
 
5169
 
                                           test_fixture *fixture,
 
5170
 
                                           __attribute__((unused))
 
5171
 
                                           gconstpointer user_data){
 
5172
 
  __attribute__((cleanup(cleanup_close)))
 
5173
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5174
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5175
 
  const char question_filename[] = "/nonexistent/question";
 
5176
 
  __attribute__((cleanup(string_set_clear)))
 
5177
 
    string_set cancelled_filenames = {};
 
5178
 
  __attribute__((cleanup(cleanup_queue)))
 
5179
 
    task_queue *queue = create_queue();
 
5180
 
  g_assert_nonnull(queue);
 
5181
 
  __attribute__((cleanup(cleanup_string)))
 
5182
 
    char *tempdir = make_temporary_directory();
 
5183
 
  g_assert_nonnull(tempdir);
 
5184
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
5185
 
  char socket_name[sizeof(unix_socket.sun_path)];
 
5186
 
  memset(socket_name, 'x', sizeof(socket_name));
 
5187
 
  socket_name[sizeof(socket_name)-1] = '\0';
 
5188
 
  char *filename = NULL;
 
5189
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5191
 
  g_assert_nonnull(filename);
 
5193
 
  task_context task = {
 
5194
 
    .func=connect_question_socket,
 
5195
 
    .question_filename=strdup(question_filename),
 
5197
 
    .password=(buffer[]){{}},
 
5199
 
    .cancelled_filenames=&cancelled_filenames,
 
5200
 
    .mandos_client_exited=(bool[]){false},
 
5201
 
    .password_is_read=(bool[]){false},
 
5202
 
    .current_time=(mono_microsecs[]){0},
 
5204
 
  g_assert_nonnull(task.question_filename);
 
5205
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5207
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5208
 
                                    question_filename));
 
5209
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5210
 
  g_assert_true(queue->next_run == 0);
 
5212
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5216
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
5217
 
                                               test_fixture *fixture,
 
5218
 
                                               __attribute__((unused))
 
5221
 
  __attribute__((cleanup(cleanup_close)))
 
5222
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5223
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5224
 
  const char question_filename[] = "/nonexistent/question";
 
5225
 
  __attribute__((cleanup(string_set_clear)))
 
5226
 
    string_set cancelled_filenames = {};
 
5227
 
  const mono_microsecs current_time = 3;
 
5228
 
  __attribute__((cleanup(cleanup_queue)))
 
5229
 
    task_queue *queue = create_queue();
 
5230
 
  g_assert_nonnull(queue);
 
5231
 
  __attribute__((cleanup(cleanup_string)))
 
5232
 
    char *tempdir = make_temporary_directory();
 
5233
 
  g_assert_nonnull(tempdir);
 
5234
 
  char socket_name[] = "nonexistent";
 
5235
 
  char *filename = NULL;
 
5236
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5238
 
  g_assert_nonnull(filename);
 
5240
 
  task_context task = {
 
5241
 
    .func=connect_question_socket,
 
5242
 
    .question_filename=strdup(question_filename),
 
5244
 
    .password=(buffer[]){{}},
 
5246
 
    .cancelled_filenames=&cancelled_filenames,
 
5247
 
    .mandos_client_exited=(bool[]){false},
 
5248
 
    .password_is_read=(bool[]){false},
 
5249
 
    .current_time=¤t_time,
 
5251
 
  g_assert_nonnull(task.question_filename);
 
5252
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5254
 
  g_assert_nonnull(find_matching_task(queue, task));
 
5256
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
5257
 
                                     question_filename));
 
5258
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5259
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5261
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5265
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
5266
 
                                            test_fixture *fixture,
 
5267
 
                                            __attribute__((unused))
 
5268
 
                                            gconstpointer user_data){
 
5269
 
  __attribute__((cleanup(cleanup_close)))
 
5270
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5271
 
  __attribute__((cleanup(cleanup_string)))
 
5272
 
    char *const question_filename = strdup("/nonexistent/question");
 
5273
 
  g_assert_nonnull(question_filename);
 
5274
 
  __attribute__((cleanup(string_set_clear)))
 
5275
 
    string_set cancelled_filenames = {};
 
5276
 
  const mono_microsecs current_time = 5;
 
5277
 
  __attribute__((cleanup(cleanup_queue)))
 
5278
 
    task_queue *queue = create_queue();
 
5279
 
  g_assert_nonnull(queue);
 
5280
 
  __attribute__((cleanup(cleanup_string)))
 
5281
 
    char *tempdir = make_temporary_directory();
 
5282
 
  g_assert_nonnull(tempdir);
 
5283
 
  __attribute__((cleanup(cleanup_close)))
 
5284
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5285
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5286
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5287
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5288
 
  const char socket_name[] = "socket_name";
 
5289
 
  __attribute__((cleanup(cleanup_string)))
 
5290
 
    char *filename = NULL;
 
5291
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5293
 
  g_assert_nonnull(filename);
 
5294
 
  g_assert_cmpint((int)strlen(filename), <,
 
5295
 
                  (int)sizeof(sock_name.sun_path));
 
5296
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5297
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5298
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5299
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5300
 
  task_context task = {
 
5301
 
    .func=connect_question_socket,
 
5302
 
    .question_filename=strdup(question_filename),
 
5304
 
    .password=(buffer[]){{}},
 
5305
 
    .filename=strdup(filename),
 
5306
 
    .cancelled_filenames=&cancelled_filenames,
 
5307
 
    .mandos_client_exited=(bool[]){false},
 
5308
 
    .password_is_read=(bool[]){false},
 
5309
 
    .current_time=¤t_time,
 
5311
 
  g_assert_nonnull(task.question_filename);
 
5312
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5314
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5315
 
  const task_context *const added_task
 
5316
 
    = find_matching_task(queue, task);
 
5317
 
  g_assert_nonnull(added_task);
 
5318
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5320
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5321
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5325
 
void test_connect_question_socket_usable(__attribute__((unused))
 
5326
 
                                         test_fixture *fixture,
 
5327
 
                                         __attribute__((unused))
 
5328
 
                                         gconstpointer user_data){
 
5329
 
  __attribute__((cleanup(cleanup_close)))
 
5330
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5331
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5332
 
  __attribute__((cleanup(cleanup_string)))
 
5333
 
    char *const question_filename = strdup("/nonexistent/question");
 
5334
 
  g_assert_nonnull(question_filename);
 
5335
 
  __attribute__((cleanup(string_set_clear)))
 
5336
 
    string_set cancelled_filenames = {};
 
5337
 
  buffer password = {};
 
5338
 
  bool mandos_client_exited = false;
 
5339
 
  bool password_is_read = false;
 
5340
 
  const mono_microsecs current_time = 0;
 
5341
 
  __attribute__((cleanup(cleanup_queue)))
 
5342
 
    task_queue *queue = create_queue();
 
5343
 
  g_assert_nonnull(queue);
 
5344
 
  __attribute__((cleanup(cleanup_string)))
 
5345
 
    char *tempdir = make_temporary_directory();
 
5346
 
  g_assert_nonnull(tempdir);
 
5347
 
  __attribute__((cleanup(cleanup_close)))
 
5348
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5349
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5350
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5351
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5352
 
  const char socket_name[] = "socket_name";
 
5353
 
  __attribute__((cleanup(cleanup_string)))
 
5354
 
    char *filename = NULL;
 
5355
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5357
 
  g_assert_nonnull(filename);
 
5358
 
  g_assert_cmpint((int)strlen(filename), <,
 
5359
 
                  (int)sizeof(sock_name.sun_path));
 
5360
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5361
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5362
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5363
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5364
 
  task_context task = {
 
5365
 
    .func=connect_question_socket,
 
5366
 
    .question_filename=strdup(question_filename),
 
5368
 
    .password=&password,
 
5369
 
    .filename=strdup(filename),
 
5370
 
    .cancelled_filenames=&cancelled_filenames,
 
5371
 
    .mandos_client_exited=&mandos_client_exited,
 
5372
 
    .password_is_read=&password_is_read,
 
5373
 
    .current_time=¤t_time,
 
5375
 
  g_assert_nonnull(task.question_filename);
 
5376
 
  task.func(task, queue);
 
5378
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5379
 
  const task_context *const added_task
 
5380
 
    = find_matching_task(queue, (task_context){
 
5381
 
        .func=send_password_to_socket,
 
5382
 
        .question_filename=question_filename,
 
5385
 
        .password=&password,
 
5386
 
        .cancelled_filenames=&cancelled_filenames,
 
5387
 
        .mandos_client_exited=&mandos_client_exited,
 
5388
 
        .password_is_read=&password_is_read,
 
5389
 
        .current_time=¤t_time,
 
5391
 
  g_assert_nonnull(added_task);
 
5392
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5394
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5397
 
  const int fd = added_task->fd;
 
5398
 
  g_assert_cmpint(fd, >, 0);
 
5399
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
5402
 
  char write_data[PIPE_BUF];
 
5404
 
    /* Construct test password buffer */
 
5405
 
    /* Start with + since that is what the real procotol uses */
 
5406
 
    write_data[0] = '+';
 
5407
 
    /* Set a special character at string end just to mark the end */
 
5408
 
    write_data[sizeof(write_data)-2] = 'y';
 
5409
 
    /* Set NUL at buffer end, as suggested by the protocol */
 
5410
 
    write_data[sizeof(write_data)-1] = '\0';
 
5411
 
    /* Fill rest of password with 'x' */
 
5412
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
5413
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
5414
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
5417
 
  /* read from sock_fd */
 
5418
 
  char read_data[sizeof(write_data)];
 
5419
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
5420
 
                  ==, sizeof(read_data));
 
5422
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
5425
 
  /* writing to sock_fd should fail */
 
5426
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
5427
 
                       MSG_NOSIGNAL), <, 0);
 
5429
 
  /* reading from fd should fail */
 
5430
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
5431
 
                            MSG_NOSIGNAL), <, 0);
 
5433
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5434
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5438
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
5439
 
                                               test_fixture *fixture,
 
5440
 
                                               __attribute__((unused))
 
5443
 
  __attribute__((cleanup(cleanup_close)))
 
5444
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5445
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5446
 
  __attribute__((cleanup(cleanup_string)))
 
5447
 
    char *const question_filename = strdup("/nonexistent/question");
 
5448
 
  g_assert_nonnull(question_filename);
 
5449
 
  __attribute__((cleanup(cleanup_string)))
 
5450
 
    char *const filename = strdup("/nonexistent/socket");
 
5451
 
  g_assert_nonnull(filename);
 
5452
 
  __attribute__((cleanup(string_set_clear)))
 
5453
 
    string_set cancelled_filenames = {};
 
5454
 
  buffer password = {};
 
5455
 
  bool password_is_read = true;
 
5456
 
  __attribute__((cleanup(cleanup_queue)))
 
5457
 
    task_queue *queue = create_queue();
 
5458
 
  g_assert_nonnull(queue);
 
5460
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5461
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5463
 
  __attribute__((cleanup(cleanup_close)))
 
5464
 
    const int read_socket = socketfds[0];
 
5465
 
  const int write_socket = socketfds[1];
 
5466
 
  task_context task = {
 
5467
 
    .func=send_password_to_socket,
 
5468
 
    .question_filename=strdup(question_filename),
 
5469
 
    .filename=strdup(filename),
 
5472
 
    .password=&password,
 
5473
 
    .cancelled_filenames=&cancelled_filenames,
 
5474
 
    .mandos_client_exited=(bool[]){false},
 
5475
 
    .password_is_read=&password_is_read,
 
5476
 
    .current_time=(mono_microsecs[]){0},
 
5478
 
  g_assert_nonnull(task.question_filename);
 
5480
 
  task.func(task, queue);
 
5482
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5484
 
  const task_context *const added_task
 
5485
 
    = find_matching_task(queue, task);
 
5486
 
  g_assert_nonnull(added_task);
 
5487
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5488
 
  g_assert_true(password_is_read);
 
5490
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5491
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5496
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
5497
 
                                               test_fixture *fixture,
 
5498
 
                                               __attribute__((unused))
 
5501
 
  __attribute__((cleanup(cleanup_close)))
 
5502
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5503
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5504
 
  __attribute__((cleanup(cleanup_string)))
 
5505
 
    char *const question_filename = strdup("/nonexistent/question");
 
5506
 
  g_assert_nonnull(question_filename);
 
5507
 
  __attribute__((cleanup(cleanup_string)))
 
5508
 
    char *const filename = strdup("/nonexistent/socket");
 
5509
 
  __attribute__((cleanup(string_set_clear)))
 
5510
 
    string_set cancelled_filenames = {};
 
5511
 
  buffer password = {};
 
5512
 
  __attribute__((cleanup(cleanup_queue)))
 
5513
 
    task_queue *queue = create_queue();
 
5514
 
  g_assert_nonnull(queue);
 
5516
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5517
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5519
 
  __attribute__((cleanup(cleanup_close)))
 
5520
 
    const int read_socket = socketfds[0];
 
5521
 
  const int write_socket = socketfds[1];
 
5522
 
  task_context task = {
 
5523
 
    .func=send_password_to_socket,
 
5524
 
    .question_filename=strdup(question_filename),
 
5525
 
    .filename=strdup(filename),
 
5528
 
    .password=&password,
 
5529
 
    .cancelled_filenames=&cancelled_filenames,
 
5530
 
    .mandos_client_exited=(bool[]){false},
 
5531
 
    .password_is_read=(bool[]){false},
 
5532
 
    .current_time=(mono_microsecs[]){0},
 
5534
 
  g_assert_nonnull(task.question_filename);
 
5536
 
  task.func(task, queue);
 
5538
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5540
 
  const task_context *const added_task = find_matching_task(queue,
 
5542
 
  g_assert_nonnull(added_task);
 
5543
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5544
 
  g_assert_true(queue->next_run == 0);
 
5546
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5547
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5552
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
5553
 
                                           test_fixture *fixture,
 
5554
 
                                           __attribute__((unused))
 
5555
 
                                           gconstpointer user_data){
 
5556
 
  __attribute__((cleanup(cleanup_close)))
 
5557
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5558
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5559
 
  const char question_filename[] = "/nonexistent/question";
 
5560
 
  char *const filename = strdup("/nonexistent/socket");
 
5561
 
  __attribute__((cleanup(string_set_clear)))
 
5562
 
    string_set cancelled_filenames = {};
 
5563
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5564
 
  __attribute__((cleanup(cleanup_buffer)))
 
5566
 
    .data=malloc(oversized),
 
5568
 
    .allocated=oversized,
 
5570
 
  g_assert_nonnull(password.data);
 
5571
 
  if(mlock(password.data, password.allocated) != 0){
 
5572
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5574
 
  /* Construct test password buffer */
 
5575
 
  /* Start with + since that is what the real procotol uses */
 
5576
 
  password.data[0] = '+';
 
5577
 
  /* Set a special character at string end just to mark the end */
 
5578
 
  password.data[oversized-3] = 'y';
 
5579
 
  /* Set NUL at buffer end, as suggested by the protocol */
 
5580
 
  password.data[oversized-2] = '\0';
 
5581
 
  /* Fill rest of password with 'x' */
 
5582
 
  memset(password.data+1, 'x', oversized-3);
 
5584
 
  __attribute__((cleanup(cleanup_queue)))
 
5585
 
    task_queue *queue = create_queue();
 
5586
 
  g_assert_nonnull(queue);
 
5588
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5589
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5591
 
  __attribute__((cleanup(cleanup_close)))
 
5592
 
    const int read_socket = socketfds[0];
 
5593
 
  __attribute__((cleanup(cleanup_close)))
 
5594
 
    const int write_socket = socketfds[1];
 
5595
 
  task_context task = {
 
5596
 
    .func=send_password_to_socket,
 
5597
 
    .question_filename=strdup(question_filename),
 
5601
 
    .password=&password,
 
5602
 
    .cancelled_filenames=&cancelled_filenames,
 
5603
 
    .mandos_client_exited=(bool[]){true},
 
5604
 
    .password_is_read=(bool[]){true},
 
5605
 
    .current_time=(mono_microsecs[]){0},
 
5607
 
  g_assert_nonnull(task.question_filename);
 
5609
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5611
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5612
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5613
 
                                    question_filename));
 
5616
 
static void test_send_password_to_socket_retry(__attribute__((unused))
 
5617
 
                                               test_fixture *fixture,
 
5618
 
                                               __attribute__((unused))
 
5621
 
  __attribute__((cleanup(cleanup_close)))
 
5622
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5623
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5624
 
  __attribute__((cleanup(cleanup_string)))
 
5625
 
    char *const question_filename = strdup("/nonexistent/question");
 
5626
 
  g_assert_nonnull(question_filename);
 
5627
 
  __attribute__((cleanup(cleanup_string)))
 
5628
 
    char *const filename = strdup("/nonexistent/socket");
 
5629
 
  g_assert_nonnull(filename);
 
5630
 
  __attribute__((cleanup(string_set_clear)))
 
5631
 
    string_set cancelled_filenames = {};
 
5632
 
  __attribute__((cleanup(cleanup_buffer)))
 
5633
 
    buffer password = {};
 
5635
 
  __attribute__((cleanup(cleanup_queue)))
 
5636
 
    task_queue *queue = create_queue();
 
5637
 
  g_assert_nonnull(queue);
 
5639
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5640
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5642
 
  __attribute__((cleanup(cleanup_close)))
 
5643
 
    const int read_socket = socketfds[0];
 
5644
 
  const int write_socket = socketfds[1];
 
5645
 
  /* Close the server side socket to force ECONNRESET on client */
 
5646
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
5647
 
  task_context task = {
 
5648
 
    .func=send_password_to_socket,
 
5649
 
    .question_filename=strdup(question_filename),
 
5650
 
    .filename=strdup(filename),
 
5653
 
    .password=&password,
 
5654
 
    .cancelled_filenames=&cancelled_filenames,
 
5655
 
    .mandos_client_exited=(bool[]){true},
 
5656
 
    .password_is_read=(bool[]){true},
 
5657
 
    .current_time=(mono_microsecs[]){0},
 
5659
 
  g_assert_nonnull(task.question_filename);
 
5661
 
  task.func(task, queue);
 
5663
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5665
 
  const task_context *const added_task = find_matching_task(queue,
 
5667
 
  g_assert_nonnull(added_task);
 
5668
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5670
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5675
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
5676
 
                                            test_fixture *fixture,
 
5677
 
                                            __attribute__((unused))
 
5678
 
                                            gconstpointer user_data){
 
5679
 
  __attribute__((cleanup(cleanup_close)))
 
5680
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5681
 
  __attribute__((cleanup(cleanup_string)))
 
5682
 
    char *const question_filename = strdup("/nonexistent/question");
 
5683
 
  g_assert_nonnull(question_filename);
 
5684
 
  __attribute__((cleanup(cleanup_string)))
 
5685
 
    char *const filename = strdup("/nonexistent/socket");
 
5686
 
  g_assert_nonnull(filename);
 
5687
 
  __attribute__((cleanup(string_set_clear)))
 
5688
 
    string_set cancelled_filenames = {};
 
5689
 
  __attribute__((cleanup(cleanup_buffer)))
 
5690
 
    buffer password = {};
 
5692
 
  const mono_microsecs current_time = 11;
 
5693
 
  __attribute__((cleanup(cleanup_queue)))
 
5694
 
    task_queue *queue = create_queue();
 
5695
 
  g_assert_nonnull(queue);
 
5697
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5698
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5700
 
  __attribute__((cleanup(cleanup_close)))
 
5701
 
    const int read_socket = socketfds[0];
 
5702
 
  const int write_socket = socketfds[1];
 
5703
 
  /* Close the server side socket to force ECONNRESET on client */
 
5704
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
5705
 
  task_context task = {
 
5706
 
    .func=send_password_to_socket,
 
5707
 
    .question_filename=strdup(question_filename),
 
5708
 
    .filename=strdup(filename),
 
5711
 
    .password=&password,
 
5712
 
    .cancelled_filenames=&cancelled_filenames,
 
5713
 
    .mandos_client_exited=(bool[]){true},
 
5714
 
    .password_is_read=(bool[]){true},
 
5715
 
    .current_time=¤t_time,
 
5717
 
  g_assert_nonnull(task.question_filename);
 
5719
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5721
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5723
 
  const task_context *const added_task = find_matching_task(queue,
 
5725
 
  g_assert_nonnull(added_task);
 
5726
 
  g_assert_true(queue->next_run == current_time + 1000000);
 
5727
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5730
 
static void assert_send_password_to_socket_password(buffer password){
 
5731
 
  __attribute__((cleanup(cleanup_close)))
 
5732
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5733
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5734
 
  char *const question_filename = strdup("/nonexistent/question");
 
5735
 
  g_assert_nonnull(question_filename);
 
5736
 
  char *const filename = strdup("/nonexistent/socket");
 
5737
 
  g_assert_nonnull(filename);
 
5738
 
  __attribute__((cleanup(string_set_clear)))
 
5739
 
    string_set cancelled_filenames = {};
 
5741
 
  __attribute__((cleanup(cleanup_queue)))
 
5742
 
    task_queue *queue = create_queue();
 
5743
 
  g_assert_nonnull(queue);
 
5745
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5746
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5748
 
  __attribute__((cleanup(cleanup_close)))
 
5749
 
    const int read_socket = socketfds[0];
 
5750
 
  const int write_socket = socketfds[1];
 
5751
 
  task_context task = {
 
5752
 
    .func=send_password_to_socket,
 
5753
 
    .question_filename=question_filename,
 
5757
 
    .password=&password,
 
5758
 
    .cancelled_filenames=&cancelled_filenames,
 
5759
 
    .mandos_client_exited=(bool[]){true},
 
5760
 
    .password_is_read=(bool[]){true},
 
5761
 
    .current_time=(mono_microsecs[]){0},
 
5764
 
  char *expected_written_data = malloc(password.length + 2);
 
5765
 
  g_assert_nonnull(expected_written_data);
 
5766
 
  expected_written_data[0] = '+';
 
5767
 
  expected_written_data[password.length + 1] = '\0';
 
5768
 
  if(password.length > 0){
 
5769
 
    g_assert_nonnull(password.data);
 
5770
 
    memcpy(expected_written_data + 1, password.data, password.length);
 
5773
 
  task.func(task, queue);
 
5776
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
5777
 
                  (int)(password.length + 2));
 
5778
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5780
 
  g_assert_true(memcmp(expected_written_data, buf,
 
5781
 
                       password.length + 2) == 0);
 
5783
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
5785
 
  free(expected_written_data);
 
5789
 
test_send_password_to_socket_null_password(__attribute__((unused))
 
5790
 
                                           test_fixture *fixture,
 
5791
 
                                           __attribute__((unused))
 
5792
 
                                           gconstpointer user_data){
 
5793
 
  __attribute__((cleanup(cleanup_buffer)))
 
5794
 
    buffer password = {};
 
5795
 
  assert_send_password_to_socket_password(password);
 
5799
 
test_send_password_to_socket_empty_password(__attribute__((unused))
 
5800
 
                                            test_fixture *fixture,
 
5801
 
                                            __attribute__((unused))
 
5802
 
                                            gconstpointer user_data){
 
5803
 
  __attribute__((cleanup(cleanup_buffer)))
 
5805
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
5807
 
    .allocated=0,               /* deliberate lie */
 
5809
 
  g_assert_nonnull(password.data);
 
5810
 
  assert_send_password_to_socket_password(password);
 
5814
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
5815
 
                                            test_fixture *fixture,
 
5816
 
                                            __attribute__((unused))
 
5817
 
                                            gconstpointer user_data){
 
5818
 
  __attribute__((cleanup(cleanup_buffer)))
 
5824
 
  if(mlock(password.data, password.allocated) != 0){
 
5825
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5827
 
  assert_send_password_to_socket_password(password);
 
5831
 
test_send_password_to_socket_text_password(__attribute__((unused))
 
5832
 
                                           test_fixture *fixture,
 
5833
 
                                           __attribute__((unused))
 
5834
 
                                           gconstpointer user_data){
 
5835
 
  const char dummy_test_password[] = "dummy test password";
 
5836
 
  __attribute__((cleanup(cleanup_buffer)))
 
5838
 
    .data = strdup(dummy_test_password),
 
5839
 
    .length = strlen(dummy_test_password),
 
5840
 
    .allocated = sizeof(dummy_test_password),
 
5842
 
  if(mlock(password.data, password.allocated) != 0){
 
5843
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5845
 
  assert_send_password_to_socket_password(password);
 
5849
 
test_send_password_to_socket_binary_password(__attribute__((unused))
 
5850
 
                                             test_fixture *fixture,
 
5851
 
                                             __attribute__((unused))
 
5852
 
                                             gconstpointer user_data){
 
5853
 
  __attribute__((cleanup(cleanup_buffer)))
 
5859
 
  g_assert_nonnull(password.data);
 
5860
 
  if(mlock(password.data, password.allocated) != 0){
 
5861
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5863
 
  char c = 1;                   /* Start at 1, avoiding NUL */
 
5864
 
  for(int i=0; i < 255; i++){
 
5865
 
    password.data[i] = c++;
 
5867
 
  assert_send_password_to_socket_password(password);
 
5871
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
5872
 
                                              test_fixture *fixture,
 
5873
 
                                              __attribute__((unused))
 
5876
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
5877
 
  __attribute__((cleanup(cleanup_buffer)))
 
5879
 
    .data=malloc(sizeof(test_password)),
 
5880
 
    .length=sizeof(test_password),
 
5881
 
    .allocated=sizeof(test_password),
 
5883
 
  g_assert_nonnull(password.data);
 
5884
 
  if(mlock(password.data, password.allocated) !=0){
 
5885
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5887
 
  memcpy(password.data, test_password, password.allocated);
 
5888
 
  assert_send_password_to_socket_password(password);
 
5891
 
static bool assert_add_existing_questions_to_devnull(task_queue
 
5904
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
5905
 
                                               test_fixture *fixture,
 
5906
 
                                               __attribute__((unused))
 
5909
 
  __attribute__((cleanup(cleanup_queue)))
 
5910
 
    task_queue *queue = create_queue();
 
5911
 
  g_assert_nonnull(queue);
 
5912
 
  __attribute__((cleanup(cleanup_close)))
 
5913
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5914
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5915
 
  __attribute__((cleanup(string_set_clear)))
 
5916
 
    string_set cancelled_filenames = {};
 
5918
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
5921
 
                  (buffer[]){{}}, /* password */
 
5922
 
                  &cancelled_filenames,
 
5923
 
                  (mono_microsecs[]){0}, /* current_time */
 
5924
 
                  (bool[]){false},       /* mandos_client_exited */
 
5925
 
                  (bool[]){false},       /* password_is_read */
 
5926
 
                  "/nonexistent"));      /* dirname */
 
5928
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5932
 
bool assert_add_existing_questions_to_devnull(task_queue
 
5939
 
                                              *cancelled_filenames,
 
5940
 
                                              const mono_microsecs
 
5941
 
                                              *const current_time,
 
5943
 
                                              mandos_client_exited,
 
5948
 
  __attribute__((cleanup(cleanup_close)))
 
5949
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5950
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
5951
 
  __attribute__((cleanup(cleanup_close)))
 
5952
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
5953
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
5954
 
  dup2(devnull_fd, STDERR_FILENO);
 
5955
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
5956
 
                                          cancelled_filenames,
 
5958
 
                                          mandos_client_exited,
 
5959
 
                                          password_is_read, dirname);
 
5960
 
  dup2(real_stderr_fd, STDERR_FILENO);
 
5965
 
void test_add_existing_questions_no_questions(__attribute__((unused))
 
5966
 
                                              test_fixture *fixture,
 
5967
 
                                              __attribute__((unused))
 
5970
 
  __attribute__((cleanup(cleanup_queue)))
 
5971
 
    task_queue *queue = create_queue();
 
5972
 
  g_assert_nonnull(queue);
 
5973
 
  __attribute__((cleanup(cleanup_close)))
 
5974
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5975
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5976
 
  __attribute__((cleanup(string_set_clear)))
 
5977
 
    string_set cancelled_filenames = {};
 
5978
 
  __attribute__((cleanup(cleanup_string)))
 
5979
 
    char *tempdir = make_temporary_directory();
 
5980
 
  g_assert_nonnull(tempdir);
 
5982
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
5985
 
                  (buffer[]){{}}, /* password */
 
5986
 
                  &cancelled_filenames,
 
5987
 
                  (mono_microsecs[]){0}, /* current_time */
 
5988
 
                  (bool[]){false},       /* mandos_client_exited */
 
5989
 
                  (bool[]){false},       /* password_is_read */
 
5992
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5994
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5997
 
static char *make_question_file_in_directory(const char *const);
 
6000
 
void test_add_existing_questions_one_question(__attribute__((unused))
 
6001
 
                                              test_fixture *fixture,
 
6002
 
                                              __attribute__((unused))
 
6005
 
  __attribute__((cleanup(cleanup_queue)))
 
6006
 
    task_queue *queue = create_queue();
 
6007
 
  g_assert_nonnull(queue);
 
6008
 
  __attribute__((cleanup(cleanup_close)))
 
6009
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6010
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6011
 
  __attribute__((cleanup(cleanup_buffer)))
 
6012
 
    buffer password = {};
 
6013
 
  __attribute__((cleanup(string_set_clear)))
 
6014
 
    string_set cancelled_filenames = {};
 
6015
 
  const mono_microsecs current_time = 0;
 
6016
 
  bool mandos_client_exited = false;
 
6017
 
  bool password_is_read = false;
 
6018
 
  __attribute__((cleanup(cleanup_string)))
 
6019
 
    char *tempdir = make_temporary_directory();
 
6020
 
  g_assert_nonnull(tempdir);
 
6021
 
  __attribute__((cleanup(cleanup_string)))
 
6022
 
    char *question_filename
 
6023
 
    = make_question_file_in_directory(tempdir);
 
6024
 
  g_assert_nonnull(question_filename);
 
6026
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6030
 
                 &cancelled_filenames,
 
6032
 
                 &mandos_client_exited,
 
6036
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6038
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6039
 
        .func=open_and_parse_question,
 
6041
 
        .filename=question_filename,
 
6042
 
        .question_filename=question_filename,
 
6043
 
        .password=&password,
 
6044
 
        .cancelled_filenames=&cancelled_filenames,
 
6045
 
        .current_time=¤t_time,
 
6046
 
        .mandos_client_exited=&mandos_client_exited,
 
6047
 
        .password_is_read=&password_is_read,
 
6050
 
  g_assert_true(queue->next_run == 1);
 
6052
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6053
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6056
 
static char *make_question_file_in_directory(const char
 
6058
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
6062
 
void test_add_existing_questions_two_questions(__attribute__((unused))
 
6063
 
                                               test_fixture *fixture,
 
6064
 
                                               __attribute__((unused))
 
6067
 
  __attribute__((cleanup(cleanup_queue)))
 
6068
 
    task_queue *queue = create_queue();
 
6069
 
  g_assert_nonnull(queue);
 
6070
 
  __attribute__((cleanup(cleanup_close)))
 
6071
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6072
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6073
 
  __attribute__((cleanup(cleanup_buffer)))
 
6074
 
    buffer password = {};
 
6075
 
  __attribute__((cleanup(string_set_clear)))
 
6076
 
    string_set cancelled_filenames = {};
 
6077
 
  const mono_microsecs current_time = 0;
 
6078
 
  bool mandos_client_exited = false;
 
6079
 
  bool password_is_read = false;
 
6080
 
  __attribute__((cleanup(cleanup_string)))
 
6081
 
    char *tempdir = make_temporary_directory();
 
6082
 
  g_assert_nonnull(tempdir);
 
6083
 
  __attribute__((cleanup(cleanup_string)))
 
6084
 
    char *question_filename1
 
6085
 
    = make_question_file_in_directory(tempdir);
 
6086
 
  g_assert_nonnull(question_filename1);
 
6087
 
  __attribute__((cleanup(cleanup_string)))
 
6088
 
    char *question_filename2
 
6089
 
    = make_question_file_in_directory(tempdir);
 
6090
 
  g_assert_nonnull(question_filename2);
 
6092
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6096
 
                 &cancelled_filenames,
 
6098
 
                 &mandos_client_exited,
 
6102
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
6104
 
  g_assert_true(queue->next_run == 1);
 
6106
 
  __attribute__((cleanup(string_set_clear)))
 
6107
 
    string_set seen_questions = {};
 
6109
 
  bool queue_contains_question_opener(char *const question_filename){
 
6110
 
    return(find_matching_task(queue, (task_context){
 
6111
 
          .func=open_and_parse_question,
 
6113
 
          .question_filename=question_filename,
 
6114
 
          .password=&password,
 
6115
 
          .cancelled_filenames=&cancelled_filenames,
 
6116
 
          .current_time=¤t_time,
 
6117
 
          .mandos_client_exited=&mandos_client_exited,
 
6118
 
          .password_is_read=&password_is_read,
 
6122
 
  g_assert_true(queue_contains_question_opener(question_filename1));
 
6123
 
  g_assert_true(queue_contains_question_opener(question_filename2));
 
6125
 
  g_assert_true(queue->next_run == 1);
 
6127
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6128
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6129
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6133
 
test_add_existing_questions_non_questions(__attribute__((unused))
 
6134
 
                                          test_fixture *fixture,
 
6135
 
                                          __attribute__((unused))
 
6136
 
                                          gconstpointer user_data){
 
6137
 
  __attribute__((cleanup(cleanup_queue)))
 
6138
 
    task_queue *queue = create_queue();
 
6139
 
  g_assert_nonnull(queue);
 
6140
 
  __attribute__((cleanup(cleanup_close)))
 
6141
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6142
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6143
 
  __attribute__((cleanup(string_set_clear)))
 
6144
 
    string_set cancelled_filenames = {};
 
6145
 
  __attribute__((cleanup(cleanup_string)))
 
6146
 
    char *tempdir = make_temporary_directory();
 
6147
 
  g_assert_nonnull(tempdir);
 
6148
 
  __attribute__((cleanup(cleanup_string)))
 
6149
 
    char *question_filename1
 
6150
 
    = make_temporary_file_in_directory(tempdir);
 
6151
 
  g_assert_nonnull(question_filename1);
 
6152
 
  __attribute__((cleanup(cleanup_string)))
 
6153
 
    char *question_filename2
 
6154
 
    = make_temporary_file_in_directory(tempdir);
 
6155
 
  g_assert_nonnull(question_filename2);
 
6157
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6160
 
                  (buffer[]){{}}, /* password */
 
6161
 
                  &cancelled_filenames,
 
6162
 
                  (mono_microsecs[]){0}, /* current_time */
 
6163
 
                  (bool[]){false},       /* mandos_client_exited */
 
6164
 
                  (bool[]){false},       /* password_is_read */
 
6167
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6169
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6170
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6171
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6175
 
test_add_existing_questions_both_types(__attribute__((unused))
 
6176
 
                                       test_fixture *fixture,
 
6177
 
                                       __attribute__((unused))
 
6178
 
                                       gconstpointer user_data){
 
6179
 
  __attribute__((cleanup(cleanup_queue)))
 
6180
 
    task_queue *queue = create_queue();
 
6181
 
  g_assert_nonnull(queue);
 
6182
 
  __attribute__((cleanup(cleanup_close)))
 
6183
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6184
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6185
 
  __attribute__((cleanup(cleanup_buffer)))
 
6186
 
    buffer password = {};
 
6187
 
  __attribute__((cleanup(string_set_clear)))
 
6188
 
    string_set cancelled_filenames = {};
 
6189
 
  const mono_microsecs current_time = 0;
 
6190
 
  bool mandos_client_exited = false;
 
6191
 
  bool password_is_read = false;
 
6192
 
  __attribute__((cleanup(cleanup_string)))
 
6193
 
    char *tempdir = make_temporary_directory();
 
6194
 
  g_assert_nonnull(tempdir);
 
6195
 
  __attribute__((cleanup(cleanup_string)))
 
6196
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
6197
 
  g_assert_nonnull(tempfilename1);
 
6198
 
  __attribute__((cleanup(cleanup_string)))
 
6199
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
6200
 
  g_assert_nonnull(tempfilename2);
 
6201
 
  __attribute__((cleanup(cleanup_string)))
 
6202
 
    char *question_filename
 
6203
 
    = make_question_file_in_directory(tempdir);
 
6204
 
  g_assert_nonnull(question_filename);
 
6206
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6210
 
                 &cancelled_filenames,
 
6212
 
                 &mandos_client_exited,
 
6216
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6218
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6219
 
        .func=open_and_parse_question,
 
6221
 
        .filename=question_filename,
 
6222
 
        .question_filename=question_filename,
 
6223
 
        .password=&password,
 
6224
 
        .cancelled_filenames=&cancelled_filenames,
 
6225
 
        .current_time=¤t_time,
 
6226
 
        .mandos_client_exited=&mandos_client_exited,
 
6227
 
        .password_is_read=&password_is_read,
 
6230
 
  g_assert_true(queue->next_run == 1);
 
6232
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
6233
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
6234
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6235
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6238
 
static void test_wait_for_event_timeout(__attribute__((unused))
 
6239
 
                                        test_fixture *fixture,
 
6240
 
                                        __attribute__((unused))
 
6241
 
                                        gconstpointer user_data){
 
6242
 
  __attribute__((cleanup(cleanup_close)))
 
6243
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6244
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6246
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
6249
 
static void test_wait_for_event_event(__attribute__((unused))
 
6250
 
                                      test_fixture *fixture,
 
6251
 
                                      __attribute__((unused))
 
6252
 
                                      gconstpointer user_data){
 
6253
 
  __attribute__((cleanup(cleanup_close)))
 
6254
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6255
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6257
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6258
 
  __attribute__((cleanup(cleanup_close)))
 
6259
 
    const int read_pipe = pipefds[0];
 
6260
 
  __attribute__((cleanup(cleanup_close)))
 
6261
 
    const int write_pipe = pipefds[1];
 
6262
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
6263
 
                            &(struct epoll_event)
 
6264
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
6265
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
6267
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6270
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
6271
 
                                        __attribute__((unused))
 
6272
 
                                        gconstpointer user_data){
 
6273
 
  const pid_t pid = fork();
 
6274
 
  if(pid == 0){         /* Child */
 
6275
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
6276
 
      _exit(EXIT_FAILURE);
 
6278
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
6279
 
      _exit(EXIT_FAILURE);
 
6283
 
  g_assert_true(pid != -1);
 
6284
 
  __attribute__((cleanup(cleanup_close)))
 
6285
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6286
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6288
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6291
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
6292
 
  g_assert_true(WIFEXITED(status));
 
6293
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
6296
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
6297
 
                                           test_fixture *fixture,
 
6298
 
                                           __attribute__((unused))
 
6299
 
                                           gconstpointer user_data){
 
6300
 
  __attribute__((cleanup(cleanup_queue)))
 
6301
 
    task_queue *queue = create_queue();
 
6302
 
  g_assert_nonnull(queue);
 
6303
 
  queue->next_run = 1;
 
6304
 
  __attribute__((cleanup(cleanup_close)))
 
6305
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6306
 
  __attribute__((cleanup(string_set_clear)))
 
6307
 
    string_set cancelled_filenames = {};
 
6308
 
  bool quit_now = false;
 
6310
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6311
 
  g_assert_false(quit_now);
 
6312
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
6316
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
6317
 
                                               test_fixture *fixture,
 
6318
 
                                               __attribute__((unused))
 
6321
 
  __attribute__((cleanup(cleanup_queue)))
 
6322
 
    task_queue *queue = create_queue();
 
6323
 
  g_assert_nonnull(queue);
 
6324
 
  __attribute__((cleanup(string_set_clear)))
 
6325
 
    string_set cancelled_filenames = {};
 
6326
 
  bool quit_now = false;
 
6327
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6328
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6329
 
                               question_filename));
 
6331
 
  g_assert_true(add_to_queue(queue,
 
6332
 
                             (task_context){ .func=dummy_func }));
 
6334
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6335
 
  g_assert_false(quit_now);
 
6336
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6337
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
6338
 
                                     question_filename));
 
6342
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
6343
 
                                              test_fixture *fixture,
 
6344
 
                                              __attribute__((unused))
 
6347
 
  __attribute__((cleanup(cleanup_queue)))
 
6348
 
    task_queue *queue = create_queue();
 
6349
 
  g_assert_nonnull(queue);
 
6350
 
  __attribute__((cleanup(string_set_clear)))
 
6351
 
    string_set cancelled_filenames = {};
 
6352
 
  bool quit_now = false;
 
6354
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6355
 
  __attribute__((cleanup(cleanup_close)))
 
6356
 
    const int read_pipe = pipefds[0];
 
6357
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
6358
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6359
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6360
 
                               question_filename));
 
6361
 
  __attribute__((nonnull))
 
6362
 
    void quit_func(const task_context task,
 
6363
 
                   __attribute__((unused)) task_queue *const q){
 
6364
 
    g_assert_nonnull(task.quit_now);
 
6365
 
    *task.quit_now = true;
 
6367
 
  task_context task = {
 
6369
 
    .question_filename=strdup(question_filename),
 
6370
 
    .quit_now=&quit_now,
 
6373
 
  g_assert_nonnull(task.question_filename);
 
6375
 
  g_assert_true(add_to_queue(queue, task));
 
6377
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6378
 
  g_assert_false(quit_now);
 
6380
 
  /* read_pipe should be closed already */
 
6382
 
  bool read_pipe_closed = (close(read_pipe) == -1);
 
6383
 
  read_pipe_closed &= (errno == EBADF);
 
6384
 
  g_assert_true(read_pipe_closed);
 
6387
 
static void test_run_queue_one_task(__attribute__((unused))
 
6388
 
                                    test_fixture *fixture,
 
6389
 
                                    __attribute__((unused))
 
6390
 
                                    gconstpointer user_data){
 
6391
 
  __attribute__((cleanup(cleanup_queue)))
 
6392
 
    task_queue *queue = create_queue();
 
6393
 
  g_assert_nonnull(queue);
 
6394
 
  __attribute__((cleanup(string_set_clear)))
 
6395
 
    string_set cancelled_filenames = {};
 
6396
 
  bool quit_now = false;
 
6398
 
  __attribute__((nonnull))
 
6399
 
    void next_run_func(__attribute__((unused))
 
6400
 
                       const task_context task,
 
6401
 
                       task_queue *const q){
 
6405
 
  task_context task = {
 
6406
 
    .func=next_run_func,
 
6408
 
  g_assert_true(add_to_queue(queue, task));
 
6410
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6411
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6412
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6415
 
static void test_run_queue_two_tasks(__attribute__((unused))
 
6416
 
                                     test_fixture *fixture,
 
6417
 
                                     __attribute__((unused))
 
6418
 
                                     gconstpointer user_data){
 
6419
 
  __attribute__((cleanup(cleanup_queue)))
 
6420
 
    task_queue *queue = create_queue();
 
6421
 
  g_assert_nonnull(queue);
 
6422
 
  queue->next_run = 1;
 
6423
 
  __attribute__((cleanup(string_set_clear)))
 
6424
 
    string_set cancelled_filenames = {};
 
6425
 
  bool quit_now = false;
 
6426
 
  bool mandos_client_exited = false;
 
6428
 
  __attribute__((nonnull))
 
6429
 
    void next_run_func(__attribute__((unused))
 
6430
 
                       const task_context task,
 
6431
 
                       task_queue *const q){
 
6435
 
  __attribute__((nonnull))
 
6436
 
    void exited_func(const task_context task,
 
6437
 
                     __attribute__((unused)) task_queue *const q){
 
6438
 
    *task.mandos_client_exited = true;
 
6441
 
  task_context task1 = {
 
6442
 
    .func=next_run_func,
 
6444
 
  g_assert_true(add_to_queue(queue, task1));
 
6446
 
  task_context task2 = {
 
6448
 
    .mandos_client_exited=&mandos_client_exited,
 
6450
 
  g_assert_true(add_to_queue(queue, task2));
 
6452
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6453
 
  g_assert_false(quit_now);
 
6454
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6455
 
  g_assert_true(mandos_client_exited);
 
6456
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6459
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
6460
 
                                          test_fixture *fixture,
 
6461
 
                                          __attribute__((unused))
 
6462
 
                                          gconstpointer user_data){
 
6463
 
  __attribute__((cleanup(cleanup_queue)))
 
6464
 
    task_queue *queue = create_queue();
 
6465
 
  g_assert_nonnull(queue);
 
6466
 
  __attribute__((cleanup(string_set_clear)))
 
6467
 
    string_set cancelled_filenames = {};
 
6468
 
  bool quit_now = false;
 
6469
 
  bool mandos_client_exited = false;
 
6470
 
  bool password_is_read = false;
 
6472
 
  __attribute__((nonnull))
 
6473
 
    void set_exited_func(const task_context task,
 
6474
 
                         __attribute__((unused)) task_queue *const q){
 
6475
 
    *task.mandos_client_exited = true;
 
6476
 
    *task.quit_now = true;
 
6478
 
  task_context task1 = {
 
6479
 
    .func=set_exited_func,
 
6480
 
    .quit_now=&quit_now,
 
6481
 
    .mandos_client_exited=&mandos_client_exited,
 
6483
 
  g_assert_true(add_to_queue(queue, task1));
 
6485
 
  __attribute__((nonnull))
 
6486
 
    void set_read_func(const task_context task,
 
6487
 
                       __attribute__((unused)) task_queue *const q){
 
6488
 
    *task.quit_now = true;
 
6489
 
    *task.password_is_read = true;
 
6491
 
  task_context task2 = {
 
6492
 
    .func=set_read_func,
 
6493
 
    .quit_now=&quit_now,
 
6494
 
    .password_is_read=&password_is_read,
 
6496
 
  g_assert_true(add_to_queue(queue, task2));
 
6498
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6499
 
  g_assert_true(quit_now);
 
6500
 
  g_assert_true(mandos_client_exited xor password_is_read);
 
6501
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6504
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
6505
 
                                             test_fixture *fixture,
 
6506
 
                                             __attribute__((unused))
 
6507
 
                                             gconstpointer user_data){
 
6508
 
  __attribute__((cleanup(cleanup_queue)))
 
6509
 
    task_queue *queue = create_queue();
 
6510
 
  g_assert_nonnull(queue);
 
6511
 
  __attribute__((cleanup(string_set_clear)))
 
6512
 
    string_set cancelled_filenames = {};
 
6514
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6515
 
  __attribute__((cleanup(cleanup_close)))
 
6516
 
    const int read_pipe = pipefds[0];
 
6517
 
  __attribute__((cleanup(cleanup_close)))
 
6518
 
    const int write_pipe = pipefds[1];
 
6519
 
  bool quit_now = false;
 
6521
 
  __attribute__((nonnull))
 
6522
 
    void read_func(const task_context task,
 
6523
 
                   __attribute__((unused)) task_queue *const q){
 
6524
 
    *task.quit_now = true;
 
6526
 
  task_context task1 = {
 
6528
 
    .quit_now=&quit_now,
 
6531
 
  g_assert_true(add_to_queue(queue, task1));
 
6533
 
  __attribute__((nonnull))
 
6534
 
    void write_func(const task_context task,
 
6535
 
                    __attribute__((unused)) task_queue *const q){
 
6536
 
    *task.quit_now = true;
 
6538
 
  task_context task2 = {
 
6540
 
    .quit_now=&quit_now,
 
6543
 
  g_assert_true(add_to_queue(queue, task2));
 
6545
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6546
 
  g_assert_true(quit_now);
 
6548
 
  /* Either read_pipe or write_pipe should be closed already */
 
6550
 
  bool close_read_pipe = (close(read_pipe) == -1);
 
6551
 
  close_read_pipe &= (errno == EBADF);
 
6553
 
  bool close_write_pipe = (close(write_pipe) == -1);
 
6554
 
  close_write_pipe &= (errno == EBADF);
 
6555
 
  g_assert_true(close_read_pipe xor close_write_pipe);
 
6556
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6559
 
static void test_setup_signal_handler(__attribute__((unused))
 
6560
 
                                      test_fixture *fixture,
 
6561
 
                                      __attribute__((unused))
 
6562
 
                                      gconstpointer user_data){
 
6563
 
  /* Save current SIGCHLD action, whatever it is */
 
6564
 
  struct sigaction expected_sigchld_action;
 
6565
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
6568
 
  /* Act; i.e. run the setup_signal_handler() function */
 
6569
 
  struct sigaction actual_old_sigchld_action;
 
6570
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
6572
 
  /* Check that the function correctly set "actual_old_sigchld_action"
 
6573
 
     to the same values as the previously saved
 
6574
 
     "expected_sigchld_action" */
 
6575
 
  /* Check member sa_handler */
 
6576
 
  g_assert_true(actual_old_sigchld_action.sa_handler
 
6577
 
                == expected_sigchld_action.sa_handler);
 
6578
 
  /* Check member sa_mask */
 
6579
 
  for(int signum = 1; signum < NSIG; signum++){
 
6580
 
    const int expected_old_block_state
 
6581
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
6582
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6583
 
    const int actual_old_block_state
 
6584
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
6585
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
6586
 
    g_assert_cmpint(actual_old_block_state,
 
6587
 
                    ==, expected_old_block_state);
 
6589
 
  /* Check member sa_flags */
 
6590
 
  g_assert_true((actual_old_sigchld_action.sa_flags
 
6591
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6592
 
                == (expected_sigchld_action.sa_flags
 
6593
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
6595
 
  /* Retrieve the current signal handler for SIGCHLD as set by
 
6596
 
     setup_signal_handler() */
 
6597
 
  struct sigaction actual_new_sigchld_action;
 
6598
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
6599
 
                            &actual_new_sigchld_action), ==, 0);
 
6600
 
  /* Check that the signal handler (member sa_handler) is correctly
 
6601
 
     set to the "handle_sigchld" function */
 
6602
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
6603
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
6604
 
  g_assert_true(actual_new_sigchld_action.sa_handler
 
6606
 
  /* Check (in member sa_mask) that at least a handful of signals are
 
6607
 
     actually blocked during the signal handler */
 
6608
 
  for(int signum = 1; signum < NSIG; signum++){
 
6609
 
    int actual_new_block_state;
 
6615
 
      actual_new_block_state
 
6616
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
6617
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
6619
 
    case SIGKILL:               /* non-blockable */
 
6620
 
    case SIGSTOP:               /* non-blockable */
 
6621
 
    case SIGCHLD:               /* always blocked */
 
6626
 
  /* Check member sa_flags */
 
6627
 
  g_assert_true((actual_new_sigchld_action.sa_flags
 
6628
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6629
 
                == (SA_NOCLDSTOP | SA_RESTART));
 
6631
 
  /* Restore signal handler */
 
6632
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
6636
 
static void test_restore_signal_handler(__attribute__((unused))
 
6637
 
                                        test_fixture *fixture,
 
6638
 
                                        __attribute__((unused))
 
6639
 
                                        gconstpointer user_data){
 
6640
 
  /* Save current SIGCHLD action, whatever it is */
 
6641
 
  struct sigaction expected_sigchld_action;
 
6642
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
6644
 
  /* Since we haven't established a signal handler yet, there should
 
6645
 
     not be one established.  But another test may have relied on
 
6646
 
     restore_signal_handler() to restore the signal handler, and if
 
6647
 
     restore_signal_handler() is buggy (which we should be prepared
 
6648
 
     for in this test) the signal handler may not have been restored
 
6649
 
     properly; check for this: */
 
6650
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
6652
 
  /* Establish a signal handler */
 
6653
 
  struct sigaction sigchld_action = {
 
6654
 
    .sa_handler=handle_sigchld,
 
6655
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
6657
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
6658
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
6660
 
  /* Act; i.e. run the restore_signal_handler() function */
 
6661
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
6663
 
  /* Retrieve the restored signal handler data */
 
6664
 
  struct sigaction actual_restored_sigchld_action;
 
6665
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
6666
 
                            &actual_restored_sigchld_action), ==, 0);
 
6668
 
  /* Check that the function correctly restored the signal action, as
 
6669
 
     saved in "actual_restored_sigchld_action", to the same values as
 
6670
 
     the previously saved "expected_sigchld_action" */
 
6671
 
  /* Check member sa_handler */
 
6672
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
6673
 
                == expected_sigchld_action.sa_handler);
 
6674
 
  /* Check member sa_mask */
 
6675
 
  for(int signum = 1; signum < NSIG; signum++){
 
6676
 
    const int expected_old_block_state
 
6677
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
6678
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6679
 
    const int actual_restored_block_state
 
6680
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
6681
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
6682
 
    g_assert_cmpint(actual_restored_block_state,
 
6683
 
                    ==, expected_old_block_state);
 
6685
 
  /* Check member sa_flags */
 
6686
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
6687
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
6688
 
                == (expected_sigchld_action.sa_flags
 
6689
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
6692
 
static void test_block_sigchld(__attribute__((unused))
 
6693
 
                               test_fixture *fixture,
 
6694
 
                               __attribute__((unused))
 
6695
 
                               gconstpointer user_data){
 
6696
 
  /* Save original signal mask */
 
6697
 
  sigset_t expected_sigmask;
 
6698
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
6701
 
  /* Make sure SIGCHLD is unblocked for this test */
 
6702
 
  sigset_t sigchld_sigmask;
 
6703
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
6704
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
6705
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
6708
 
  /* Act; i.e. run the block_sigchld() function */
 
6709
 
  sigset_t actual_old_sigmask;
 
6710
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
6712
 
  /* Check the actual_old_sigmask; it should be the same as the
 
6713
 
     previously saved signal mask "expected_sigmask". */
 
6714
 
  for(int signum = 1; signum < NSIG; signum++){
 
6715
 
    const int expected_old_block_state
 
6716
 
      = sigismember(&expected_sigmask, signum);
 
6717
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6718
 
    const int actual_old_block_state
 
6719
 
      = sigismember(&actual_old_sigmask, signum);
 
6720
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
6721
 
    g_assert_cmpint(actual_old_block_state,
 
6722
 
                    ==, expected_old_block_state);
 
6725
 
  /* Retrieve the newly set signal mask */
 
6726
 
  sigset_t actual_sigmask;
 
6727
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
6729
 
  /* SIGCHLD should be blocked */
 
6730
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
6732
 
  /* Restore signal mask */
 
6733
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
6737
 
static void test_restore_sigmask(__attribute__((unused))
 
6738
 
                                 test_fixture *fixture,
 
6739
 
                                 __attribute__((unused))
 
6740
 
                                 gconstpointer user_data){
 
6741
 
  /* Save original signal mask */
 
6742
 
  sigset_t orig_sigmask;
 
6743
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
6745
 
  /* Make sure SIGCHLD is blocked for this test */
 
6746
 
  sigset_t sigchld_sigmask;
 
6747
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
6748
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
6749
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
6752
 
  /* Act; i.e. run the restore_sigmask() function */
 
6753
 
  g_assert_true(restore_sigmask(&orig_sigmask));
 
6755
 
  /* Retrieve the newly restored signal mask */
 
6756
 
  sigset_t restored_sigmask;
 
6757
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
6760
 
  /* Check the restored_sigmask; it should be the same as the
 
6761
 
     previously saved signal mask "orig_sigmask". */
 
6762
 
  for(int signum = 1; signum < NSIG; signum++){
 
6763
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
6764
 
    g_assert_cmpint(orig_block_state, >=, 0);
 
6765
 
    const int restored_block_state = sigismember(&restored_sigmask,
 
6767
 
    g_assert_cmpint(restored_block_state, >=, 0);
 
6768
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
6771
 
  /* Restore signal mask */
 
6772
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
6776
 
static void test_parse_arguments_noargs(__attribute__((unused))
 
6777
 
                                        test_fixture *fixture,
 
6778
 
                                        __attribute__((unused))
 
6779
 
                                        gconstpointer user_data){
 
6783
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6785
 
  char *agent_directory = NULL;
 
6786
 
  char *helper_directory = NULL;
 
6789
 
  char *mandos_argz = NULL;
 
6790
 
  size_t mandos_argz_length = 0;
 
6792
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6793
 
                                &helper_directory, &user, &group,
 
6794
 
                                &mandos_argz, &mandos_argz_length));
 
6795
 
  g_assert_null(agent_directory);
 
6796
 
  g_assert_null(helper_directory);
 
6797
 
  g_assert_true(user == 0);
 
6798
 
  g_assert_true(group == 0);
 
6799
 
  g_assert_null(mandos_argz);
 
6800
 
  g_assert_true(mandos_argz_length == 0);
 
6802
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6807
 
__attribute__((nonnull))
 
6808
 
static bool parse_arguments_devnull(int argc, char *argv[],
 
6809
 
                                    const bool exit_failure,
 
6810
 
                                    char **agent_directory,
 
6811
 
                                    char **helper_directory,
 
6815
 
                                    size_t *mandos_argz_length){
 
6817
 
  FILE *real_stderr = stderr;
 
6818
 
  FILE *devnull = fopen("/dev/null", "we");
 
6819
 
  g_assert_nonnull(devnull);
 
6822
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
6824
 
                                   helper_directory, user, group,
 
6825
 
                                   mandos_argz, mandos_argz_length);
 
6826
 
  const error_t saved_errno = errno;
 
6828
 
  stderr = real_stderr;
 
6829
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
6831
 
  errno = saved_errno;
 
6836
 
static void test_parse_arguments_invalid(__attribute__((unused))
 
6837
 
                                         test_fixture *fixture,
 
6838
 
                                         __attribute__((unused))
 
6839
 
                                         gconstpointer user_data){
 
6842
 
    strdup("--invalid"),
 
6844
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6846
 
  char *agent_directory = NULL;
 
6847
 
  char *helper_directory = NULL;
 
6850
 
  char *mandos_argz = NULL;
 
6851
 
  size_t mandos_argz_length = 0;
 
6853
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
6855
 
                                         &helper_directory, &user,
 
6856
 
                                         &group, &mandos_argz,
 
6857
 
                                         &mandos_argz_length));
 
6859
 
  g_assert_true(errno == EINVAL);
 
6860
 
  g_assert_null(agent_directory);
 
6861
 
  g_assert_null(helper_directory);
 
6862
 
  g_assert_null(mandos_argz);
 
6863
 
  g_assert_true(mandos_argz_length == 0);
 
6865
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6870
 
static void test_parse_arguments_long_dir(__attribute__((unused))
 
6871
 
                                          test_fixture *fixture,
 
6872
 
                                          __attribute__((unused))
 
6873
 
                                          gconstpointer user_data){
 
6876
 
    strdup("--agent-directory"),
 
6879
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6881
 
  __attribute__((cleanup(cleanup_string)))
 
6882
 
    char *agent_directory = NULL;
 
6883
 
  char *helper_directory = NULL;
 
6886
 
  __attribute__((cleanup(cleanup_string)))
 
6887
 
    char *mandos_argz = NULL;
 
6888
 
  size_t mandos_argz_length = 0;
 
6890
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6891
 
                                &helper_directory, &user, &group,
 
6892
 
                                &mandos_argz, &mandos_argz_length));
 
6894
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
6895
 
  g_assert_null(helper_directory);
 
6896
 
  g_assert_true(user == 0);
 
6897
 
  g_assert_true(group == 0);
 
6898
 
  g_assert_null(mandos_argz);
 
6899
 
  g_assert_true(mandos_argz_length == 0);
 
6901
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6906
 
static void test_parse_arguments_short_dir(__attribute__((unused))
 
6907
 
                                           test_fixture *fixture,
 
6908
 
                                           __attribute__((unused))
 
6909
 
                                           gconstpointer user_data){
 
6915
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6917
 
  __attribute__((cleanup(cleanup_string)))
 
6918
 
    char *agent_directory = NULL;
 
6919
 
  char *helper_directory = NULL;
 
6922
 
  __attribute__((cleanup(cleanup_string)))
 
6923
 
    char *mandos_argz = NULL;
 
6924
 
  size_t mandos_argz_length = 0;
 
6926
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6927
 
                                &helper_directory, &user, &group,
 
6928
 
                                &mandos_argz, &mandos_argz_length));
 
6930
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
6931
 
  g_assert_null(helper_directory);
 
6932
 
  g_assert_true(user == 0);
 
6933
 
  g_assert_true(group == 0);
 
6934
 
  g_assert_null(mandos_argz);
 
6935
 
  g_assert_true(mandos_argz_length == 0);
 
6937
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6943
 
void test_parse_arguments_helper_directory(__attribute__((unused))
 
6944
 
                                           test_fixture *fixture,
 
6945
 
                                           __attribute__((unused))
 
6946
 
                                           gconstpointer user_data){
 
6949
 
    strdup("--helper-directory"),
 
6952
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6954
 
  char *agent_directory = NULL;
 
6955
 
  __attribute__((cleanup(cleanup_string)))
 
6956
 
    char *helper_directory = NULL;
 
6959
 
  __attribute__((cleanup(cleanup_string)))
 
6960
 
    char *mandos_argz = NULL;
 
6961
 
  size_t mandos_argz_length = 0;
 
6963
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
6964
 
                                &helper_directory, &user, &group,
 
6965
 
                                &mandos_argz, &mandos_argz_length));
 
6967
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
6968
 
  g_assert_null(agent_directory);
 
6969
 
  g_assert_true(user == 0);
 
6970
 
  g_assert_true(group == 0);
 
6971
 
  g_assert_null(mandos_argz);
 
6972
 
  g_assert_true(mandos_argz_length == 0);
 
6974
 
  for(char **arg = argv; *arg != NULL; arg++){
 
6980
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
6981
 
                                            test_fixture *fixture,
 
6982
 
                                            __attribute__((unused))
 
6983
 
                                            gconstpointer user_data){
 
6986
 
    strdup("--plugin-helper-dir"),
 
6989
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
6991
 
  char *agent_directory = NULL;
 
6992
 
  __attribute__((cleanup(cleanup_string)))
 
6993
 
    char *helper_directory = NULL;
 
6996
 
  __attribute__((cleanup(cleanup_string)))
 
6997
 
    char *mandos_argz = NULL;
 
6998
 
  size_t mandos_argz_length = 0;
 
7000
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7001
 
                                &helper_directory, &user, &group,
 
7002
 
                                &mandos_argz, &mandos_argz_length));
 
7004
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
7005
 
  g_assert_null(agent_directory);
 
7006
 
  g_assert_true(user == 0);
 
7007
 
  g_assert_true(group == 0);
 
7008
 
  g_assert_null(mandos_argz);
 
7009
 
  g_assert_true(mandos_argz_length == 0);
 
7011
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7016
 
static void test_parse_arguments_user(__attribute__((unused))
 
7017
 
                                      test_fixture *fixture,
 
7018
 
                                      __attribute__((unused))
 
7019
 
                                      gconstpointer user_data){
 
7025
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7027
 
  char *agent_directory = NULL;
 
7028
 
  __attribute__((cleanup(cleanup_string)))
 
7029
 
    char *helper_directory = NULL;
 
7032
 
  __attribute__((cleanup(cleanup_string)))
 
7033
 
    char *mandos_argz = NULL;
 
7034
 
  size_t mandos_argz_length = 0;
 
7036
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7037
 
                                &helper_directory, &user, &group,
 
7038
 
                                &mandos_argz, &mandos_argz_length));
 
7040
 
  g_assert_null(helper_directory);
 
7041
 
  g_assert_null(agent_directory);
 
7042
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
7043
 
  g_assert_true(group == 0);
 
7044
 
  g_assert_null(mandos_argz);
 
7045
 
  g_assert_true(mandos_argz_length == 0);
 
7047
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7052
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
7053
 
                                              test_fixture *fixture,
 
7054
 
                                              __attribute__((unused))
 
7062
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7064
 
  char *agent_directory = NULL;
 
7065
 
  __attribute__((cleanup(cleanup_string)))
 
7066
 
    char *helper_directory = NULL;
 
7069
 
  __attribute__((cleanup(cleanup_string)))
 
7070
 
    char *mandos_argz = NULL;
 
7071
 
  size_t mandos_argz_length = 0;
 
7073
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7075
 
                                         &helper_directory, &user,
 
7076
 
                                         &group, &mandos_argz,
 
7077
 
                                         &mandos_argz_length));
 
7079
 
  g_assert_null(helper_directory);
 
7080
 
  g_assert_null(agent_directory);
 
7081
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7082
 
  g_assert_true(group == 0);
 
7083
 
  g_assert_null(mandos_argz);
 
7084
 
  g_assert_true(mandos_argz_length == 0);
 
7086
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7092
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
7093
 
                                            test_fixture *fixture,
 
7094
 
                                            __attribute__((unused))
 
7095
 
                                            gconstpointer user_data){
 
7101
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7103
 
  char *agent_directory = NULL;
 
7104
 
  __attribute__((cleanup(cleanup_string)))
 
7105
 
    char *helper_directory = NULL;
 
7108
 
  __attribute__((cleanup(cleanup_string)))
 
7109
 
    char *mandos_argz = NULL;
 
7110
 
  size_t mandos_argz_length = 0;
 
7112
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7114
 
                                         &helper_directory, &user,
 
7115
 
                                         &group, &mandos_argz,
 
7116
 
                                         &mandos_argz_length));
 
7118
 
  g_assert_null(helper_directory);
 
7119
 
  g_assert_null(agent_directory);
 
7120
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7121
 
  g_assert_true(group == 0);
 
7122
 
  g_assert_null(mandos_argz);
 
7123
 
  g_assert_true(mandos_argz_length == 0);
 
7125
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7130
 
static void test_parse_arguments_group(__attribute__((unused))
 
7131
 
                                       test_fixture *fixture,
 
7132
 
                                       __attribute__((unused))
 
7133
 
                                       gconstpointer user_data){
 
7139
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7141
 
  char *agent_directory = NULL;
 
7142
 
  __attribute__((cleanup(cleanup_string)))
 
7143
 
    char *helper_directory = NULL;
 
7146
 
  __attribute__((cleanup(cleanup_string)))
 
7147
 
    char *mandos_argz = NULL;
 
7148
 
  size_t mandos_argz_length = 0;
 
7150
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7151
 
                                &helper_directory, &user, &group,
 
7152
 
                                &mandos_argz, &mandos_argz_length));
 
7154
 
  g_assert_null(helper_directory);
 
7155
 
  g_assert_null(agent_directory);
 
7156
 
  g_assert_true(user == 0);
 
7157
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
7158
 
  g_assert_null(mandos_argz);
 
7159
 
  g_assert_true(mandos_argz_length == 0);
 
7161
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7166
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
7167
 
                                               test_fixture *fixture,
 
7168
 
                                               __attribute__((unused))
 
7176
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7178
 
  char *agent_directory = NULL;
 
7179
 
  __attribute__((cleanup(cleanup_string)))
 
7180
 
    char *helper_directory = NULL;
 
7183
 
  __attribute__((cleanup(cleanup_string)))
 
7184
 
    char *mandos_argz = NULL;
 
7185
 
  size_t mandos_argz_length = 0;
 
7187
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7189
 
                                         &helper_directory, &user,
 
7190
 
                                         &group, &mandos_argz,
 
7191
 
                                         &mandos_argz_length));
 
7193
 
  g_assert_null(helper_directory);
 
7194
 
  g_assert_null(agent_directory);
 
7195
 
  g_assert_true(user == 0);
 
7196
 
  g_assert_true(group == 0);
 
7197
 
  g_assert_null(mandos_argz);
 
7198
 
  g_assert_true(mandos_argz_length == 0);
 
7200
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7206
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
7207
 
                                             test_fixture *fixture,
 
7208
 
                                             __attribute__((unused))
 
7209
 
                                             gconstpointer user_data){
 
7215
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7217
 
  char *agent_directory = NULL;
 
7218
 
  __attribute__((cleanup(cleanup_string)))
 
7219
 
    char *helper_directory = NULL;
 
7222
 
  __attribute__((cleanup(cleanup_string)))
 
7223
 
    char *mandos_argz = NULL;
 
7224
 
  size_t mandos_argz_length = 0;
 
7226
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7228
 
                                         &helper_directory, &user,
 
7229
 
                                         &group, &mandos_argz,
 
7230
 
                                         &mandos_argz_length));
 
7232
 
  g_assert_null(helper_directory);
 
7233
 
  g_assert_null(agent_directory);
 
7234
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
7235
 
  g_assert_true(group == 0);
 
7236
 
  g_assert_null(mandos_argz);
 
7237
 
  g_assert_true(mandos_argz_length == 0);
 
7239
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7244
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
7245
 
                                               test_fixture *fixture,
 
7246
 
                                               __attribute__((unused))
 
7251
 
    strdup("mandos-client"),
 
7253
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7255
 
  __attribute__((cleanup(cleanup_string)))
 
7256
 
    char *agent_directory = NULL;
 
7257
 
  __attribute__((cleanup(cleanup_string)))
 
7258
 
    char *helper_directory = NULL;
 
7261
 
  __attribute__((cleanup(cleanup_string)))
 
7262
 
    char *mandos_argz = NULL;
 
7263
 
  size_t mandos_argz_length = 0;
 
7265
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7266
 
                                &helper_directory, &user, &group,
 
7267
 
                                &mandos_argz, &mandos_argz_length));
 
7269
 
  g_assert_null(agent_directory);
 
7270
 
  g_assert_null(helper_directory);
 
7271
 
  g_assert_true(user == 0);
 
7272
 
  g_assert_true(group == 0);
 
7273
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
7274
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7275
 
                                            mandos_argz_length),
 
7278
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7283
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
7284
 
                                             test_fixture *fixture,
 
7285
 
                                             __attribute__((unused))
 
7286
 
                                             gconstpointer user_data){
 
7289
 
    strdup("mandos-client"),
 
7294
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7296
 
  __attribute__((cleanup(cleanup_string)))
 
7297
 
    char *agent_directory = NULL;
 
7298
 
  __attribute__((cleanup(cleanup_string)))
 
7299
 
    char *helper_directory = NULL;
 
7302
 
  __attribute__((cleanup(cleanup_string)))
 
7303
 
    char *mandos_argz = NULL;
 
7304
 
  size_t mandos_argz_length = 0;
 
7306
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7307
 
                                &helper_directory, &user, &group,
 
7308
 
                                &mandos_argz, &mandos_argz_length));
 
7310
 
  g_assert_null(agent_directory);
 
7311
 
  g_assert_null(helper_directory);
 
7312
 
  g_assert_true(user == 0);
 
7313
 
  g_assert_true(group == 0);
 
7314
 
  char *marg = mandos_argz;
 
7315
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7316
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7317
 
  g_assert_cmpstr(marg, ==, "one");
 
7318
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7319
 
  g_assert_cmpstr(marg, ==, "two");
 
7320
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7321
 
  g_assert_cmpstr(marg, ==, "three");
 
7322
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7323
 
                                            mandos_argz_length),
 
7326
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7331
 
static void test_parse_arguments_all_args(__attribute__((unused))
 
7332
 
                                          test_fixture *fixture,
 
7333
 
                                          __attribute__((unused))
 
7334
 
                                          gconstpointer user_data){
 
7337
 
    strdup("--agent-directory"),
 
7339
 
    strdup("--helper-directory"),
 
7345
 
    strdup("mandos-client"),
 
7350
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7352
 
  __attribute__((cleanup(cleanup_string)))
 
7353
 
    char *agent_directory = NULL;
 
7354
 
  __attribute__((cleanup(cleanup_string)))
 
7355
 
    char *helper_directory = NULL;
 
7358
 
  __attribute__((cleanup(cleanup_string)))
 
7359
 
    char *mandos_argz = NULL;
 
7360
 
  size_t mandos_argz_length = 0;
 
7362
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7363
 
                                &helper_directory, &user, &group,
 
7364
 
                                &mandos_argz, &mandos_argz_length));
 
7366
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7367
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7368
 
  g_assert_true(user == 1);
 
7369
 
  g_assert_true(group == 2);
 
7370
 
  char *marg = mandos_argz;
 
7371
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7372
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7373
 
  g_assert_cmpstr(marg, ==, "one");
 
7374
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7375
 
  g_assert_cmpstr(marg, ==, "two");
 
7376
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7377
 
  g_assert_cmpstr(marg, ==, "three");
 
7378
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7379
 
                                            mandos_argz_length),
 
7382
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7387
 
static void test_parse_arguments_mixed(__attribute__((unused))
 
7388
 
                                       test_fixture *fixture,
 
7389
 
                                       __attribute__((unused))
 
7390
 
                                       gconstpointer user_data){
 
7393
 
    strdup("mandos-client"),
 
7397
 
    strdup("--agent-directory"),
 
7401
 
    strdup("--helper-directory=/var/tmp"),
 
7403
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7405
 
  __attribute__((cleanup(cleanup_string)))
 
7406
 
    char *agent_directory = NULL;
 
7407
 
  __attribute__((cleanup(cleanup_string)))
 
7408
 
    char *helper_directory = NULL;
 
7411
 
  __attribute__((cleanup(cleanup_string)))
 
7412
 
    char *mandos_argz = NULL;
 
7413
 
  size_t mandos_argz_length = 0;
 
7415
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7416
 
                                &helper_directory, &user, &group,
 
7417
 
                                &mandos_argz, &mandos_argz_length));
 
7419
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7420
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7421
 
  g_assert_true(user == 1);
 
7422
 
  g_assert_true(group == 0);
 
7423
 
  char *marg = mandos_argz;
 
7424
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7425
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7426
 
  g_assert_cmpstr(marg, ==, "one");
 
7427
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7428
 
  g_assert_cmpstr(marg, ==, "two");
 
7429
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7430
 
  g_assert_cmpstr(marg, ==, "three");
 
7431
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7432
 
                                            mandos_argz_length),
 
7435
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7440
 
/* End of tests section */
 
7442
 
/* Test boilerplate section; New tests should be added to the test
 
7443
 
   suite definition here, in the "run_tests" function.
 
7445
 
   Finally, this section also contains the should_only_run_tests()
 
7446
 
   function used by main() for deciding if tests should be run or to
 
7449
 
__attribute__((cold))
 
7450
 
static bool run_tests(int argc, char *argv[]){
 
7451
 
  g_test_init(&argc, &argv, NULL);
 
7453
 
  /* A macro to add a test with no setup or teardown functions */
 
7454
 
#define test_add(testpath, testfunc)                    \
 
7456
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
7457
 
               (testfunc), NULL);                       \
 
7460
 
  /* Test the signal-related functions first, since some other tests
 
7461
 
     depend on these functions in their setups and teardowns */
 
7462
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
7463
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
7464
 
  test_add("/signal-handling/block", test_block_sigchld);
 
7465
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
7467
 
  /* Regular non-signal-related tests; these use no setups or
 
7469
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
7470
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
7471
 
  test_add("/parse_arguments/long-dir",
 
7472
 
           test_parse_arguments_long_dir);
 
7473
 
  test_add("/parse_arguments/short-dir",
 
7474
 
           test_parse_arguments_short_dir);
 
7475
 
  test_add("/parse_arguments/helper-directory",
 
7476
 
           test_parse_arguments_helper_directory);
 
7477
 
  test_add("/parse_arguments/plugin-helper-dir",
 
7478
 
           test_parse_arguments_plugin_helper_dir);
 
7479
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
7480
 
  test_add("/parse_arguments/user-invalid",
 
7481
 
           test_parse_arguments_user_invalid);
 
7482
 
  test_add("/parse_arguments/user-zero-invalid",
 
7483
 
           test_parse_arguments_user_zero_invalid);
 
7484
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
7485
 
  test_add("/parse_arguments/group-invalid",
 
7486
 
           test_parse_arguments_group_invalid);
 
7487
 
  test_add("/parse_arguments/group-zero-invalid",
 
7488
 
           test_parse_arguments_group_zero_invalid);
 
7489
 
  test_add("/parse_arguments/mandos-noargs",
 
7490
 
           test_parse_arguments_mandos_noargs);
 
7491
 
  test_add("/parse_arguments/mandos-args",
 
7492
 
           test_parse_arguments_mandos_args);
 
7493
 
  test_add("/parse_arguments/all-args",
 
7494
 
           test_parse_arguments_all_args);
 
7495
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
7496
 
  test_add("/queue/create", test_create_queue);
 
7497
 
  test_add("/queue/add", test_add_to_queue);
 
7498
 
  test_add("/queue/has_question/empty",
 
7499
 
           test_queue_has_question_empty);
 
7500
 
  test_add("/queue/has_question/false",
 
7501
 
           test_queue_has_question_false);
 
7502
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
7503
 
  test_add("/queue/has_question/false2",
 
7504
 
           test_queue_has_question_false2);
 
7505
 
  test_add("/queue/has_question/true2",
 
7506
 
           test_queue_has_question_true2);
 
7507
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
7508
 
  test_add("/string_set/net-set-contains-nothing",
 
7509
 
           test_string_set_new_set_contains_nothing);
 
7510
 
  test_add("/string_set/with-added-string-contains-it",
 
7511
 
           test_string_set_with_added_string_contains_it);
 
7512
 
  test_add("/string_set/cleared-does-not-contain-string",
 
7513
 
           test_string_set_cleared_does_not_contain_str);
 
7514
 
  test_add("/string_set/swap/one-with-empty",
 
7515
 
           test_string_set_swap_one_with_empty);
 
7516
 
  test_add("/string_set/swap/empty-with-one",
 
7517
 
           test_string_set_swap_empty_with_one);
 
7518
 
  test_add("/string_set/swap/one-with-one",
 
7519
 
           test_string_set_swap_one_with_one);
 
7521
 
  /* A macro to add a test using the setup and teardown functions */
 
7522
 
#define test_add_st(path, func)                                 \
 
7524
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
7528
 
  /* Signal-related tests; these use setups and teardowns which
 
7529
 
     establish, during each test run, a signal handler for, and a
 
7530
 
     signal mask blocking, the SIGCHLD signal, just like main() */
 
7531
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
7532
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
7533
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
7534
 
  test_add_st("/run_queue/zeroes-next-run",
 
7535
 
              test_run_queue_zeroes_next_run);
 
7536
 
  test_add_st("/run_queue/clears-cancelled_filenames",
 
7537
 
              test_run_queue_clears_cancelled_filenames);
 
7538
 
  test_add_st("/run_queue/skips-cancelled-filenames",
 
7539
 
              test_run_queue_skips_cancelled_filenames);
 
7540
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
7541
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
7542
 
  test_add_st("/run_queue/two-tasks/quit",
 
7543
 
              test_run_queue_two_tasks_quit);
 
7544
 
  test_add_st("/run_queue/two-tasks-cleanup",
 
7545
 
              test_run_queue_two_tasks_cleanup);
 
7546
 
  test_add_st("/task-creators/start_mandos_client",
 
7547
 
              test_start_mandos_client);
 
7548
 
  test_add_st("/task-creators/start_mandos_client/execv",
 
7549
 
              test_start_mandos_client_execv);
 
7550
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
7551
 
              test_start_mandos_client_suid_euid);
 
7552
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
7553
 
              test_start_mandos_client_suid_egid);
 
7554
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
7555
 
              test_start_mandos_client_suid_ruid);
 
7556
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
7557
 
              test_start_mandos_client_suid_rgid);
 
7558
 
  test_add_st("/task-creators/start_mandos_client/read",
 
7559
 
              test_start_mandos_client_read);
 
7560
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
7561
 
              test_start_mandos_client_helper_directory);
 
7562
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
7563
 
              test_start_mandos_client_sigmask);
 
7564
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
7565
 
              test_wait_for_mandos_client_exit_badpid);
 
7566
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
7567
 
              test_wait_for_mandos_client_exit_noexit);
 
7568
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
7569
 
              test_wait_for_mandos_client_exit_success);
 
7570
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
7571
 
              test_wait_for_mandos_client_exit_failure);
 
7572
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
7573
 
              test_wait_for_mandos_client_exit_killed);
 
7574
 
  test_add_st("/task/read_mandos_client_output/readerror",
 
7575
 
              test_read_mandos_client_output_readerror);
 
7576
 
  test_add_st("/task/read_mandos_client_output/nodata",
 
7577
 
              test_read_mandos_client_output_nodata);
 
7578
 
  test_add_st("/task/read_mandos_client_output/eof",
 
7579
 
              test_read_mandos_client_output_eof);
 
7580
 
  test_add_st("/task/read_mandos_client_output/once",
 
7581
 
              test_read_mandos_client_output_once);
 
7582
 
  test_add_st("/task/read_mandos_client_output/malloc",
 
7583
 
              test_read_mandos_client_output_malloc);
 
7584
 
  test_add_st("/task/read_mandos_client_output/append",
 
7585
 
              test_read_mandos_client_output_append);
 
7586
 
  test_add_st("/task-creators/add_inotify_dir_watch",
 
7587
 
              test_add_inotify_dir_watch);
 
7588
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
7589
 
              test_add_inotify_dir_watch_fail);
 
7590
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
7591
 
              test_add_inotify_dir_watch_EAGAIN);
 
7592
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
7593
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
7594
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
7595
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
7596
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
7597
 
              test_add_inotify_dir_watch_IN_DELETE);
 
7598
 
  test_add_st("/task/read_inotify_event/readerror",
 
7599
 
              test_read_inotify_event_readerror);
 
7600
 
  test_add_st("/task/read_inotify_event/bad-epoll",
 
7601
 
              test_read_inotify_event_bad_epoll);
 
7602
 
  test_add_st("/task/read_inotify_event/nodata",
 
7603
 
              test_read_inotify_event_nodata);
 
7604
 
  test_add_st("/task/read_inotify_event/eof",
 
7605
 
              test_read_inotify_event_eof);
 
7606
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
7607
 
              test_read_inotify_event_IN_CLOSE_WRITE);
 
7608
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
7609
 
              test_read_inotify_event_IN_MOVED_TO);
 
7610
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
7611
 
              test_read_inotify_event_IN_DELETE);
 
7612
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
7613
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
7614
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
7615
 
              test_read_inotify_event_IN_MOVED_TO_badname);
 
7616
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
7617
 
              test_read_inotify_event_IN_DELETE_badname);
 
7618
 
  test_add_st("/task/open_and_parse_question/ENOENT",
 
7619
 
              test_open_and_parse_question_ENOENT);
 
7620
 
  test_add_st("/task/open_and_parse_question/EIO",
 
7621
 
              test_open_and_parse_question_EIO);
 
7622
 
  test_add_st("/task/open_and_parse_question/parse-error",
 
7623
 
              test_open_and_parse_question_parse_error);
 
7624
 
  test_add_st("/task/open_and_parse_question/nosocket",
 
7625
 
              test_open_and_parse_question_nosocket);
 
7626
 
  test_add_st("/task/open_and_parse_question/badsocket",
 
7627
 
              test_open_and_parse_question_badsocket);
 
7628
 
  test_add_st("/task/open_and_parse_question/nopid",
 
7629
 
              test_open_and_parse_question_nopid);
 
7630
 
  test_add_st("/task/open_and_parse_question/badpid",
 
7631
 
              test_open_and_parse_question_badpid);
 
7632
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
7633
 
              test_open_and_parse_question_noexist_pid);
 
7634
 
  test_add_st("/task/open_and_parse_question/no-notafter",
 
7635
 
              test_open_and_parse_question_no_notafter);
 
7636
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
7637
 
              test_open_and_parse_question_bad_notafter);
 
7638
 
  test_add_st("/task/open_and_parse_question/notafter-0",
 
7639
 
              test_open_and_parse_question_notafter_0);
 
7640
 
  test_add_st("/task/open_and_parse_question/notafter-1",
 
7641
 
              test_open_and_parse_question_notafter_1);
 
7642
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
7643
 
              test_open_and_parse_question_notafter_1_1);
 
7644
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
7645
 
              test_open_and_parse_question_notafter_1_2);
 
7646
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
7647
 
              test_open_and_parse_question_equal_notafter);
 
7648
 
  test_add_st("/task/open_and_parse_question/late-notafter",
 
7649
 
              test_open_and_parse_question_late_notafter);
 
7650
 
  test_add_st("/task/cancel_old_question/0-1-2",
 
7651
 
              test_cancel_old_question_0_1_2);
 
7652
 
  test_add_st("/task/cancel_old_question/0-2-1",
 
7653
 
              test_cancel_old_question_0_2_1);
 
7654
 
  test_add_st("/task/cancel_old_question/1-2-3",
 
7655
 
              test_cancel_old_question_1_2_3);
 
7656
 
  test_add_st("/task/cancel_old_question/1-3-2",
 
7657
 
              test_cancel_old_question_1_3_2);
 
7658
 
  test_add_st("/task/cancel_old_question/2-1-3",
 
7659
 
              test_cancel_old_question_2_1_3);
 
7660
 
  test_add_st("/task/cancel_old_question/2-3-1",
 
7661
 
              test_cancel_old_question_2_3_1);
 
7662
 
  test_add_st("/task/cancel_old_question/3-1-2",
 
7663
 
              test_cancel_old_question_3_1_2);
 
7664
 
  test_add_st("/task/cancel_old_question/3-2-1",
 
7665
 
              test_cancel_old_question_3_2_1);
 
7666
 
  test_add_st("/task/connect_question_socket/name-too-long",
 
7667
 
              test_connect_question_socket_name_too_long);
 
7668
 
  test_add_st("/task/connect_question_socket/connect-fail",
 
7669
 
              test_connect_question_socket_connect_fail);
 
7670
 
  test_add_st("/task/connect_question_socket/bad-epoll",
 
7671
 
              test_connect_question_socket_bad_epoll);
 
7672
 
  test_add_st("/task/connect_question_socket/usable",
 
7673
 
              test_connect_question_socket_usable);
 
7674
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
7675
 
              test_send_password_to_socket_client_not_exited);
 
7676
 
  test_add_st("/task/send_password_to_socket/password-not-read",
 
7677
 
              test_send_password_to_socket_password_not_read);
 
7678
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
7679
 
              test_send_password_to_socket_EMSGSIZE);
 
7680
 
  test_add_st("/task/send_password_to_socket/retry",
 
7681
 
              test_send_password_to_socket_retry);
 
7682
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
7683
 
              test_send_password_to_socket_bad_epoll);
 
7684
 
  test_add_st("/task/send_password_to_socket/null-password",
 
7685
 
              test_send_password_to_socket_null_password);
 
7686
 
  test_add_st("/task/send_password_to_socket/empty-password",
 
7687
 
              test_send_password_to_socket_empty_password);
 
7688
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
7689
 
              test_send_password_to_socket_empty_str_pass);
 
7690
 
  test_add_st("/task/send_password_to_socket/text-password",
 
7691
 
              test_send_password_to_socket_text_password);
 
7692
 
  test_add_st("/task/send_password_to_socket/binary-password",
 
7693
 
              test_send_password_to_socket_binary_password);
 
7694
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
7695
 
              test_send_password_to_socket_nuls_in_password);
 
7696
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
7697
 
              test_add_existing_questions_ENOENT);
 
7698
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
7699
 
              test_add_existing_questions_no_questions);
 
7700
 
  test_add_st("/task-creators/add_existing_questions/one-question",
 
7701
 
              test_add_existing_questions_one_question);
 
7702
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
7703
 
              test_add_existing_questions_two_questions);
 
7704
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
7705
 
              test_add_existing_questions_non_questions);
 
7706
 
  test_add_st("/task-creators/add_existing_questions/both-types",
 
7707
 
              test_add_existing_questions_both_types);
 
7709
 
  return g_test_run() == 0;
 
7712
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
7713
 
  GOptionContext *context = g_option_context_new("");
 
7715
 
  g_option_context_set_help_enabled(context, FALSE);
 
7716
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
7718
 
  gboolean run_tests = FALSE;
 
7719
 
  GOptionEntry entries[] = {
 
7720
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
7721
 
      &run_tests, "Run tests", NULL },
 
7724
 
  g_option_context_add_main_entries(context, entries, NULL);
 
7726
 
  GError *error = NULL;
 
7728
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
7729
 
    g_option_context_free(context);
 
7730
 
    g_error("Failed to parse options: %s", error->message);
 
7733
 
  g_option_context_free(context);
 
7734
 
  return run_tests != FALSE;