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)
 
 
1074
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
1075
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
1078
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
 
1079
  if(read_length == 0){ /* EOF */
 
 
1080
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
 
1082
    cleanup_task(&task);
 
 
1085
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
 
1086
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
 
1089
    cleanup_task(&task);
 
 
1092
  if(read_length > 0            /* Data has been read */
 
 
1093
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
 
1094
    char *question_filename = NULL;
 
 
1095
    const ssize_t question_filename_length
 
 
1096
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
 
1097
    if(question_filename_length < 0){
 
 
1098
      error(0, errno, "Failed to create file name from directory name"
 
 
1099
            " %s and file name %s", filename, ievent->name);
 
 
1101
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
 
1102
        if(not add_to_queue(queue, (task_context){
 
 
1103
              .func=open_and_parse_question,
 
 
1105
              .question_filename=question_filename,
 
 
1106
              .filename=question_filename,
 
 
1108
              .cancelled_filenames=cancelled_filenames,
 
 
1109
              .current_time=current_time,
 
 
1110
              .mandos_client_exited=mandos_client_exited,
 
 
1111
              .password_is_read=password_is_read,
 
 
1113
          error(0, errno, "Failed to add open_and_parse_question task"
 
 
1114
                " for file name %s to queue", filename);
 
 
1116
          /* Force the added task (open_and_parse_question) to run
 
 
1118
          queue->next_run = 1;
 
 
1120
      } else if(ievent->mask & IN_DELETE){
 
 
1121
        if(not string_set_add(cancelled_filenames,
 
 
1122
                              question_filename)){
 
 
1123
          error(0, errno, "Could not add question %s to"
 
 
1124
                " cancelled_questions", question_filename);
 
 
1126
          free(question_filename);
 
 
1127
          cleanup_task(&task);
 
 
1130
        free(question_filename);
 
 
1135
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
 
1138
  /* Re-add myself to the queue */
 
 
1139
  if(not add_to_queue(queue, task)){
 
 
1140
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
 
1141
          " queue", filename);
 
 
1143
    cleanup_task(&task);
 
 
1147
  /* Re-add the fd to the epoll set */
 
 
1148
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1149
                            &(struct epoll_event)
 
 
1150
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1151
  if(ret != 0 and errno != EEXIST){
 
 
1152
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
 
1153
          " directory %s to epoll set", fd, filename);
 
 
1154
    /* Force the added task (read_inotify_event) to run again, at most
 
 
1155
       one second from now */
 
 
1156
    if((queue->next_run == 0)
 
 
1157
       or (queue->next_run > (*current_time + 1000000))){
 
 
1158
      queue->next_run = *current_time + 1000000;
 
 
1163
__attribute__((nonnull))
 
 
1164
void open_and_parse_question(const task_context task,
 
 
1165
                             task_queue *const queue){
 
 
1166
  __attribute__((cleanup(cleanup_string)))
 
 
1167
    char *question_filename = task.question_filename;
 
 
1168
  const int epoll_fd = task.epoll_fd;
 
 
1169
  buffer *const password = task.password;
 
 
1170
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1171
  const mono_microsecs *const current_time = task.current_time;
 
 
1172
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1173
  bool *const password_is_read = task.password_is_read;
 
 
1175
  /* We use the GLib "Key-value file parser" functions to parse the
 
 
1176
     question file.  See <https://www.freedesktop.org/wiki/Software
 
 
1177
     /systemd/PasswordAgents/> for specification of contents */
 
 
1178
  __attribute__((nonnull))
 
 
1179
    void cleanup_g_key_file(GKeyFile **key_file){
 
 
1180
    if(*key_file != NULL){
 
 
1181
      g_key_file_free(*key_file);
 
 
1185
  __attribute__((cleanup(cleanup_g_key_file)))
 
 
1186
    GKeyFile *key_file = g_key_file_new();
 
 
1187
  if(key_file == NULL){
 
 
1188
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
 
1192
  GError *glib_error = NULL;
 
 
1193
  if(g_key_file_load_from_file(key_file, question_filename,
 
 
1194
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
 
1195
    /* If a file was removed, we should ignore it, so */
 
 
1196
    /* only show error message if file actually existed */
 
 
1197
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
 
1198
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
 
1199
            question_filename, glib_error->message);
 
 
1204
  __attribute__((cleanup(cleanup_string)))
 
 
1205
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
 
1208
  if(socket_name == NULL){
 
 
1209
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
 
1210
          question_filename, glib_error->message);
 
 
1214
  if(strlen(socket_name) == 0){
 
 
1215
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
 
1220
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
 
1222
  if(glib_error != NULL){
 
 
1223
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
 
1224
          question_filename, glib_error->message);
 
 
1228
  if((pid != (guint64)((pid_t)pid))
 
 
1229
     or (kill((pid_t)pid, 0) != 0)){
 
 
1230
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
 
1231
          " does not exist", (uintmax_t)pid, question_filename);
 
 
1235
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
 
1236
                                           "NotAfter", &glib_error);
 
 
1237
  if(glib_error != NULL){
 
 
1238
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
 
1239
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
 
1240
            " %s", question_filename, glib_error->message);
 
 
1245
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
 
1246
      queue->next_run = notafter;
 
 
1248
    if(*current_time >= notafter){
 
 
1253
  const task_context connect_question_socket_task = {
 
 
1254
    .func=connect_question_socket,
 
 
1255
    .question_filename=strdup(question_filename),
 
 
1258
    .filename=strdup(socket_name),
 
 
1259
    .cancelled_filenames=task.cancelled_filenames,
 
 
1260
    .mandos_client_exited=mandos_client_exited,
 
 
1261
    .password_is_read=password_is_read,
 
 
1262
    .current_time=current_time,
 
 
1264
  if(connect_question_socket_task.question_filename == NULL
 
 
1265
     or connect_question_socket_task.filename == NULL
 
 
1266
     or not add_to_queue(queue, connect_question_socket_task)){
 
 
1267
    error(0, errno, "Failed to add connect_question_socket for socket"
 
 
1268
          " %s (from \"%s\") to queue", socket_name,
 
 
1270
    cleanup_task(&connect_question_socket_task);
 
 
1273
  /* Force the added task (connect_question_socket) to run
 
 
1275
  queue->next_run = 1;
 
 
1278
    char *const dup_filename = strdup(question_filename);
 
 
1279
    const task_context cancel_old_question_task = {
 
 
1280
      .func=cancel_old_question,
 
 
1281
      .question_filename=dup_filename,
 
 
1283
      .filename=dup_filename,
 
 
1284
      .cancelled_filenames=cancelled_filenames,
 
 
1285
      .current_time=current_time,
 
 
1287
    if(cancel_old_question_task.question_filename == NULL
 
 
1288
       or not add_to_queue(queue, cancel_old_question_task)){
 
 
1289
      error(0, errno, "Failed to add cancel_old_question for file "
 
 
1290
            "\"%s\" to queue", question_filename);
 
 
1291
      cleanup_task(&cancel_old_question_task);
 
 
1297
__attribute__((nonnull))
 
 
1298
void cancel_old_question(const task_context task,
 
 
1299
                         task_queue *const queue){
 
 
1300
  char *const question_filename = task.question_filename;
 
 
1301
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1302
  const mono_microsecs notafter = task.notafter;
 
 
1303
  const mono_microsecs *const current_time = task.current_time;
 
 
1305
  if(*current_time >= notafter){
 
 
1306
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1307
      error(0, errno, "Failed to cancel question for file %s",
 
 
1310
    cleanup_task(&task);
 
 
1314
  if(not add_to_queue(queue, task)){
 
 
1315
    error(0, errno, "Failed to add cancel_old_question for file "
 
 
1316
          "%s to queue", question_filename);
 
 
1317
    cleanup_task(&task);
 
 
1321
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
 
1322
    queue->next_run = notafter;
 
 
1326
__attribute__((nonnull))
 
 
1327
void connect_question_socket(const task_context task,
 
 
1328
                             task_queue *const queue){
 
 
1329
  char *const question_filename = task.question_filename;
 
 
1330
  char *const filename = task.filename;
 
 
1331
  const int epoll_fd = task.epoll_fd;
 
 
1332
  buffer *const password = task.password;
 
 
1333
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1334
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1335
  bool *const password_is_read = task.password_is_read;
 
 
1336
  const mono_microsecs *const current_time = task.current_time;
 
 
1338
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
1340
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
 
1341
    error(0, 0, "Socket filename is larger than"
 
 
1342
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
 
1343
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
 
1344
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1345
      error(0, errno, "Failed to cancel question for file %s",
 
 
1348
    cleanup_task(&task);
 
 
1352
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
1353
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
1356
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
 
1357
    if(not add_to_queue(queue, task)){
 
 
1358
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1359
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1361
      cleanup_task(&task);
 
 
1363
      /* Force the added task (connect_question_socket) to run
 
 
1365
      queue->next_run = 1;
 
 
1370
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
1371
  if(connect(fd, (struct sockaddr *)&sock_name,
 
 
1372
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
 
1373
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
 
1374
    if(not add_to_queue(queue, task)){
 
 
1375
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1376
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1378
      cleanup_task(&task);
 
 
1380
      /* Force the added task (connect_question_socket) to run again,
 
 
1381
         at most one second from now */
 
 
1382
      if((queue->next_run == 0)
 
 
1383
         or (queue->next_run > (*current_time + 1000000))){
 
 
1384
        queue->next_run = *current_time + 1000000;
 
 
1390
  /* Not necessary, but we can try, and merely warn on failure */
 
 
1391
  if(shutdown(fd, SHUT_RD) != 0){
 
 
1392
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
 
1396
  /* Add the fd to the epoll set */
 
 
1397
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1398
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1400
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
 
1401
          " socket %s to epoll set", fd, filename);
 
 
1402
    if(not add_to_queue(queue, task)){
 
 
1403
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1404
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1406
      cleanup_task(&task);
 
 
1408
      /* Force the added task (connect_question_socket) to run again,
 
 
1409
         at most one second from now */
 
 
1410
      if((queue->next_run == 0)
 
 
1411
         or (queue->next_run > (*current_time + 1000000))){
 
 
1412
        queue->next_run = *current_time + 1000000;
 
 
1418
  /* add task send_password_to_socket to queue */
 
 
1419
  const task_context send_password_to_socket_task = {
 
 
1420
    .func=send_password_to_socket,
 
 
1421
    .question_filename=question_filename,
 
 
1426
    .cancelled_filenames=cancelled_filenames,
 
 
1427
    .mandos_client_exited=mandos_client_exited,
 
 
1428
    .password_is_read=password_is_read,
 
 
1429
    .current_time=current_time,
 
 
1432
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
 
1433
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1434
          " file \"%s\" and socket \"%s\" to queue",
 
 
1435
          question_filename, filename);
 
 
1436
    cleanup_task(&send_password_to_socket_task);
 
 
1440
__attribute__((nonnull))
 
 
1441
void send_password_to_socket(const task_context task,
 
 
1442
                             task_queue *const queue){
 
 
1443
  char *const question_filename=task.question_filename;
 
 
1444
  char *const filename=task.filename;
 
 
1445
  const int epoll_fd=task.epoll_fd;
 
 
1446
  const int fd=task.fd;
 
 
1447
  buffer *const password=task.password;
 
 
1448
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
 
1449
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1450
  bool *const password_is_read = task.password_is_read;
 
 
1451
  const mono_microsecs *const current_time = task.current_time;
 
 
1453
  if(*mandos_client_exited and *password_is_read){
 
 
1455
    const size_t send_buffer_length = password->length + 2;
 
 
1456
    char *send_buffer = malloc(send_buffer_length);
 
 
1457
    if(send_buffer == NULL){
 
 
1458
      error(0, errno, "Failed to allocate send_buffer");
 
 
1460
      if(mlock(send_buffer, send_buffer_length) != 0){
 
 
1461
        /* Warn but do not treat as fatal error */
 
 
1462
        if(errno != EPERM and errno != ENOMEM){
 
 
1463
          error(0, errno, "Failed to lock memory for password"
 
 
1467
      /* “[…] send a single datagram to the socket consisting of the
 
 
1468
         password string either prefixed with "+" or with "-"
 
 
1469
         depending on whether the password entry was successful or
 
 
1470
         not. You may but don't have to include a final NUL byte in
 
 
1473
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
 
1474
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
 
1476
      send_buffer[0] = '+';     /* Prefix with "+" */
 
 
1477
      /* Always add an extra NUL */
 
 
1478
      send_buffer[password->length + 1] = '\0';
 
 
1479
      if(password->length > 0){
 
 
1480
        memcpy(send_buffer + 1, password->data, password->length);
 
 
1483
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
 
1485
      const error_t saved_errno = errno;
 
 
1486
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
1487
      explicit_bzero(send_buffer, send_buffer_length);
 
 
1489
      memset(send_buffer, '\0', send_buffer_length);
 
 
1491
      if(munlock(send_buffer, send_buffer_length) != 0){
 
 
1492
        error(0, errno, "Failed to unlock memory of send buffer");
 
 
1495
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
 
1496
        switch(saved_errno){
 
 
1509
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
 
1510
                (uintmax_t)password->length);
 
 
1514
          __attribute__((fallthrough));
 
 
1517
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
 
1518
            error(0, 0, "Password only partially sent to socket");
 
 
1523
          __attribute__((fallthrough));
 
 
1526
          error(0, saved_errno, "Failed to send() to socket %s",
 
 
1528
          if(not string_set_add(cancelled_filenames,
 
 
1529
                                question_filename)){
 
 
1530
            error(0, errno, "Failed to cancel question for file %s",
 
 
1533
          cleanup_task(&task);
 
 
1538
        cleanup_task(&task);
 
 
1544
  /* We failed or are not ready yet; retry later */
 
 
1546
  if(not add_to_queue(queue, task)){
 
 
1547
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1548
          " file %s and socket %s to queue", question_filename,
 
 
1550
    cleanup_task(&task);
 
 
1553
  /* Add the fd to the epoll set */
 
 
1554
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1555
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1557
    error(0, errno, "Failed to add socket file descriptor %d for"
 
 
1558
          " socket %s to epoll set", fd, filename);
 
 
1559
    /* Force the added task (send_password_to_socket) to run again, at
 
 
1560
       most one second from now */
 
 
1561
    if((queue->next_run == 0)
 
 
1562
       or (queue->next_run > (*current_time + 1000000))){
 
 
1563
      queue->next_run = *current_time + 1000000;
 
 
1568
__attribute__((warn_unused_result))
 
 
1569
bool add_existing_questions(task_queue *const queue,
 
 
1571
                            buffer *const password,
 
 
1572
                            string_set *cancelled_filenames,
 
 
1573
                            const mono_microsecs *const current_time,
 
 
1574
                            bool *const mandos_client_exited,
 
 
1575
                            bool *const password_is_read,
 
 
1576
                            const char *const dirname){
 
 
1577
  __attribute__((cleanup(cleanup_string)))
 
 
1578
    char *dir_pattern = NULL;
 
 
1579
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
 
1580
  if(ret < 0 or dir_pattern == NULL){
 
 
1581
    error(0, errno, "Could not create glob pattern for directory %s",
 
 
1585
  __attribute__((cleanup(globfree)))
 
 
1586
    glob_t question_filenames = {};
 
 
1587
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
 
1588
              NULL, &question_filenames)){
 
 
1591
    error(0, errno, "Failed to open directory %s", dirname);
 
 
1594
    error(0, errno, "There are no question files in %s", dirname);
 
 
1597
    error(0, errno, "Could not allocate memory for question file"
 
 
1598
          " names in %s", dirname);
 
 
1602
    __attribute__((fallthrough));
 
 
1605
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
 
1606
      char *const question_filename = strdup(question_filenames
 
 
1608
      const task_context task = {
 
 
1609
        .func=open_and_parse_question,
 
 
1611
        .question_filename=question_filename,
 
 
1612
        .filename=question_filename,
 
 
1614
        .cancelled_filenames=cancelled_filenames,
 
 
1615
        .current_time=current_time,
 
 
1616
        .mandos_client_exited=mandos_client_exited,
 
 
1617
        .password_is_read=password_is_read,
 
 
1620
      if(question_filename == NULL
 
 
1621
         or not add_to_queue(queue, task)){
 
 
1622
        error(0, errno, "Failed to add open_and_parse_question for"
 
 
1623
              " file %s to queue",
 
 
1624
              question_filenames.gl_pathv[i]);
 
 
1625
        free(question_filename);
 
 
1627
        queue->next_run = 1;
 
 
1634
__attribute__((nonnull, warn_unused_result))
 
 
1635
bool wait_for_event(const int epoll_fd,
 
 
1636
                    const mono_microsecs queue_next_run,
 
 
1637
                    const mono_microsecs current_time){
 
 
1638
  __attribute__((const))
 
 
1639
    int milliseconds_to_wait(const mono_microsecs currtime,
 
 
1640
                             const mono_microsecs nextrun){
 
 
1641
    if(currtime >= nextrun){
 
 
1644
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
 
1645
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
 
1648
    return (int)wait_time_ms;
 
 
1651
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
 
1654
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
 
1655
  sigset_t temporary_unblocked_sigmask;
 
 
1656
  /* Get current signal mask */
 
 
1657
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
 
1660
  /* Remove SIGCHLD from the signal mask */
 
 
1661
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
 
1664
  struct epoll_event events[8]; /* Ignored */
 
 
1665
  int ret = epoll_pwait(epoll_fd, events,
 
 
1666
                        sizeof(events) / sizeof(struct epoll_event),
 
 
1667
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
 
1668
                        &temporary_unblocked_sigmask);
 
 
1669
  if(ret < 0 and errno != EINTR){
 
 
1670
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
 
1672
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
 
1675
  return clear_all_fds_from_epoll_set(epoll_fd);
 
 
1678
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
 
1679
  /* Create a new empty epoll set */
 
 
1680
  __attribute__((cleanup(cleanup_close)))
 
 
1681
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
1682
  if(new_epoll_fd < 0){
 
 
1685
  /* dup3() the new epoll set fd over the old one, replacing it */
 
 
1686
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
 
1692
__attribute__((nonnull, warn_unused_result))
 
 
1693
bool run_queue(task_queue **const queue,
 
 
1694
               string_set *const cancelled_filenames,
 
 
1695
               bool *const quit_now){
 
 
1697
  task_queue *new_queue = create_queue();
 
 
1698
  if(new_queue == NULL){
 
 
1702
  __attribute__((cleanup(string_set_clear)))
 
 
1703
    string_set old_cancelled_filenames = {};
 
 
1704
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
 
1706
  /* Declare i outside the for loop, since we might need i after the
 
 
1707
     loop in case we aborted in the middle */
 
 
1709
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
 
1710
    task_context *const task = &((*queue)->tasks[i]);
 
 
1711
    const char *const question_filename = task->question_filename;
 
 
1712
    /* Skip any task referencing a cancelled question filename */
 
 
1713
    if(question_filename != NULL
 
 
1714
       and string_set_contains(old_cancelled_filenames,
 
 
1715
                               question_filename)){
 
 
1719
    task->func(*task, new_queue);
 
 
1723
    /* we might be in the middle of the queue, so clean up any
 
 
1724
       remaining tasks in the current queue */
 
 
1725
    for(; i < (*queue)->length; i++){
 
 
1726
      cleanup_task(&((*queue)->tasks[i]));
 
 
1740
/* End of regular code section */
 
 
1742
/* Start of tests section; here are the tests for the above code */
 
 
1744
/* This "fixture" data structure is used by the test setup and
 
 
1745
   teardown functions */
 
 
1747
  struct sigaction orig_sigaction;
 
 
1748
  sigset_t orig_sigmask;
 
 
1751
static void test_setup(test_fixture *fixture,
 
 
1752
                       __attribute__((unused))
 
 
1753
                       gconstpointer user_data){
 
 
1754
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
 
1755
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
 
1758
static void test_teardown(test_fixture *fixture,
 
 
1759
                          __attribute__((unused))
 
 
1760
                          gconstpointer user_data){
 
 
1761
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
 
1762
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
 
1765
/* Utility function used by tests to search queue for matching task */
 
 
1766
__attribute__((pure, nonnull, warn_unused_result))
 
 
1767
static task_context *find_matching_task(const task_queue *const queue,
 
 
1768
                                        const task_context task){
 
 
1769
  /* The argument "task" structure is a pattern to match; 0 in any
 
 
1770
     member means any value matches, otherwise the value must match.
 
 
1771
     The filename strings are compared by strcmp(), not by pointer. */
 
 
1772
  for(size_t i = 0; i < queue->length; i++){
 
 
1773
    task_context *const current_task = queue->tasks+i;
 
 
1774
    /* Check all members of task_context, if set to a non-zero value.
 
 
1775
       If a member does not match, continue to next task in queue */
 
 
1777
    /* task_func *const func */
 
 
1778
    if(task.func != NULL and current_task->func != task.func){
 
 
1781
    /* char *const question_filename; */
 
 
1782
    if(task.question_filename != NULL
 
 
1783
       and (current_task->question_filename == NULL
 
 
1784
            or strcmp(current_task->question_filename,
 
 
1785
                      task.question_filename) != 0)){
 
 
1788
    /* const pid_t pid; */
 
 
1789
    if(task.pid != 0 and current_task->pid != task.pid){
 
 
1792
    /* const int epoll_fd; */
 
 
1793
    if(task.epoll_fd != 0
 
 
1794
       and current_task->epoll_fd != task.epoll_fd){
 
 
1797
    /* bool *const quit_now; */
 
 
1798
    if(task.quit_now != NULL
 
 
1799
       and current_task->quit_now != task.quit_now){
 
 
1803
    if(task.fd != 0 and current_task->fd != task.fd){
 
 
1806
    /* bool *const mandos_client_exited; */
 
 
1807
    if(task.mandos_client_exited != NULL
 
 
1808
       and current_task->mandos_client_exited
 
 
1809
       != task.mandos_client_exited){
 
 
1812
    /* buffer *const password; */
 
 
1813
    if(task.password != NULL
 
 
1814
       and current_task->password != task.password){
 
 
1817
    /* bool *const password_is_read; */
 
 
1818
    if(task.password_is_read != NULL
 
 
1819
       and current_task->password_is_read != task.password_is_read){
 
 
1822
    /* char *filename; */
 
 
1823
    if(task.filename != NULL
 
 
1824
       and (current_task->filename == NULL
 
 
1825
            or strcmp(current_task->filename, task.filename) != 0)){
 
 
1828
    /* string_set *const cancelled_filenames; */
 
 
1829
    if(task.cancelled_filenames != NULL
 
 
1830
       and current_task->cancelled_filenames
 
 
1831
       != task.cancelled_filenames){
 
 
1834
    /* const mono_microsecs notafter; */
 
 
1835
    if(task.notafter != 0
 
 
1836
       and current_task->notafter != task.notafter){
 
 
1839
    /* const mono_microsecs *const current_time; */
 
 
1840
    if(task.current_time != NULL
 
 
1841
       and current_task->current_time != task.current_time){
 
 
1844
    /* Current task matches all members; return it */
 
 
1845
    return current_task;
 
 
1847
  /* No task in queue matches passed pattern task */
 
 
1851
static void test_create_queue(__attribute__((unused))
 
 
1852
                              test_fixture *fixture,
 
 
1853
                              __attribute__((unused))
 
 
1854
                              gconstpointer user_data){
 
 
1855
  __attribute__((cleanup(cleanup_queue)))
 
 
1856
    task_queue *const queue = create_queue();
 
 
1857
  g_assert_nonnull(queue);
 
 
1858
  g_assert_null(queue->tasks);
 
 
1859
  g_assert_true(queue->length == 0);
 
 
1860
  g_assert_true(queue->next_run == 0);
 
 
1863
static task_func dummy_func;
 
 
1865
static void test_add_to_queue(__attribute__((unused))
 
 
1866
                              test_fixture *fixture,
 
 
1867
                              __attribute__((unused))
 
 
1868
                              gconstpointer user_data){
 
 
1869
  __attribute__((cleanup(cleanup_queue)))
 
 
1870
    task_queue *queue = create_queue();
 
 
1871
  g_assert_nonnull(queue);
 
 
1873
  g_assert_true(add_to_queue(queue,
 
 
1874
                             (task_context){ .func=dummy_func }));
 
 
1875
  g_assert_true(queue->length == 1);
 
 
1876
  g_assert_nonnull(queue->tasks);
 
 
1877
  g_assert_true(queue->tasks[0].func == dummy_func);
 
 
1880
static void dummy_func(__attribute__((unused))
 
 
1881
                       const task_context task,
 
 
1882
                       __attribute__((unused))
 
 
1883
                       task_queue *const queue){
 
 
1886
static void test_queue_has_question_empty(__attribute__((unused))
 
 
1887
                                          test_fixture *fixture,
 
 
1888
                                          __attribute__((unused))
 
 
1889
                                          gconstpointer user_data){
 
 
1890
  __attribute__((cleanup(cleanup_queue)))
 
 
1891
    task_queue *queue = create_queue();
 
 
1892
  g_assert_nonnull(queue);
 
 
1893
  g_assert_false(queue_has_question(queue));
 
 
1896
static void test_queue_has_question_false(__attribute__((unused))
 
 
1897
                                          test_fixture *fixture,
 
 
1898
                                          __attribute__((unused))
 
 
1899
                                          gconstpointer user_data){
 
 
1900
  __attribute__((cleanup(cleanup_queue)))
 
 
1901
    task_queue *queue = create_queue();
 
 
1902
  g_assert_nonnull(queue);
 
 
1903
  g_assert_true(add_to_queue(queue,
 
 
1904
                             (task_context){ .func=dummy_func }));
 
 
1905
  g_assert_false(queue_has_question(queue));
 
 
1908
static void test_queue_has_question_true(__attribute__((unused))
 
 
1909
                                         test_fixture *fixture,
 
 
1910
                                         __attribute__((unused))
 
 
1911
                                         gconstpointer user_data){
 
 
1912
  __attribute__((cleanup(cleanup_queue)))
 
 
1913
    task_queue *queue = create_queue();
 
 
1914
  g_assert_nonnull(queue);
 
 
1915
  char *const question_filename
 
 
1916
    = strdup("/nonexistent/question_filename");
 
 
1917
  g_assert_nonnull(question_filename);
 
 
1918
  task_context task = {
 
 
1920
    .question_filename=question_filename,
 
 
1922
  g_assert_true(add_to_queue(queue, task));
 
 
1923
  g_assert_true(queue_has_question(queue));
 
 
1926
static void test_queue_has_question_false2(__attribute__((unused))
 
 
1927
                                           test_fixture *fixture,
 
 
1928
                                           __attribute__((unused))
 
 
1929
                                           gconstpointer user_data){
 
 
1930
  __attribute__((cleanup(cleanup_queue)))
 
 
1931
    task_queue *queue = create_queue();
 
 
1932
  g_assert_nonnull(queue);
 
 
1933
  task_context task = { .func=dummy_func };
 
 
1934
  g_assert_true(add_to_queue(queue, task));
 
 
1935
  g_assert_true(add_to_queue(queue, task));
 
 
1936
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
1937
  g_assert_false(queue_has_question(queue));
 
 
1940
static void test_queue_has_question_true2(__attribute__((unused))
 
 
1941
                                          test_fixture *fixture,
 
 
1942
                                          __attribute__((unused))
 
 
1943
                                          gconstpointer user_data){
 
 
1944
  __attribute__((cleanup(cleanup_queue)))
 
 
1945
    task_queue *queue = create_queue();
 
 
1946
  g_assert_nonnull(queue);
 
 
1947
  task_context task1 = { .func=dummy_func };
 
 
1948
  g_assert_true(add_to_queue(queue, task1));
 
 
1949
  char *const question_filename
 
 
1950
    = strdup("/nonexistent/question_filename");
 
 
1951
  g_assert_nonnull(question_filename);
 
 
1952
  task_context task2 = {
 
 
1954
    .question_filename=question_filename,
 
 
1956
  g_assert_true(add_to_queue(queue, task2));
 
 
1957
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
1958
  g_assert_true(queue_has_question(queue));
 
 
1961
static void test_cleanup_buffer(__attribute__((unused))
 
 
1962
                                test_fixture *fixture,
 
 
1963
                                __attribute__((unused))
 
 
1964
                                gconstpointer user_data){
 
 
1967
  const size_t buffersize = 10;
 
 
1969
  buf.data = malloc(buffersize);
 
 
1970
  g_assert_nonnull(buf.data);
 
 
1971
  if(mlock(buf.data, buffersize) != 0){
 
 
1972
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
1975
  cleanup_buffer(&buf);
 
 
1976
  g_assert_null(buf.data);
 
 
1980
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
 
1981
                                              test_fixture *fixture,
 
 
1982
                                              __attribute__((unused))
 
 
1985
  __attribute__((cleanup(string_set_clear)))
 
 
1986
    string_set set = {};
 
 
1987
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
 
1988
  g_assert_false(string_set_contains(set, "test_string"));
 
 
1992
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
 
1993
                                              test_fixture *fixture,
 
 
1994
                                              __attribute__((unused))
 
 
1997
  __attribute__((cleanup(string_set_clear)))
 
 
1998
    string_set set = {};
 
 
1999
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2000
  g_assert_true(string_set_contains(set, "test_string"));
 
 
2004
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
 
2005
                                             test_fixture *fixture,
 
 
2006
                                             __attribute__((unused))
 
 
2007
                                             gconstpointer user_data){
 
 
2008
  __attribute__((cleanup(string_set_clear)))
 
 
2009
    string_set set = {};
 
 
2010
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2011
  string_set_clear(&set);
 
 
2012
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2016
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
 
2017
                                         test_fixture *fixture,
 
 
2018
                                         __attribute__((unused))
 
 
2019
                                         gconstpointer user_data){
 
 
2020
  __attribute__((cleanup(string_set_clear)))
 
 
2021
    string_set set1 = {};
 
 
2022
  __attribute__((cleanup(string_set_clear)))
 
 
2023
    string_set set2 = {};
 
 
2024
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2025
  string_set_swap(&set1, &set2);
 
 
2026
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2027
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2031
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
 
2032
                                         test_fixture *fixture,
 
 
2033
                                         __attribute__((unused))
 
 
2034
                                         gconstpointer user_data){
 
 
2035
  __attribute__((cleanup(string_set_clear)))
 
 
2036
    string_set set1 = {};
 
 
2037
  __attribute__((cleanup(string_set_clear)))
 
 
2038
    string_set set2 = {};
 
 
2039
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2040
  string_set_swap(&set1, &set2);
 
 
2041
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2042
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2045
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
 
2046
                                              test_fixture *fixture,
 
 
2047
                                              __attribute__((unused))
 
 
2050
  __attribute__((cleanup(string_set_clear)))
 
 
2051
    string_set set1 = {};
 
 
2052
  __attribute__((cleanup(string_set_clear)))
 
 
2053
    string_set set2 = {};
 
 
2054
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2055
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2056
  string_set_swap(&set1, &set2);
 
 
2057
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2058
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2059
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2060
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2063
static bool fd_has_cloexec_and_nonblock(const int);
 
 
2065
static bool epoll_set_contains(int, int, uint32_t);
 
 
2067
static void test_start_mandos_client(test_fixture *fixture,
 
 
2068
                                     __attribute__((unused))
 
 
2069
                                     gconstpointer user_data){
 
 
2071
  bool mandos_client_exited = false;
 
 
2072
  bool quit_now = false;
 
 
2073
  __attribute__((cleanup(cleanup_close)))
 
 
2074
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2075
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2076
  __attribute__((cleanup(cleanup_queue)))
 
 
2077
    task_queue *queue = create_queue();
 
 
2078
  g_assert_nonnull(queue);
 
 
2079
  buffer password = {};
 
 
2080
  bool password_is_read = false;
 
 
2081
  const char helper_directory[] = "/nonexistent";
 
 
2082
  const char *const argv[] = { "/bin/true", NULL };
 
 
2084
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2085
                                    &mandos_client_exited, &quit_now,
 
 
2086
                                    &password, &password_is_read,
 
 
2087
                                    &fixture->orig_sigaction,
 
 
2088
                                    fixture->orig_sigmask,
 
 
2089
                                    helper_directory, 0, 0, argv));
 
 
2091
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
2093
  const task_context *const added_wait_task
 
 
2094
    = find_matching_task(queue, (task_context){
 
 
2095
        .func=wait_for_mandos_client_exit,
 
 
2096
        .mandos_client_exited=&mandos_client_exited,
 
 
2097
        .quit_now=&quit_now,
 
 
2099
  g_assert_nonnull(added_wait_task);
 
 
2100
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
 
2101
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
 
2102
  waitpid(added_wait_task->pid, NULL, 0);
 
 
2104
  const task_context *const added_read_task
 
 
2105
    = find_matching_task(queue, (task_context){
 
 
2106
        .func=read_mandos_client_output,
 
 
2108
        .password=&password,
 
 
2109
        .password_is_read=&password_is_read,
 
 
2110
        .quit_now=&quit_now,
 
 
2112
  g_assert_nonnull(added_read_task);
 
 
2113
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
2114
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
2115
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
 
2116
                                   EPOLLIN | EPOLLRDHUP));
 
 
2119
static bool fd_has_cloexec_and_nonblock(const int fd){
 
 
2120
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
 
2121
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
 
2122
  return ((socket_fd_flags >= 0)
 
 
2123
          and (socket_fd_flags & FD_CLOEXEC)
 
 
2124
          and (socket_file_flags >= 0)
 
 
2125
          and (socket_file_flags & O_NONBLOCK));
 
 
2128
__attribute__((const))
 
 
2129
bool is_privileged(void){
 
 
2130
  uid_t user = getuid() + 1;
 
 
2131
  if(user == 0){                /* Overflow check */
 
 
2134
  gid_t group = getuid() + 1;
 
 
2135
  if(group == 0){               /* Overflow check */
 
 
2138
  const pid_t pid = fork();
 
 
2139
  if(pid == 0){                 /* Child */
 
 
2140
    if(setresgid((uid_t)-1, group, group) == -1){
 
 
2142
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
 
2143
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
2147
    if(setresuid((uid_t)-1, user, user) == -1){
 
 
2149
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
 
2150
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
2157
  waitpid(pid, &status, 0);
 
 
2158
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
 
2164
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
 
2165
  /* Only scan for events in this eventmask */
 
 
2166
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
 
2167
  __attribute__((cleanup(cleanup_string)))
 
 
2168
    char *fdinfo_name = NULL;
 
 
2169
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
 
2170
  g_assert_cmpint(ret, >, 0);
 
 
2171
  g_assert_nonnull(fdinfo_name);
 
 
2173
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
 
2174
  g_assert_nonnull(fdinfo);
 
 
2175
  uint32_t reported_events;
 
 
2180
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
 
2183
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
 
2184
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
 
2185
              &found_fd, &reported_events) == 2){
 
 
2190
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
 
2191
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
 
2198
    /* Don't check events if none are given */
 
 
2201
  return (reported_events & eventmask) == (events & eventmask);
 
 
2204
static void test_start_mandos_client_execv(test_fixture *fixture,
 
 
2205
                                           __attribute__((unused))
 
 
2206
                                           gconstpointer user_data){
 
 
2207
  bool mandos_client_exited = false;
 
 
2208
  bool quit_now = false;
 
 
2209
  __attribute__((cleanup(cleanup_close)))
 
 
2210
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2211
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2212
  __attribute__((cleanup(cleanup_queue)))
 
 
2213
    task_queue *queue = create_queue();
 
 
2214
  g_assert_nonnull(queue);
 
 
2215
  __attribute__((cleanup(cleanup_buffer)))
 
 
2216
    buffer password = {};
 
 
2217
  const char helper_directory[] = "/nonexistent";
 
 
2218
  /* Can't execv("/", ...), so this should fail */
 
 
2219
  const char *const argv[] = { "/", NULL };
 
 
2222
    __attribute__((cleanup(cleanup_close)))
 
 
2223
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
 
2224
    g_assert_cmpint(devnull_fd, >=, 0);
 
 
2225
    __attribute__((cleanup(cleanup_close)))
 
 
2226
      const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2227
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2228
    dup2(devnull_fd, STDERR_FILENO);
 
 
2230
    const bool success = start_mandos_client(queue, epoll_fd,
 
 
2231
                                             &mandos_client_exited,
 
 
2235
                                             &fixture->orig_sigaction,
 
 
2236
                                             fixture->orig_sigmask,
 
 
2237
                                             helper_directory, 0, 0,
 
 
2239
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2240
    g_assert_true(success);
 
 
2242
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
2244
  struct timespec starttime, currtime;
 
 
2245
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2247
    queue->next_run = 0;
 
 
2248
    string_set cancelled_filenames = {};
 
 
2251
      __attribute__((cleanup(cleanup_close)))
 
 
2252
        const int devnull_fd = open("/dev/null",
 
 
2253
                                    O_WRONLY | O_CLOEXEC);
 
 
2254
      g_assert_cmpint(devnull_fd, >=, 0);
 
 
2255
      __attribute__((cleanup(cleanup_close)))
 
 
2256
        const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2257
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2258
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2259
      dup2(devnull_fd, STDERR_FILENO);
 
 
2260
      const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2262
      dup2(real_stderr_fd, STDERR_FILENO);
 
 
2267
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2268
  } while(((queue->length) > 0)
 
 
2270
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2272
  g_assert_true(quit_now);
 
 
2273
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2274
  g_assert_true(mandos_client_exited);
 
 
2277
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
 
2278
                                               __attribute__((unused))
 
 
2281
  if(not is_privileged()){
 
 
2282
    g_test_skip("Not privileged");
 
 
2286
  bool mandos_client_exited = false;
 
 
2287
  bool quit_now = false;
 
 
2288
  __attribute__((cleanup(cleanup_close)))
 
 
2289
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2290
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2291
  __attribute__((cleanup(cleanup_queue)))
 
 
2292
    task_queue *queue = create_queue();
 
 
2293
  g_assert_nonnull(queue);
 
 
2294
  __attribute__((cleanup(cleanup_buffer)))
 
 
2295
    buffer password = {};
 
 
2296
  bool password_is_read = false;
 
 
2297
  const char helper_directory[] = "/nonexistent";
 
 
2298
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
 
2302
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2303
                                           &mandos_client_exited,
 
 
2304
                                           &quit_now, &password,
 
 
2306
                                           &fixture->orig_sigaction,
 
 
2307
                                           fixture->orig_sigmask,
 
 
2308
                                           helper_directory, user,
 
 
2310
  g_assert_true(success);
 
 
2311
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2313
  struct timespec starttime, currtime;
 
 
2314
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2316
    queue->next_run = 0;
 
 
2317
    string_set cancelled_filenames = {};
 
 
2318
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2319
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2320
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2321
  } while(((queue->length) > 0)
 
 
2323
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2325
  g_assert_false(quit_now);
 
 
2326
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2327
  g_assert_true(mandos_client_exited);
 
 
2329
  g_assert_true(password_is_read);
 
 
2330
  g_assert_nonnull(password.data);
 
 
2333
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2335
  g_assert_true((uid_t)id == id);
 
 
2337
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2340
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
 
2341
                                               __attribute__((unused))
 
 
2344
  if(not is_privileged()){
 
 
2345
    g_test_skip("Not privileged");
 
 
2349
  bool mandos_client_exited = false;
 
 
2350
  bool quit_now = false;
 
 
2351
  __attribute__((cleanup(cleanup_close)))
 
 
2352
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2353
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2354
  __attribute__((cleanup(cleanup_queue)))
 
 
2355
    task_queue *queue = create_queue();
 
 
2356
  g_assert_nonnull(queue);
 
 
2357
  __attribute__((cleanup(cleanup_buffer)))
 
 
2358
    buffer password = {};
 
 
2359
  bool password_is_read = false;
 
 
2360
  const char helper_directory[] = "/nonexistent";
 
 
2361
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
 
2365
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2366
                                           &mandos_client_exited,
 
 
2367
                                           &quit_now, &password,
 
 
2369
                                           &fixture->orig_sigaction,
 
 
2370
                                           fixture->orig_sigmask,
 
 
2371
                                           helper_directory, user,
 
 
2373
  g_assert_true(success);
 
 
2374
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2376
  struct timespec starttime, currtime;
 
 
2377
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2379
    queue->next_run = 0;
 
 
2380
    string_set cancelled_filenames = {};
 
 
2381
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2382
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2383
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2384
  } while(((queue->length) > 0)
 
 
2386
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2388
  g_assert_false(quit_now);
 
 
2389
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2390
  g_assert_true(mandos_client_exited);
 
 
2392
  g_assert_true(password_is_read);
 
 
2393
  g_assert_nonnull(password.data);
 
 
2396
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2398
  g_assert_true((gid_t)id == id);
 
 
2400
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2403
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
 
2404
                                               __attribute__((unused))
 
 
2407
  if(not is_privileged()){
 
 
2408
    g_test_skip("Not privileged");
 
 
2412
  bool mandos_client_exited = false;
 
 
2413
  bool quit_now = false;
 
 
2414
  __attribute__((cleanup(cleanup_close)))
 
 
2415
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2416
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2417
  __attribute__((cleanup(cleanup_queue)))
 
 
2418
    task_queue *queue = create_queue();
 
 
2419
  g_assert_nonnull(queue);
 
 
2420
  __attribute__((cleanup(cleanup_buffer)))
 
 
2421
    buffer password = {};
 
 
2422
  bool password_is_read = false;
 
 
2423
  const char helper_directory[] = "/nonexistent";
 
 
2424
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
 
2429
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2430
                                           &mandos_client_exited,
 
 
2431
                                           &quit_now, &password,
 
 
2433
                                           &fixture->orig_sigaction,
 
 
2434
                                           fixture->orig_sigmask,
 
 
2435
                                           helper_directory, user,
 
 
2437
  g_assert_true(success);
 
 
2438
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2440
  struct timespec starttime, currtime;
 
 
2441
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2443
    queue->next_run = 0;
 
 
2444
    string_set cancelled_filenames = {};
 
 
2445
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2446
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2447
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2448
  } while(((queue->length) > 0)
 
 
2450
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2452
  g_assert_false(quit_now);
 
 
2453
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2454
  g_assert_true(mandos_client_exited);
 
 
2456
  g_assert_true(password_is_read);
 
 
2457
  g_assert_nonnull(password.data);
 
 
2460
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2462
  g_assert_true((uid_t)id == id);
 
 
2464
  g_assert_cmpuint((unsigned int)id, ==, user);
 
 
2467
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
 
2468
                                               __attribute__((unused))
 
 
2471
  if(not is_privileged()){
 
 
2472
    g_test_skip("Not privileged");
 
 
2476
  bool mandos_client_exited = false;
 
 
2477
  bool quit_now = false;
 
 
2478
  __attribute__((cleanup(cleanup_close)))
 
 
2479
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2480
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2481
  __attribute__((cleanup(cleanup_queue)))
 
 
2482
    task_queue *queue = create_queue();
 
 
2483
  g_assert_nonnull(queue);
 
 
2484
  __attribute__((cleanup(cleanup_buffer)))
 
 
2485
    buffer password = {};
 
 
2486
  bool password_is_read = false;
 
 
2487
  const char helper_directory[] = "/nonexistent";
 
 
2488
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
 
2493
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2494
                                           &mandos_client_exited,
 
 
2495
                                           &quit_now, &password,
 
 
2497
                                           &fixture->orig_sigaction,
 
 
2498
                                           fixture->orig_sigmask,
 
 
2499
                                           helper_directory, user,
 
 
2501
  g_assert_true(success);
 
 
2502
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2504
  struct timespec starttime, currtime;
 
 
2505
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2507
    queue->next_run = 0;
 
 
2508
    string_set cancelled_filenames = {};
 
 
2509
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2510
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2511
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2512
  } while(((queue->length) > 0)
 
 
2514
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2516
  g_assert_false(quit_now);
 
 
2517
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2518
  g_assert_true(mandos_client_exited);
 
 
2520
  g_assert_true(password_is_read);
 
 
2521
  g_assert_nonnull(password.data);
 
 
2524
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2526
  g_assert_true((gid_t)id == id);
 
 
2528
  g_assert_cmpuint((unsigned int)id, ==, group);
 
 
2531
static void test_start_mandos_client_read(test_fixture *fixture,
 
 
2532
                                          __attribute__((unused))
 
 
2533
                                          gconstpointer user_data){
 
 
2534
  bool mandos_client_exited = false;
 
 
2535
  bool quit_now = false;
 
 
2536
  __attribute__((cleanup(cleanup_close)))
 
 
2537
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2538
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2539
  __attribute__((cleanup(cleanup_queue)))
 
 
2540
    task_queue *queue = create_queue();
 
 
2541
  g_assert_nonnull(queue);
 
 
2542
  __attribute__((cleanup(cleanup_buffer)))
 
 
2543
    buffer password = {};
 
 
2544
  bool password_is_read = false;
 
 
2545
  const char dummy_test_password[] = "dummy test password";
 
 
2546
  const char helper_directory[] = "/nonexistent";
 
 
2547
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
 
2550
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2551
                                           &mandos_client_exited,
 
 
2552
                                           &quit_now, &password,
 
 
2554
                                           &fixture->orig_sigaction,
 
 
2555
                                           fixture->orig_sigmask,
 
 
2556
                                           helper_directory, 0, 0,
 
 
2558
  g_assert_true(success);
 
 
2559
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2561
  struct timespec starttime, currtime;
 
 
2562
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2564
    queue->next_run = 0;
 
 
2565
    string_set cancelled_filenames = {};
 
 
2566
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2567
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2568
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2569
  } while(((queue->length) > 0)
 
 
2571
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2573
  g_assert_false(quit_now);
 
 
2574
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2575
  g_assert_true(mandos_client_exited);
 
 
2577
  g_assert_true(password_is_read);
 
 
2578
  g_assert_cmpint((int)password.length, ==,
 
 
2579
                  sizeof(dummy_test_password)-1);
 
 
2580
  g_assert_nonnull(password.data);
 
 
2581
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
 
2582
                         sizeof(dummy_test_password)-1), ==, 0);
 
 
2586
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
 
2587
                                               __attribute__((unused))
 
 
2590
  bool mandos_client_exited = false;
 
 
2591
  bool quit_now = false;
 
 
2592
  __attribute__((cleanup(cleanup_close)))
 
 
2593
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2594
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2595
  __attribute__((cleanup(cleanup_queue)))
 
 
2596
    task_queue *queue = create_queue();
 
 
2597
  g_assert_nonnull(queue);
 
 
2598
  __attribute__((cleanup(cleanup_buffer)))
 
 
2599
    buffer password = {};
 
 
2600
  bool password_is_read = false;
 
 
2601
  const char helper_directory[] = "/nonexistent";
 
 
2602
  const char *const argv[] = { "/bin/sh", "-c",
 
 
2603
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
 
2605
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2606
                                           &mandos_client_exited,
 
 
2607
                                           &quit_now, &password,
 
 
2609
                                           &fixture->orig_sigaction,
 
 
2610
                                           fixture->orig_sigmask,
 
 
2611
                                           helper_directory, 0, 0,
 
 
2613
  g_assert_true(success);
 
 
2614
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2616
  struct timespec starttime, currtime;
 
 
2617
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2619
    queue->next_run = 0;
 
 
2620
    string_set cancelled_filenames = {};
 
 
2621
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2622
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2623
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2624
  } while(((queue->length) > 0)
 
 
2626
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2628
  g_assert_false(quit_now);
 
 
2629
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2630
  g_assert_true(mandos_client_exited);
 
 
2632
  g_assert_true(password_is_read);
 
 
2633
  g_assert_cmpint((int)password.length, ==,
 
 
2634
                  sizeof(helper_directory)-1);
 
 
2635
  g_assert_nonnull(password.data);
 
 
2636
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
 
2637
                         sizeof(helper_directory)-1), ==, 0);
 
 
2640
__attribute__((nonnull, warn_unused_result))
 
 
2641
static bool proc_status_sigblk_to_sigset(const char *const,
 
 
2644
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
 
2645
                                             __attribute__((unused))
 
 
2646
                                             gconstpointer user_data){
 
 
2647
  bool mandos_client_exited = false;
 
 
2648
  bool quit_now = false;
 
 
2649
  __attribute__((cleanup(cleanup_close)))
 
 
2650
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2651
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2652
  __attribute__((cleanup(cleanup_queue)))
 
 
2653
    task_queue *queue = create_queue();
 
 
2654
  g_assert_nonnull(queue);
 
 
2655
  __attribute__((cleanup(cleanup_buffer)))
 
 
2656
    buffer password = {};
 
 
2657
  bool password_is_read = false;
 
 
2658
  const char helper_directory[] = "/nonexistent";
 
 
2659
  /* see proc(5) for format of /proc/self/status */
 
 
2660
  const char *const argv[] = { "/usr/bin/awk",
 
 
2661
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
 
2663
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2664
                                    &mandos_client_exited, &quit_now,
 
 
2665
                                    &password, &password_is_read,
 
 
2666
                                    &fixture->orig_sigaction,
 
 
2667
                                    fixture->orig_sigmask,
 
 
2668
                                    helper_directory, 0, 0, argv));
 
 
2670
  struct timespec starttime, currtime;
 
 
2671
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2673
    queue->next_run = 0;
 
 
2674
    string_set cancelled_filenames = {};
 
 
2675
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2676
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2677
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2678
  } while((not (mandos_client_exited and password_is_read))
 
 
2680
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2681
  g_assert_true(mandos_client_exited);
 
 
2682
  g_assert_true(password_is_read);
 
 
2684
  sigset_t parsed_sigmask;
 
 
2685
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
 
2688
  for(int signum = 1; signum < NSIG; signum++){
 
 
2689
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
 
2690
    if(sigismember(&fixture->orig_sigmask, signum)){
 
 
2691
      g_assert_true(has_signal);
 
 
2693
      g_assert_false(has_signal);
 
 
2698
__attribute__((nonnull, warn_unused_result))
 
 
2699
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
 
2700
                                         sigset_t *const sigmask){
 
 
2701
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
 
2702
  uintmax_t scanned_sigmask;
 
 
2703
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
 
2706
  if(sigemptyset(sigmask) != 0){
 
 
2709
  for(int signum = 1; signum < NSIG; signum++){
 
 
2710
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
 
2711
      if(sigaddset(sigmask, signum) != 0){
 
 
2719
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2720
                                             task_queue *const queue);
 
 
2723
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
 
2724
                                             test_fixture *fixture,
 
 
2725
                                             __attribute__((unused))
 
 
2726
                                             gconstpointer user_data){
 
 
2728
  bool mandos_client_exited = false;
 
 
2729
  bool quit_now = false;
 
 
2731
  __attribute__((cleanup(cleanup_queue)))
 
 
2732
    task_queue *queue = create_queue();
 
 
2733
  g_assert_nonnull(queue);
 
 
2734
  const task_context task = {
 
 
2735
    .func=wait_for_mandos_client_exit,
 
 
2737
    .mandos_client_exited=&mandos_client_exited,
 
 
2738
    .quit_now=&quit_now,
 
 
2740
  run_task_with_stderr_to_dev_null(task, queue);
 
 
2742
  g_assert_false(mandos_client_exited);
 
 
2743
  g_assert_true(quit_now);
 
 
2744
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2747
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2748
                                             task_queue *const queue){
 
 
2749
  FILE *real_stderr = stderr;
 
 
2750
  FILE *devnull = fopen("/dev/null", "we");
 
 
2751
  g_assert_nonnull(devnull);
 
 
2754
  task.func(task, queue);
 
 
2755
  stderr = real_stderr;
 
 
2757
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
2761
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
 
2762
                                             __attribute__((unused))
 
 
2763
                                             gconstpointer user_data){
 
 
2764
  bool mandos_client_exited = false;
 
 
2765
  bool quit_now = false;
 
 
2767
  pid_t create_eternal_process(void){
 
 
2768
    const pid_t pid = fork();
 
 
2769
    if(pid == 0){               /* Child */
 
 
2770
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2771
        _exit(EXIT_FAILURE);
 
 
2773
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2774
        _exit(EXIT_FAILURE);
 
 
2782
  pid_t pid = create_eternal_process();
 
 
2783
  g_assert_true(pid != -1);
 
 
2785
  __attribute__((cleanup(cleanup_queue)))
 
 
2786
    task_queue *queue = create_queue();
 
 
2787
  g_assert_nonnull(queue);
 
 
2788
  const task_context task = {
 
 
2789
    .func=wait_for_mandos_client_exit,
 
 
2791
    .mandos_client_exited=&mandos_client_exited,
 
 
2792
    .quit_now=&quit_now,
 
 
2794
  task.func(task, queue);
 
 
2796
  g_assert_false(mandos_client_exited);
 
 
2797
  g_assert_false(quit_now);
 
 
2798
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
2800
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
2801
        .func=wait_for_mandos_client_exit,
 
 
2803
        .mandos_client_exited=&mandos_client_exited,
 
 
2804
        .quit_now=&quit_now,
 
 
2809
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
 
2810
                                              __attribute__((unused))
 
 
2813
  bool mandos_client_exited = false;
 
 
2814
  bool quit_now = false;
 
 
2816
  pid_t create_successful_process(void){
 
 
2817
    const pid_t pid = fork();
 
 
2818
    if(pid == 0){               /* Child */
 
 
2819
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2820
        _exit(EXIT_FAILURE);
 
 
2822
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2823
        _exit(EXIT_FAILURE);
 
 
2829
  const pid_t pid = create_successful_process();
 
 
2830
  g_assert_true(pid != -1);
 
 
2832
  __attribute__((cleanup(cleanup_queue)))
 
 
2833
    task_queue *queue = create_queue();
 
 
2834
  g_assert_nonnull(queue);
 
 
2835
  const task_context initial_task = {
 
 
2836
    .func=wait_for_mandos_client_exit,
 
 
2838
    .mandos_client_exited=&mandos_client_exited,
 
 
2839
    .quit_now=&quit_now,
 
 
2841
  g_assert_true(add_to_queue(queue, initial_task));
 
 
2843
  struct timespec starttime, currtime;
 
 
2844
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2845
  __attribute__((cleanup(cleanup_close)))
 
 
2846
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2848
    queue->next_run = 0;
 
 
2849
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2850
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
 
2851
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2852
  } while((not mandos_client_exited)
 
 
2854
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2856
  g_assert_true(mandos_client_exited);
 
 
2857
  g_assert_false(quit_now);
 
 
2858
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2862
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
 
2863
                                              __attribute__((unused))
 
 
2866
  bool mandos_client_exited = false;
 
 
2867
  bool quit_now = false;
 
 
2869
  pid_t create_failing_process(void){
 
 
2870
    const pid_t pid = fork();
 
 
2871
    if(pid == 0){               /* Child */
 
 
2872
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2873
        _exit(EXIT_FAILURE);
 
 
2875
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2876
        _exit(EXIT_FAILURE);
 
 
2882
  const pid_t pid = create_failing_process();
 
 
2883
  g_assert_true(pid != -1);
 
 
2885
  __attribute__((cleanup(string_set_clear)))
 
 
2886
    string_set cancelled_filenames = {};
 
 
2887
  __attribute__((cleanup(cleanup_close)))
 
 
2888
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2889
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2890
  __attribute__((cleanup(cleanup_queue)))
 
 
2891
    task_queue *queue = create_queue();
 
 
2892
  g_assert_nonnull(queue);
 
 
2893
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2894
        .func=wait_for_mandos_client_exit,
 
 
2896
        .mandos_client_exited=&mandos_client_exited,
 
 
2897
        .quit_now=&quit_now,
 
 
2900
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
 
2902
  __attribute__((cleanup(cleanup_close)))
 
 
2903
    const int devnull_fd = open("/dev/null",
 
 
2904
                                O_WRONLY | O_CLOEXEC);
 
 
2905
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2906
  __attribute__((cleanup(cleanup_close)))
 
 
2907
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2908
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2910
  struct timespec starttime, currtime;
 
 
2911
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2913
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2914
    dup2(devnull_fd, STDERR_FILENO);
 
 
2915
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2917
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2922
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2923
  } while((not mandos_client_exited)
 
 
2925
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2927
  g_assert_true(quit_now);
 
 
2928
  g_assert_true(mandos_client_exited);
 
 
2929
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2933
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
 
2934
                                             __attribute__((unused))
 
 
2935
                                             gconstpointer user_data){
 
 
2936
  bool mandos_client_exited = false;
 
 
2937
  bool quit_now = false;
 
 
2939
  pid_t create_killed_process(void){
 
 
2940
    const pid_t pid = fork();
 
 
2941
    if(pid == 0){               /* Child */
 
 
2942
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2943
        _exit(EXIT_FAILURE);
 
 
2945
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2946
        _exit(EXIT_FAILURE);
 
 
2955
  const pid_t pid = create_killed_process();
 
 
2956
  g_assert_true(pid != -1);
 
 
2958
  __attribute__((cleanup(string_set_clear)))
 
 
2959
    string_set cancelled_filenames = {};
 
 
2960
  __attribute__((cleanup(cleanup_close)))
 
 
2961
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2962
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2963
  __attribute__((cleanup(cleanup_queue)))
 
 
2964
    task_queue *queue = create_queue();
 
 
2965
  g_assert_nonnull(queue);
 
 
2966
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2967
        .func=wait_for_mandos_client_exit,
 
 
2969
        .mandos_client_exited=&mandos_client_exited,
 
 
2970
        .quit_now=&quit_now,
 
 
2973
  __attribute__((cleanup(cleanup_close)))
 
 
2974
    const int devnull_fd = open("/dev/null",
 
 
2975
                                O_WRONLY | O_CLOEXEC);
 
 
2976
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2977
  __attribute__((cleanup(cleanup_close)))
 
 
2978
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2979
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2981
  struct timespec starttime, currtime;
 
 
2982
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2984
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2985
    dup2(devnull_fd, STDERR_FILENO);
 
 
2986
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2988
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2993
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2994
  } while((not mandos_client_exited)
 
 
2996
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2998
  g_assert_true(mandos_client_exited);
 
 
2999
  g_assert_true(quit_now);
 
 
3000
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3003
static bool epoll_set_does_not_contain(int, int);
 
 
3006
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
 
3007
                                              test_fixture *fixture,
 
 
3008
                                              __attribute__((unused))
 
 
3011
  __attribute__((cleanup(cleanup_close)))
 
 
3012
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3013
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3015
  __attribute__((cleanup(cleanup_buffer)))
 
 
3016
    buffer password = {};
 
 
3018
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
 
3019
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
 
3021
  bool password_is_read = false;
 
 
3022
  bool quit_now = false;
 
 
3023
  __attribute__((cleanup(cleanup_queue)))
 
 
3024
    task_queue *queue = create_queue();
 
 
3025
  g_assert_nonnull(queue);
 
 
3027
  task_context task = {
 
 
3028
    .func=read_mandos_client_output,
 
 
3031
    .password=&password,
 
 
3032
    .password_is_read=&password_is_read,
 
 
3033
    .quit_now=&quit_now,
 
 
3035
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3036
  g_assert_false(password_is_read);
 
 
3037
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3038
  g_assert_true(quit_now);
 
 
3039
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3041
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3043
  g_assert_cmpint(close(fd), ==, -1);
 
 
3046
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
 
3047
  return not epoll_set_contains(epoll_fd, fd, 0);
 
 
3051
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
 
3052
                                           test_fixture *fixture,
 
 
3053
                                           __attribute__((unused))
 
 
3054
                                           gconstpointer user_data){
 
 
3055
  __attribute__((cleanup(cleanup_close)))
 
 
3056
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3057
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3060
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3062
  __attribute__((cleanup(cleanup_buffer)))
 
 
3063
    buffer password = {};
 
 
3065
  bool password_is_read = false;
 
 
3066
  bool quit_now = false;
 
 
3067
  __attribute__((cleanup(cleanup_queue)))
 
 
3068
    task_queue *queue = create_queue();
 
 
3069
  g_assert_nonnull(queue);
 
 
3071
  task_context task = {
 
 
3072
    .func=read_mandos_client_output,
 
 
3075
    .password=&password,
 
 
3076
    .password_is_read=&password_is_read,
 
 
3077
    .quit_now=&quit_now,
 
 
3079
  task.func(task, queue);
 
 
3080
  g_assert_false(password_is_read);
 
 
3081
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3082
  g_assert_false(quit_now);
 
 
3083
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3085
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3086
        .func=read_mandos_client_output,
 
 
3089
        .password=&password,
 
 
3090
        .password_is_read=&password_is_read,
 
 
3091
        .quit_now=&quit_now,
 
 
3094
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3095
                                   EPOLLIN | EPOLLRDHUP));
 
 
3097
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3100
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
 
3101
                                               test_fixture *fixture,
 
 
3102
                                               __attribute__((unused))
 
 
3105
  __attribute__((cleanup(cleanup_close)))
 
 
3106
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3107
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3110
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3111
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3113
  __attribute__((cleanup(cleanup_buffer)))
 
 
3114
    buffer password = {};
 
 
3116
  bool password_is_read = false;
 
 
3117
  bool quit_now = false;
 
 
3118
  __attribute__((cleanup(cleanup_queue)))
 
 
3119
    task_queue *queue = create_queue();
 
 
3120
  g_assert_nonnull(queue);
 
 
3122
  task_context task = {
 
 
3123
    .func=read_mandos_client_output,
 
 
3126
    .password=&password,
 
 
3127
    .password_is_read=&password_is_read,
 
 
3128
    .quit_now=&quit_now,
 
 
3130
  task.func(task, queue);
 
 
3131
  g_assert_true(password_is_read);
 
 
3132
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3133
  g_assert_false(quit_now);
 
 
3134
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3136
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3138
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3142
void test_read_mandos_client_output_once(__attribute__((unused))
 
 
3143
                                         test_fixture *fixture,
 
 
3144
                                         __attribute__((unused))
 
 
3145
                                         gconstpointer user_data){
 
 
3146
  __attribute__((cleanup(cleanup_close)))
 
 
3147
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3148
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3151
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3153
  const char dummy_test_password[] = "dummy test password";
 
 
3154
  /* Start with a pre-allocated buffer */
 
 
3155
  __attribute__((cleanup(cleanup_buffer)))
 
 
3157
    .data=malloc(sizeof(dummy_test_password)),
 
 
3159
    .allocated=sizeof(dummy_test_password),
 
 
3161
  g_assert_nonnull(password.data);
 
 
3162
  if(mlock(password.data, password.allocated) != 0){
 
 
3163
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3166
  bool password_is_read = false;
 
 
3167
  bool quit_now = false;
 
 
3168
  __attribute__((cleanup(cleanup_queue)))
 
 
3169
    task_queue *queue = create_queue();
 
 
3170
  g_assert_nonnull(queue);
 
 
3172
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3173
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3174
                             sizeof(dummy_test_password)),
 
 
3175
                  ==, (int)sizeof(dummy_test_password));
 
 
3177
  task_context task = {
 
 
3178
    .func=read_mandos_client_output,
 
 
3181
    .password=&password,
 
 
3182
    .password_is_read=&password_is_read,
 
 
3183
    .quit_now=&quit_now,
 
 
3185
  task.func(task, queue);
 
 
3187
  g_assert_false(password_is_read);
 
 
3188
  g_assert_cmpint((int)password.length, ==,
 
 
3189
                  (int)sizeof(dummy_test_password));
 
 
3190
  g_assert_nonnull(password.data);
 
 
3191
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3192
                         sizeof(dummy_test_password)), ==, 0);
 
 
3194
  g_assert_false(quit_now);
 
 
3195
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3197
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3198
        .func=read_mandos_client_output,
 
 
3201
        .password=&password,
 
 
3202
        .password_is_read=&password_is_read,
 
 
3203
        .quit_now=&quit_now,
 
 
3206
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3207
                                   EPOLLIN | EPOLLRDHUP));
 
 
3209
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3213
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
 
3214
                                           test_fixture *fixture,
 
 
3215
                                           __attribute__((unused))
 
 
3216
                                           gconstpointer user_data){
 
 
3217
  __attribute__((cleanup(cleanup_close)))
 
 
3218
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3219
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3222
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3224
  const char dummy_test_password[] = "dummy test password";
 
 
3225
  /* Start with an empty buffer */
 
 
3226
  __attribute__((cleanup(cleanup_buffer)))
 
 
3227
    buffer password = {};
 
 
3229
  bool password_is_read = false;
 
 
3230
  bool quit_now = false;
 
 
3231
  __attribute__((cleanup(cleanup_queue)))
 
 
3232
    task_queue *queue = create_queue();
 
 
3233
  g_assert_nonnull(queue);
 
 
3235
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3236
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3237
                             sizeof(dummy_test_password)),
 
 
3238
                  ==, (int)sizeof(dummy_test_password));
 
 
3240
  task_context task = {
 
 
3241
    .func=read_mandos_client_output,
 
 
3244
    .password=&password,
 
 
3245
    .password_is_read=&password_is_read,
 
 
3246
    .quit_now=&quit_now,
 
 
3248
  task.func(task, queue);
 
 
3250
  g_assert_false(password_is_read);
 
 
3251
  g_assert_cmpint((int)password.length, ==,
 
 
3252
                  (int)sizeof(dummy_test_password));
 
 
3253
  g_assert_nonnull(password.data);
 
 
3254
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3255
                         sizeof(dummy_test_password)), ==, 0);
 
 
3257
  g_assert_false(quit_now);
 
 
3258
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3260
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3261
        .func=read_mandos_client_output,
 
 
3264
        .password=&password,
 
 
3265
        .password_is_read=&password_is_read,
 
 
3266
        .quit_now=&quit_now,
 
 
3269
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3270
                                   EPOLLIN | EPOLLRDHUP));
 
 
3272
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3276
void test_read_mandos_client_output_append(__attribute__((unused))
 
 
3277
                                           test_fixture *fixture,
 
 
3278
                                           __attribute__((unused))
 
 
3279
                                           gconstpointer user_data){
 
 
3280
  __attribute__((cleanup(cleanup_close)))
 
 
3281
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3282
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3285
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3287
  const char dummy_test_password[] = "dummy test password";
 
 
3288
  __attribute__((cleanup(cleanup_buffer)))
 
 
3290
    .data=malloc(PIPE_BUF),
 
 
3292
    .allocated=PIPE_BUF,
 
 
3294
  g_assert_nonnull(password.data);
 
 
3295
  if(mlock(password.data, password.allocated) != 0){
 
 
3296
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3299
  memset(password.data, 'x', PIPE_BUF);
 
 
3300
  char password_expected[PIPE_BUF];
 
 
3301
  memcpy(password_expected, password.data, PIPE_BUF);
 
 
3303
  bool password_is_read = false;
 
 
3304
  bool quit_now = false;
 
 
3305
  __attribute__((cleanup(cleanup_queue)))
 
 
3306
    task_queue *queue = create_queue();
 
 
3307
  g_assert_nonnull(queue);
 
 
3309
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3310
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3311
                             sizeof(dummy_test_password)),
 
 
3312
                  ==, (int)sizeof(dummy_test_password));
 
 
3314
  task_context task = {
 
 
3315
    .func=read_mandos_client_output,
 
 
3318
    .password=&password,
 
 
3319
    .password_is_read=&password_is_read,
 
 
3320
    .quit_now=&quit_now,
 
 
3322
  task.func(task, queue);
 
 
3324
  g_assert_false(password_is_read);
 
 
3325
  g_assert_cmpint((int)password.length, ==,
 
 
3326
                  PIPE_BUF + sizeof(dummy_test_password));
 
 
3327
  g_assert_nonnull(password.data);
 
 
3328
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
 
3330
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
 
3331
                         dummy_test_password,
 
 
3332
                         sizeof(dummy_test_password)), ==, 0);
 
 
3333
  g_assert_false(quit_now);
 
 
3334
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3336
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3337
        .func=read_mandos_client_output,
 
 
3340
        .password=&password,
 
 
3341
        .password_is_read=&password_is_read,
 
 
3342
        .quit_now=&quit_now,
 
 
3345
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3346
                                   EPOLLIN | EPOLLRDHUP));
 
 
3349
static char *make_temporary_directory(void);
 
 
3351
static void test_add_inotify_dir_watch(__attribute__((unused))
 
 
3352
                                       test_fixture *fixture,
 
 
3353
                                       __attribute__((unused))
 
 
3354
                                       gconstpointer user_data){
 
 
3355
  __attribute__((cleanup(cleanup_close)))
 
 
3356
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3357
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3358
  __attribute__((cleanup(cleanup_queue)))
 
 
3359
    task_queue *queue = create_queue();
 
 
3360
  g_assert_nonnull(queue);
 
 
3361
  __attribute__((cleanup(string_set_clear)))
 
 
3362
    string_set cancelled_filenames = {};
 
 
3363
  const mono_microsecs current_time = 0;
 
 
3365
  bool quit_now = false;
 
 
3366
  buffer password = {};
 
 
3367
  bool mandos_client_exited = false;
 
 
3368
  bool password_is_read = false;
 
 
3370
  __attribute__((cleanup(cleanup_string)))
 
 
3371
    char *tempdir = make_temporary_directory();
 
 
3372
  g_assert_nonnull(tempdir);
 
 
3374
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3376
                                      &cancelled_filenames,
 
 
3378
                                      &mandos_client_exited,
 
 
3379
                                      &password_is_read));
 
 
3381
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3383
  const task_context *const added_read_task
 
 
3384
    = find_matching_task(queue, (task_context){
 
 
3385
        .func=read_inotify_event,
 
 
3387
        .quit_now=&quit_now,
 
 
3388
        .password=&password,
 
 
3390
        .cancelled_filenames=&cancelled_filenames,
 
 
3391
        .current_time=¤t_time,
 
 
3392
        .mandos_client_exited=&mandos_client_exited,
 
 
3393
        .password_is_read=&password_is_read,
 
 
3395
  g_assert_nonnull(added_read_task);
 
 
3397
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3398
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3399
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
 
3400
                                   added_read_task->fd,
 
 
3401
                                   EPOLLIN | EPOLLRDHUP));
 
 
3403
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3406
static char *make_temporary_directory(void){
 
 
3407
  char *name = strdup("/tmp/mandosXXXXXX");
 
 
3408
  g_assert_nonnull(name);
 
 
3409
  char *result = mkdtemp(name);
 
 
3416
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
 
3417
                                            test_fixture *fixture,
 
 
3418
                                            __attribute__((unused))
 
 
3419
                                            gconstpointer user_data){
 
 
3420
  __attribute__((cleanup(cleanup_close)))
 
 
3421
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3422
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3423
  __attribute__((cleanup(cleanup_queue)))
 
 
3424
    task_queue *queue = create_queue();
 
 
3425
  g_assert_nonnull(queue);
 
 
3426
  __attribute__((cleanup(string_set_clear)))
 
 
3427
    string_set cancelled_filenames = {};
 
 
3428
  const mono_microsecs current_time = 0;
 
 
3430
  bool quit_now = false;
 
 
3431
  buffer password = {};
 
 
3432
  bool mandos_client_exited = false;
 
 
3433
  bool password_is_read = false;
 
 
3435
  const char nonexistent_dir[] = "/nonexistent";
 
 
3437
  FILE *real_stderr = stderr;
 
 
3438
  FILE *devnull = fopen("/dev/null", "we");
 
 
3439
  g_assert_nonnull(devnull);
 
 
3441
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3442
                                       &password, nonexistent_dir,
 
 
3443
                                       &cancelled_filenames,
 
 
3445
                                       &mandos_client_exited,
 
 
3446
                                       &password_is_read));
 
 
3447
  stderr = real_stderr;
 
 
3448
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3450
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3453
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
 
3454
                                              test_fixture *fixture,
 
 
3455
                                              __attribute__((unused))
 
 
3458
  __attribute__((cleanup(cleanup_close)))
 
 
3459
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3460
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3461
  __attribute__((cleanup(cleanup_queue)))
 
 
3462
    task_queue *queue = create_queue();
 
 
3463
  g_assert_nonnull(queue);
 
 
3464
  __attribute__((cleanup(string_set_clear)))
 
 
3465
    string_set cancelled_filenames = {};
 
 
3466
  const mono_microsecs current_time = 0;
 
 
3468
  bool quit_now = false;
 
 
3469
  buffer password = {};
 
 
3470
  bool mandos_client_exited = false;
 
 
3471
  bool password_is_read = false;
 
 
3473
  __attribute__((cleanup(cleanup_string)))
 
 
3474
    char *tempdir = make_temporary_directory();
 
 
3475
  g_assert_nonnull(tempdir);
 
 
3477
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3479
                                      &cancelled_filenames,
 
 
3481
                                      &mandos_client_exited,
 
 
3482
                                      &password_is_read));
 
 
3484
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3486
  const task_context *const added_read_task
 
 
3487
    = find_matching_task(queue,
 
 
3488
                         (task_context){ .func=read_inotify_event });
 
 
3489
  g_assert_nonnull(added_read_task);
 
 
3491
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3492
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3494
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3495
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3497
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3498
  g_assert_nonnull(ievent);
 
 
3500
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
 
3502
  g_assert_cmpint(errno, ==, EAGAIN);
 
 
3506
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3509
static char *make_temporary_file_in_directory(const char
 
 
3513
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3514
                                               test_fixture *fixture,
 
 
3515
                                               __attribute__((unused))
 
 
3518
  __attribute__((cleanup(cleanup_close)))
 
 
3519
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3520
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3521
  __attribute__((cleanup(cleanup_queue)))
 
 
3522
    task_queue *queue = create_queue();
 
 
3523
  g_assert_nonnull(queue);
 
 
3524
  __attribute__((cleanup(string_set_clear)))
 
 
3525
    string_set cancelled_filenames = {};
 
 
3526
  const mono_microsecs current_time = 0;
 
 
3528
  bool quit_now = false;
 
 
3529
  buffer password = {};
 
 
3530
  bool mandos_client_exited = false;
 
 
3531
  bool password_is_read = false;
 
 
3533
  __attribute__((cleanup(cleanup_string)))
 
 
3534
    char *tempdir = make_temporary_directory();
 
 
3535
  g_assert_nonnull(tempdir);
 
 
3537
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3539
                                      &cancelled_filenames,
 
 
3541
                                      &mandos_client_exited,
 
 
3542
                                      &password_is_read));
 
 
3544
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3546
  const task_context *const added_read_task
 
 
3547
    = find_matching_task(queue,
 
 
3548
                         (task_context){ .func=read_inotify_event });
 
 
3549
  g_assert_nonnull(added_read_task);
 
 
3551
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3552
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3554
  __attribute__((cleanup(cleanup_string)))
 
 
3555
    char *filename = make_temporary_file_in_directory(tempdir);
 
 
3556
  g_assert_nonnull(filename);
 
 
3558
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3559
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3561
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3562
  g_assert_nonnull(ievent);
 
 
3564
  ssize_t read_size = 0;
 
 
3565
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3567
  g_assert_cmpint((int)read_size, >, 0);
 
 
3568
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
 
3569
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
 
3573
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
3574
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3577
static char *make_temporary_prefixed_file_in_directory(const char
 
 
3581
  char *filename = NULL;
 
 
3582
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
 
3584
  g_assert_nonnull(filename);
 
 
3585
  const int fd = mkostemp(filename, O_CLOEXEC);
 
 
3586
  g_assert_cmpint(fd, >=, 0);
 
 
3587
  g_assert_cmpint(close(fd), ==, 0);
 
 
3591
static char *make_temporary_file_in_directory(const char
 
 
3593
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
 
3597
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
 
3598
                                            test_fixture *fixture,
 
 
3599
                                            __attribute__((unused))
 
 
3600
                                            gconstpointer user_data){
 
 
3601
  __attribute__((cleanup(cleanup_close)))
 
 
3602
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3603
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3604
  __attribute__((cleanup(cleanup_queue)))
 
 
3605
    task_queue *queue = create_queue();
 
 
3606
  g_assert_nonnull(queue);
 
 
3607
  __attribute__((cleanup(string_set_clear)))
 
 
3608
    string_set cancelled_filenames = {};
 
 
3609
  const mono_microsecs current_time = 0;
 
 
3611
  bool quit_now = false;
 
 
3612
  buffer password = {};
 
 
3613
  bool mandos_client_exited = false;
 
 
3614
  bool password_is_read = false;
 
 
3616
  __attribute__((cleanup(cleanup_string)))
 
 
3617
    char *watchdir = make_temporary_directory();
 
 
3618
  g_assert_nonnull(watchdir);
 
 
3620
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3621
                                      &password, watchdir,
 
 
3622
                                      &cancelled_filenames,
 
 
3624
                                      &mandos_client_exited,
 
 
3625
                                      &password_is_read));
 
 
3627
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3629
  const task_context *const added_read_task
 
 
3630
    = find_matching_task(queue,
 
 
3631
                         (task_context){ .func=read_inotify_event });
 
 
3632
  g_assert_nonnull(added_read_task);
 
 
3634
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3635
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3637
  char *sourcedir = make_temporary_directory();
 
 
3638
  g_assert_nonnull(sourcedir);
 
 
3640
  __attribute__((cleanup(cleanup_string)))
 
 
3641
    char *filename = make_temporary_file_in_directory(sourcedir);
 
 
3642
  g_assert_nonnull(filename);
 
 
3644
  __attribute__((cleanup(cleanup_string)))
 
 
3645
    char *targetfilename = NULL;
 
 
3646
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
 
3647
                           basename(filename)), >, 0);
 
 
3648
  g_assert_nonnull(targetfilename);
 
 
3650
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
 
3651
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
 
3654
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3655
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3657
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3658
  g_assert_nonnull(ievent);
 
 
3660
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3662
  g_assert_cmpint((int)read_size, >, 0);
 
 
3663
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
 
3664
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
 
3668
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3669
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
 
3673
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
 
3674
                                          test_fixture *fixture,
 
 
3675
                                          __attribute__((unused))
 
 
3676
                                          gconstpointer user_data){
 
 
3677
  __attribute__((cleanup(cleanup_close)))
 
 
3678
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3679
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3680
  __attribute__((cleanup(cleanup_queue)))
 
 
3681
    task_queue *queue = create_queue();
 
 
3682
  g_assert_nonnull(queue);
 
 
3683
  __attribute__((cleanup(string_set_clear)))
 
 
3684
    string_set cancelled_filenames = {};
 
 
3685
  const mono_microsecs current_time = 0;
 
 
3687
  bool quit_now = false;
 
 
3688
  buffer password = {};
 
 
3689
  bool mandos_client_exited = false;
 
 
3690
  bool password_is_read = false;
 
 
3692
  __attribute__((cleanup(cleanup_string)))
 
 
3693
    char *tempdir = make_temporary_directory();
 
 
3694
  g_assert_nonnull(tempdir);
 
 
3696
  __attribute__((cleanup(cleanup_string)))
 
 
3697
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3698
  g_assert_nonnull(tempfile);
 
 
3700
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3702
                                      &cancelled_filenames,
 
 
3704
                                      &mandos_client_exited,
 
 
3705
                                      &password_is_read));
 
 
3706
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3708
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3710
  const task_context *const added_read_task
 
 
3711
    = find_matching_task(queue,
 
 
3712
                         (task_context){ .func=read_inotify_event });
 
 
3713
  g_assert_nonnull(added_read_task);
 
 
3715
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3716
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3718
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3719
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3721
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3722
  g_assert_nonnull(ievent);
 
 
3724
  ssize_t read_size = 0;
 
 
3725
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3727
  g_assert_cmpint((int)read_size, >, 0);
 
 
3728
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3729
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3733
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3736
static void test_read_inotify_event_readerror(__attribute__((unused))
 
 
3737
                                              test_fixture *fixture,
 
 
3738
                                              __attribute__((unused))
 
 
3741
  __attribute__((cleanup(cleanup_close)))
 
 
3742
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3743
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3744
  const mono_microsecs current_time = 0;
 
 
3746
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
 
3747
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
 
3749
  bool quit_now = false;
 
 
3750
  __attribute__((cleanup(cleanup_queue)))
 
 
3751
    task_queue *queue = create_queue();
 
 
3752
  g_assert_nonnull(queue);
 
 
3754
  task_context task = {
 
 
3755
    .func=read_inotify_event,
 
 
3758
    .quit_now=&quit_now,
 
 
3759
    .filename=strdup("/nonexistent"),
 
 
3760
    .cancelled_filenames = &(string_set){},
 
 
3762
    .current_time=¤t_time,
 
 
3764
  g_assert_nonnull(task.filename);
 
 
3765
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3766
  g_assert_true(quit_now);
 
 
3767
  g_assert_true(queue->next_run == 0);
 
 
3768
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3770
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3772
  g_assert_cmpint(close(fd), ==, -1);
 
 
3775
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
 
3776
                                              test_fixture *fixture,
 
 
3777
                                              __attribute__((unused))
 
 
3780
  const mono_microsecs current_time = 17;
 
 
3783
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3784
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
 
3786
  bool quit_now = false;
 
 
3787
  buffer password = {};
 
 
3788
  bool mandos_client_exited = false;
 
 
3789
  bool password_is_read = false;
 
 
3790
  __attribute__((cleanup(cleanup_queue)))
 
 
3791
    task_queue *queue = create_queue();
 
 
3792
  g_assert_nonnull(queue);
 
 
3794
  task_context task = {
 
 
3795
    .func=read_inotify_event,
 
 
3798
    .quit_now=&quit_now,
 
 
3799
    .password=&password,
 
 
3800
    .filename=strdup("/nonexistent"),
 
 
3801
    .cancelled_filenames = &(string_set){},
 
 
3803
    .current_time=¤t_time,
 
 
3804
    .mandos_client_exited=&mandos_client_exited,
 
 
3805
    .password_is_read=&password_is_read,
 
 
3807
  g_assert_nonnull(task.filename);
 
 
3808
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3810
  g_assert_nonnull(find_matching_task(queue, task));
 
 
3811
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
3813
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
 
3814
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3817
static void test_read_inotify_event_nodata(__attribute__((unused))
 
 
3818
                                           test_fixture *fixture,
 
 
3819
                                           __attribute__((unused))
 
 
3820
                                           gconstpointer user_data){
 
 
3821
  __attribute__((cleanup(cleanup_close)))
 
 
3822
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3823
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3824
  const mono_microsecs current_time = 0;
 
 
3827
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3829
  bool quit_now = false;
 
 
3830
  buffer password = {};
 
 
3831
  bool mandos_client_exited = false;
 
 
3832
  bool password_is_read = false;
 
 
3833
  __attribute__((cleanup(cleanup_queue)))
 
 
3834
    task_queue *queue = create_queue();
 
 
3835
  g_assert_nonnull(queue);
 
 
3837
  task_context task = {
 
 
3838
    .func=read_inotify_event,
 
 
3841
    .quit_now=&quit_now,
 
 
3842
    .password=&password,
 
 
3843
    .filename=strdup("/nonexistent"),
 
 
3844
    .cancelled_filenames = &(string_set){},
 
 
3846
    .current_time=¤t_time,
 
 
3847
    .mandos_client_exited=&mandos_client_exited,
 
 
3848
    .password_is_read=&password_is_read,
 
 
3850
  g_assert_nonnull(task.filename);
 
 
3851
  task.func(task, queue);
 
 
3852
  g_assert_false(quit_now);
 
 
3853
  g_assert_true(queue->next_run == 0);
 
 
3854
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3856
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3857
        .func=read_inotify_event,
 
 
3860
        .quit_now=&quit_now,
 
 
3861
        .password=&password,
 
 
3862
        .filename=task.filename,
 
 
3863
        .cancelled_filenames=task.cancelled_filenames,
 
 
3864
        .current_time=¤t_time,
 
 
3865
        .mandos_client_exited=&mandos_client_exited,
 
 
3866
        .password_is_read=&password_is_read,
 
 
3869
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3870
                                   EPOLLIN | EPOLLRDHUP));
 
 
3872
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3875
static void test_read_inotify_event_eof(__attribute__((unused))
 
 
3876
                                        test_fixture *fixture,
 
 
3877
                                        __attribute__((unused))
 
 
3878
                                        gconstpointer user_data){
 
 
3879
  __attribute__((cleanup(cleanup_close)))
 
 
3880
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3881
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3882
  const mono_microsecs current_time = 0;
 
 
3885
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3886
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3888
  bool quit_now = false;
 
 
3889
  buffer password = {};
 
 
3890
  __attribute__((cleanup(cleanup_queue)))
 
 
3891
    task_queue *queue = create_queue();
 
 
3892
  g_assert_nonnull(queue);
 
 
3894
  task_context task = {
 
 
3895
    .func=read_inotify_event,
 
 
3898
    .quit_now=&quit_now,
 
 
3899
    .password=&password,
 
 
3900
    .filename=strdup("/nonexistent"),
 
 
3901
    .cancelled_filenames = &(string_set){},
 
 
3903
    .current_time=¤t_time,
 
 
3905
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3906
  g_assert_true(quit_now);
 
 
3907
  g_assert_true(queue->next_run == 0);
 
 
3908
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3910
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3912
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3916
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3917
                                            test_fixture *fixture,
 
 
3918
                                            __attribute__((unused))
 
 
3919
                                            gconstpointer user_data){
 
 
3920
  __attribute__((cleanup(cleanup_close)))
 
 
3921
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3922
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3923
  const mono_microsecs current_time = 0;
 
 
3926
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3928
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3929
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
3931
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
3932
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
3933
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
3936
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
3937
  ievent->mask = IN_CLOSE_WRITE;
 
 
3938
  ievent->len = sizeof(dummy_file_name);
 
 
3939
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
3940
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3941
                              + sizeof(dummy_file_name));
 
 
3942
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
3944
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3946
  bool quit_now = false;
 
 
3947
  buffer password = {};
 
 
3948
  bool mandos_client_exited = false;
 
 
3949
  bool password_is_read = false;
 
 
3950
  __attribute__((cleanup(cleanup_queue)))
 
 
3951
    task_queue *queue = create_queue();
 
 
3952
  g_assert_nonnull(queue);
 
 
3954
  task_context task = {
 
 
3955
    .func=read_inotify_event,
 
 
3958
    .quit_now=&quit_now,
 
 
3959
    .password=&password,
 
 
3960
    .filename=strdup("/nonexistent"),
 
 
3961
    .cancelled_filenames = &(string_set){},
 
 
3963
    .current_time=¤t_time,
 
 
3964
    .mandos_client_exited=&mandos_client_exited,
 
 
3965
    .password_is_read=&password_is_read,
 
 
3967
  task.func(task, queue);
 
 
3968
  g_assert_false(quit_now);
 
 
3969
  g_assert_true(queue->next_run != 0);
 
 
3970
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
3972
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3973
        .func=read_inotify_event,
 
 
3976
        .quit_now=&quit_now,
 
 
3977
        .password=&password,
 
 
3978
        .filename=task.filename,
 
 
3979
        .cancelled_filenames=task.cancelled_filenames,
 
 
3980
        .current_time=¤t_time,
 
 
3981
        .mandos_client_exited=&mandos_client_exited,
 
 
3982
        .password_is_read=&password_is_read,
 
 
3985
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3986
                                   EPOLLIN | EPOLLRDHUP));
 
 
3988
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
3990
  __attribute__((cleanup(cleanup_string)))
 
 
3991
    char *filename = NULL;
 
 
3992
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
3993
                           dummy_file_name), >, 0);
 
 
3994
  g_assert_nonnull(filename);
 
 
3995
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3996
        .func=open_and_parse_question,
 
 
3999
        .question_filename=filename,
 
 
4000
        .password=&password,
 
 
4001
        .cancelled_filenames=task.cancelled_filenames,
 
 
4002
        .current_time=¤t_time,
 
 
4003
        .mandos_client_exited=&mandos_client_exited,
 
 
4004
        .password_is_read=&password_is_read,
 
 
4009
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
 
4010
                                         test_fixture *fixture,
 
 
4011
                                         __attribute__((unused))
 
 
4012
                                         gconstpointer user_data){
 
 
4013
  __attribute__((cleanup(cleanup_close)))
 
 
4014
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4015
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4016
  const mono_microsecs current_time = 0;
 
 
4019
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4021
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4022
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4024
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4025
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
4026
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
4029
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4030
  ievent->mask = IN_MOVED_TO;
 
 
4031
  ievent->len = sizeof(dummy_file_name);
 
 
4032
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4033
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4034
                              + sizeof(dummy_file_name));
 
 
4035
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
4037
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4039
  bool quit_now = false;
 
 
4040
  buffer password = {};
 
 
4041
  bool mandos_client_exited = false;
 
 
4042
  bool password_is_read = false;
 
 
4043
  __attribute__((cleanup(cleanup_queue)))
 
 
4044
    task_queue *queue = create_queue();
 
 
4045
  g_assert_nonnull(queue);
 
 
4047
  task_context task = {
 
 
4048
    .func=read_inotify_event,
 
 
4051
    .quit_now=&quit_now,
 
 
4052
    .password=&password,
 
 
4053
    .filename=strdup("/nonexistent"),
 
 
4054
    .cancelled_filenames = &(string_set){},
 
 
4056
    .current_time=¤t_time,
 
 
4057
    .mandos_client_exited=&mandos_client_exited,
 
 
4058
    .password_is_read=&password_is_read,
 
 
4060
  task.func(task, queue);
 
 
4061
  g_assert_false(quit_now);
 
 
4062
  g_assert_true(queue->next_run != 0);
 
 
4063
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4065
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4066
        .func=read_inotify_event,
 
 
4069
        .quit_now=&quit_now,
 
 
4070
        .password=&password,
 
 
4071
        .filename=task.filename,
 
 
4072
        .cancelled_filenames=task.cancelled_filenames,
 
 
4073
        .current_time=¤t_time,
 
 
4074
        .mandos_client_exited=&mandos_client_exited,
 
 
4075
        .password_is_read=&password_is_read,
 
 
4078
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4079
                                   EPOLLIN | EPOLLRDHUP));
 
 
4081
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4083
  __attribute__((cleanup(cleanup_string)))
 
 
4084
    char *filename = NULL;
 
 
4085
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4086
                           dummy_file_name), >, 0);
 
 
4087
  g_assert_nonnull(filename);
 
 
4088
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4089
        .func=open_and_parse_question,
 
 
4092
        .question_filename=filename,
 
 
4093
        .password=&password,
 
 
4094
        .cancelled_filenames=task.cancelled_filenames,
 
 
4095
        .current_time=¤t_time,
 
 
4096
        .mandos_client_exited=&mandos_client_exited,
 
 
4097
        .password_is_read=&password_is_read,
 
 
4101
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
 
4102
                                              test_fixture *fixture,
 
 
4103
                                              __attribute__((unused))
 
 
4106
  __attribute__((cleanup(cleanup_close)))
 
 
4107
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4108
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4109
  __attribute__((cleanup(string_set_clear)))
 
 
4110
    string_set cancelled_filenames = {};
 
 
4111
  const mono_microsecs current_time = 0;
 
 
4114
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4116
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4117
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4119
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4120
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
4121
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
4124
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4125
  ievent->mask = IN_DELETE;
 
 
4126
  ievent->len = sizeof(dummy_file_name);
 
 
4127
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4128
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4129
                              + sizeof(dummy_file_name));
 
 
4130
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
4132
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4134
  bool quit_now = false;
 
 
4135
  buffer password = {};
 
 
4136
  bool mandos_client_exited = false;
 
 
4137
  bool password_is_read = false;
 
 
4138
  __attribute__((cleanup(cleanup_queue)))
 
 
4139
    task_queue *queue = create_queue();
 
 
4140
  g_assert_nonnull(queue);
 
 
4142
  task_context task = {
 
 
4143
    .func=read_inotify_event,
 
 
4146
    .quit_now=&quit_now,
 
 
4147
    .password=&password,
 
 
4148
    .filename=strdup("/nonexistent"),
 
 
4149
    .cancelled_filenames=&cancelled_filenames,
 
 
4150
    .current_time=¤t_time,
 
 
4151
    .mandos_client_exited=&mandos_client_exited,
 
 
4152
    .password_is_read=&password_is_read,
 
 
4154
  task.func(task, queue);
 
 
4155
  g_assert_false(quit_now);
 
 
4156
  g_assert_true(queue->next_run == 0);
 
 
4157
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4159
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4160
        .func=read_inotify_event,
 
 
4163
        .quit_now=&quit_now,
 
 
4164
        .password=&password,
 
 
4165
        .filename=task.filename,
 
 
4166
        .cancelled_filenames=&cancelled_filenames,
 
 
4167
        .current_time=¤t_time,
 
 
4168
        .mandos_client_exited=&mandos_client_exited,
 
 
4169
        .password_is_read=&password_is_read,
 
 
4172
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4173
                                   EPOLLIN | EPOLLRDHUP));
 
 
4175
  __attribute__((cleanup(cleanup_string)))
 
 
4176
    char *filename = NULL;
 
 
4177
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4178
                           dummy_file_name), >, 0);
 
 
4179
  g_assert_nonnull(filename);
 
 
4180
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4185
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
 
4186
                                               test_fixture *fixture,
 
 
4187
                                               __attribute__((unused))
 
 
4190
  __attribute__((cleanup(cleanup_close)))
 
 
4191
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4192
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4193
  const mono_microsecs current_time = 0;
 
 
4196
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4198
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4199
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4201
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4202
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
4203
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
4206
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4207
  ievent->mask = IN_CLOSE_WRITE;
 
 
4208
  ievent->len = sizeof(dummy_file_name);
 
 
4209
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4210
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4211
                              + sizeof(dummy_file_name));
 
 
4212
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
4214
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4216
  bool quit_now = false;
 
 
4217
  buffer password = {};
 
 
4218
  bool mandos_client_exited = false;
 
 
4219
  bool password_is_read = false;
 
 
4220
  __attribute__((cleanup(cleanup_queue)))
 
 
4221
    task_queue *queue = create_queue();
 
 
4222
  g_assert_nonnull(queue);
 
 
4224
  task_context task = {
 
 
4225
    .func=read_inotify_event,
 
 
4228
    .quit_now=&quit_now,
 
 
4229
    .password=&password,
 
 
4230
    .filename=strdup("/nonexistent"),
 
 
4231
    .cancelled_filenames = &(string_set){},
 
 
4233
    .current_time=¤t_time,
 
 
4234
    .mandos_client_exited=&mandos_client_exited,
 
 
4235
    .password_is_read=&password_is_read,
 
 
4237
  task.func(task, queue);
 
 
4238
  g_assert_false(quit_now);
 
 
4239
  g_assert_true(queue->next_run == 0);
 
 
4240
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4242
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4243
        .func=read_inotify_event,
 
 
4246
        .quit_now=&quit_now,
 
 
4247
        .password=&password,
 
 
4248
        .filename=task.filename,
 
 
4249
        .cancelled_filenames=task.cancelled_filenames,
 
 
4250
        .current_time=¤t_time,
 
 
4251
        .mandos_client_exited=&mandos_client_exited,
 
 
4252
        .password_is_read=&password_is_read,
 
 
4255
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4256
                                   EPOLLIN | EPOLLRDHUP));
 
 
4260
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
 
4261
                                            test_fixture *fixture,
 
 
4262
                                            __attribute__((unused))
 
 
4263
                                            gconstpointer user_data){
 
 
4264
  __attribute__((cleanup(cleanup_close)))
 
 
4265
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4266
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4267
  const mono_microsecs current_time = 0;
 
 
4270
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4272
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4273
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4275
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4276
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
4277
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
4280
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4281
  ievent->mask = IN_MOVED_TO;
 
 
4282
  ievent->len = sizeof(dummy_file_name);
 
 
4283
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4284
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4285
                              + sizeof(dummy_file_name));
 
 
4286
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
4288
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4290
  bool quit_now = false;
 
 
4291
  buffer password = {};
 
 
4292
  bool mandos_client_exited = false;
 
 
4293
  bool password_is_read = false;
 
 
4294
  __attribute__((cleanup(cleanup_queue)))
 
 
4295
    task_queue *queue = create_queue();
 
 
4296
  g_assert_nonnull(queue);
 
 
4298
  task_context task = {
 
 
4299
    .func=read_inotify_event,
 
 
4302
    .quit_now=&quit_now,
 
 
4303
    .password=&password,
 
 
4304
    .filename=strdup("/nonexistent"),
 
 
4305
    .cancelled_filenames = &(string_set){},
 
 
4307
    .current_time=¤t_time,
 
 
4308
    .mandos_client_exited=&mandos_client_exited,
 
 
4309
    .password_is_read=&password_is_read,
 
 
4311
  task.func(task, queue);
 
 
4312
  g_assert_false(quit_now);
 
 
4313
  g_assert_true(queue->next_run == 0);
 
 
4314
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4316
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4317
        .func=read_inotify_event,
 
 
4320
        .quit_now=&quit_now,
 
 
4321
        .password=&password,
 
 
4322
        .filename=task.filename,
 
 
4323
        .cancelled_filenames=task.cancelled_filenames,
 
 
4324
        .current_time=¤t_time,
 
 
4325
        .mandos_client_exited=&mandos_client_exited,
 
 
4326
        .password_is_read=&password_is_read,
 
 
4329
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4330
                                   EPOLLIN | EPOLLRDHUP));
 
 
4334
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
 
4335
                                               test_fixture *fixture,
 
 
4336
                                               __attribute__((unused))
 
 
4339
  __attribute__((cleanup(cleanup_close)))
 
 
4340
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4341
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4342
  __attribute__((cleanup(string_set_clear)))
 
 
4343
    string_set cancelled_filenames = {};
 
 
4344
  const mono_microsecs current_time = 0;
 
 
4347
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4349
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4350
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4352
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4353
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
 
4354
  struct inotify_event *ievent = ((struct inotify_event *)
 
 
4357
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4358
  ievent->mask = IN_DELETE;
 
 
4359
  ievent->len = sizeof(dummy_file_name);
 
 
4360
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4361
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4362
                              + sizeof(dummy_file_name));
 
 
4363
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
 
4365
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4367
  bool quit_now = false;
 
 
4368
  buffer password = {};
 
 
4369
  bool mandos_client_exited = false;
 
 
4370
  bool password_is_read = false;
 
 
4371
  __attribute__((cleanup(cleanup_queue)))
 
 
4372
    task_queue *queue = create_queue();
 
 
4373
  g_assert_nonnull(queue);
 
 
4375
  task_context task = {
 
 
4376
    .func=read_inotify_event,
 
 
4379
    .quit_now=&quit_now,
 
 
4380
    .password=&password,
 
 
4381
    .filename=strdup("/nonexistent"),
 
 
4382
    .cancelled_filenames=&cancelled_filenames,
 
 
4383
    .current_time=¤t_time,
 
 
4384
    .mandos_client_exited=&mandos_client_exited,
 
 
4385
    .password_is_read=&password_is_read,
 
 
4387
  task.func(task, queue);
 
 
4388
  g_assert_false(quit_now);
 
 
4389
  g_assert_true(queue->next_run == 0);
 
 
4390
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4392
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4393
        .func=read_inotify_event,
 
 
4396
        .quit_now=&quit_now,
 
 
4397
        .password=&password,
 
 
4398
        .filename=task.filename,
 
 
4399
        .cancelled_filenames=&cancelled_filenames,
 
 
4400
        .current_time=¤t_time,
 
 
4401
        .mandos_client_exited=&mandos_client_exited,
 
 
4402
        .password_is_read=&password_is_read,
 
 
4405
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4406
                                   EPOLLIN | EPOLLRDHUP));
 
 
4408
  __attribute__((cleanup(cleanup_string)))
 
 
4409
    char *filename = NULL;
 
 
4410
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4411
                           dummy_file_name), >, 0);
 
 
4412
  g_assert_nonnull(filename);
 
 
4413
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4417
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
 
4418
                                         test_fixture *fixture,
 
 
4419
                                         __attribute__((unused))
 
 
4420
                                         gconstpointer user_data){
 
 
4421
  __attribute__((cleanup(cleanup_close)))
 
 
4422
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4423
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4424
  __attribute__((cleanup(string_set_clear)))
 
 
4425
    string_set cancelled_filenames = {};
 
 
4426
  bool mandos_client_exited = false;
 
 
4427
  bool password_is_read = false;
 
 
4428
  __attribute__((cleanup(cleanup_queue)))
 
 
4429
    task_queue *queue = create_queue();
 
 
4430
  g_assert_nonnull(queue);
 
 
4432
  char *const filename = strdup("/nonexistent");
 
 
4433
  g_assert_nonnull(filename);
 
 
4434
  task_context task = {
 
 
4435
    .func=open_and_parse_question,
 
 
4436
    .question_filename=filename,
 
 
4438
    .password=(buffer[]){{}},
 
 
4440
    .cancelled_filenames=&cancelled_filenames,
 
 
4441
    .current_time=(mono_microsecs[]){0},
 
 
4442
    .mandos_client_exited=&mandos_client_exited,
 
 
4443
    .password_is_read=&password_is_read,
 
 
4445
  task.func(task, queue);
 
 
4446
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4449
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
 
4450
                                             test_fixture *fixture,
 
 
4451
                                             __attribute__((unused))
 
 
4452
                                             gconstpointer user_data){
 
 
4453
  __attribute__((cleanup(cleanup_close)))
 
 
4454
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4455
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4456
  __attribute__((cleanup(string_set_clear)))
 
 
4457
    string_set cancelled_filenames = {};
 
 
4458
  buffer password = {};
 
 
4459
  bool mandos_client_exited = false;
 
 
4460
  bool password_is_read = false;
 
 
4461
  __attribute__((cleanup(cleanup_queue)))
 
 
4462
    task_queue *queue = create_queue();
 
 
4463
  g_assert_nonnull(queue);
 
 
4464
  const mono_microsecs current_time = 0;
 
 
4466
  char *filename = strdup("/proc/self/mem");
 
 
4467
  task_context task = {
 
 
4468
    .func=open_and_parse_question,
 
 
4469
    .question_filename=filename,
 
 
4471
    .password=&password,
 
 
4473
    .cancelled_filenames=&cancelled_filenames,
 
 
4474
    .current_time=¤t_time,
 
 
4475
    .mandos_client_exited=&mandos_client_exited,
 
 
4476
    .password_is_read=&password_is_read,
 
 
4478
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4479
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4483
test_open_and_parse_question_parse_error(__attribute__((unused))
 
 
4484
                                         test_fixture *fixture,
 
 
4485
                                         __attribute__((unused))
 
 
4486
                                         gconstpointer user_data){
 
 
4487
  __attribute__((cleanup(cleanup_close)))
 
 
4488
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4489
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4490
  __attribute__((cleanup(string_set_clear)))
 
 
4491
    string_set cancelled_filenames = {};
 
 
4492
  __attribute__((cleanup(cleanup_queue)))
 
 
4493
    task_queue *queue = create_queue();
 
 
4494
  g_assert_nonnull(queue);
 
 
4496
  __attribute__((cleanup(cleanup_string)))
 
 
4497
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4498
  g_assert_nonnull(tempfilename);
 
 
4499
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4500
  g_assert_cmpint(tempfile, >, 0);
 
 
4501
  const char bad_data[] = "this is bad syntax\n";
 
 
4502
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
 
4503
                  ==, sizeof(bad_data));
 
 
4504
  g_assert_cmpint(close(tempfile), ==, 0);
 
 
4506
  char *const filename = strdup(tempfilename);
 
 
4507
  g_assert_nonnull(filename);
 
 
4508
  task_context task = {
 
 
4509
    .func=open_and_parse_question,
 
 
4510
    .question_filename=filename,
 
 
4512
    .password=(buffer[]){{}},
 
 
4514
    .cancelled_filenames=&cancelled_filenames,
 
 
4515
    .current_time=(mono_microsecs[]){0},
 
 
4516
    .mandos_client_exited=(bool[]){false},
 
 
4517
    .password_is_read=(bool[]){false},
 
 
4519
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4521
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4523
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4527
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
 
4528
                                           test_fixture *fixture,
 
 
4529
                                           __attribute__((unused))
 
 
4530
                                           gconstpointer user_data){
 
 
4531
  __attribute__((cleanup(cleanup_close)))
 
 
4532
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4533
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4534
  __attribute__((cleanup(string_set_clear)))
 
 
4535
    string_set cancelled_filenames = {};
 
 
4536
  __attribute__((cleanup(cleanup_queue)))
 
 
4537
    task_queue *queue = create_queue();
 
 
4538
  g_assert_nonnull(queue);
 
 
4540
  __attribute__((cleanup(cleanup_string)))
 
 
4541
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4542
  g_assert_nonnull(tempfilename);
 
 
4543
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4544
  g_assert_cmpint(questionfile, >, 0);
 
 
4545
  FILE *qf = fdopen(questionfile, "w");
 
 
4546
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
 
4547
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4549
  char *const filename = strdup(tempfilename);
 
 
4550
  g_assert_nonnull(filename);
 
 
4551
  task_context task = {
 
 
4552
    .func=open_and_parse_question,
 
 
4553
    .question_filename=filename,
 
 
4555
    .password=(buffer[]){{}},
 
 
4557
    .cancelled_filenames=&cancelled_filenames,
 
 
4558
    .current_time=(mono_microsecs[]){0},
 
 
4559
    .mandos_client_exited=(bool[]){false},
 
 
4560
    .password_is_read=(bool[]){false},
 
 
4562
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4563
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4565
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4569
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
 
4570
                                            test_fixture *fixture,
 
 
4571
                                            __attribute__((unused))
 
 
4572
                                            gconstpointer user_data){
 
 
4573
  __attribute__((cleanup(cleanup_close)))
 
 
4574
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4575
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4576
  __attribute__((cleanup(string_set_clear)))
 
 
4577
    string_set cancelled_filenames = {};
 
 
4578
  __attribute__((cleanup(cleanup_queue)))
 
 
4579
    task_queue *queue = create_queue();
 
 
4580
  g_assert_nonnull(queue);
 
 
4582
  __attribute__((cleanup(cleanup_string)))
 
 
4583
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4584
  g_assert_nonnull(tempfilename);
 
 
4585
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4586
  g_assert_cmpint(questionfile, >, 0);
 
 
4587
  FILE *qf = fdopen(questionfile, "w");
 
 
4588
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
 
4589
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4591
  char *const filename = strdup(tempfilename);
 
 
4592
  g_assert_nonnull(filename);
 
 
4593
  task_context task = {
 
 
4594
    .func=open_and_parse_question,
 
 
4595
    .question_filename=filename,
 
 
4597
    .password=(buffer[]){{}},
 
 
4599
    .cancelled_filenames=&cancelled_filenames,
 
 
4600
    .current_time=(mono_microsecs[]){0},
 
 
4601
    .mandos_client_exited=(bool[]){false},
 
 
4602
    .password_is_read=(bool[]){false},
 
 
4604
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4605
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4607
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4611
void test_open_and_parse_question_nopid(__attribute__((unused))
 
 
4612
                                        test_fixture *fixture,
 
 
4613
                                        __attribute__((unused))
 
 
4614
                                        gconstpointer user_data){
 
 
4615
  __attribute__((cleanup(cleanup_close)))
 
 
4616
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4617
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4618
  __attribute__((cleanup(string_set_clear)))
 
 
4619
    string_set cancelled_filenames = {};
 
 
4620
  __attribute__((cleanup(cleanup_queue)))
 
 
4621
    task_queue *queue = create_queue();
 
 
4622
  g_assert_nonnull(queue);
 
 
4624
  __attribute__((cleanup(cleanup_string)))
 
 
4625
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4626
  g_assert_nonnull(tempfilename);
 
 
4627
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4628
  g_assert_cmpint(questionfile, >, 0);
 
 
4629
  FILE *qf = fdopen(questionfile, "w");
 
 
4630
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
 
4631
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4633
  char *const filename = strdup(tempfilename);
 
 
4634
  g_assert_nonnull(filename);
 
 
4635
  task_context task = {
 
 
4636
    .func=open_and_parse_question,
 
 
4637
    .question_filename=filename,
 
 
4639
    .password=(buffer[]){{}},
 
 
4641
    .cancelled_filenames=&cancelled_filenames,
 
 
4642
    .current_time=(mono_microsecs[]){0},
 
 
4643
    .mandos_client_exited=(bool[]){false},
 
 
4644
    .password_is_read=(bool[]){false},
 
 
4646
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4647
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4649
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4653
void test_open_and_parse_question_badpid(__attribute__((unused))
 
 
4654
                                         test_fixture *fixture,
 
 
4655
                                         __attribute__((unused))
 
 
4656
                                         gconstpointer user_data){
 
 
4657
  __attribute__((cleanup(cleanup_close)))
 
 
4658
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4659
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4660
  __attribute__((cleanup(string_set_clear)))
 
 
4661
    string_set cancelled_filenames = {};
 
 
4662
  __attribute__((cleanup(cleanup_queue)))
 
 
4663
    task_queue *queue = create_queue();
 
 
4664
  g_assert_nonnull(queue);
 
 
4666
  __attribute__((cleanup(cleanup_string)))
 
 
4667
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4668
  g_assert_nonnull(tempfilename);
 
 
4669
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4670
  g_assert_cmpint(questionfile, >, 0);
 
 
4671
  FILE *qf = fdopen(questionfile, "w");
 
 
4672
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
 
4674
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4676
  char *const filename = strdup(tempfilename);
 
 
4677
  g_assert_nonnull(filename);
 
 
4678
  task_context task = {
 
 
4679
    .func=open_and_parse_question,
 
 
4680
    .question_filename=filename,
 
 
4682
    .password=(buffer[]){{}},
 
 
4684
    .cancelled_filenames=&cancelled_filenames,
 
 
4685
    .current_time=(mono_microsecs[]){0},
 
 
4686
    .mandos_client_exited=(bool[]){false},
 
 
4687
    .password_is_read=(bool[]){false},
 
 
4689
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4690
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4692
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4696
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
 
4697
                                         test_fixture *fixture,
 
 
4698
                                         __attribute__((unused))
 
 
4699
                                         gconstpointer user_data){
 
 
4700
  __attribute__((cleanup(cleanup_close)))
 
 
4701
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4702
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4703
  __attribute__((cleanup(string_set_clear)))
 
 
4704
    string_set cancelled_filenames = {};
 
 
4705
  buffer password = {};
 
 
4706
  bool mandos_client_exited = false;
 
 
4707
  bool password_is_read = false;
 
 
4708
  __attribute__((cleanup(cleanup_queue)))
 
 
4709
    task_queue *queue = create_queue();
 
 
4710
  g_assert_nonnull(queue);
 
 
4711
  const mono_microsecs current_time = 0;
 
 
4713
  /* Find value of sysctl kernel.pid_max */
 
 
4714
  uintmax_t pid_max = 0;
 
 
4715
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
 
4716
  g_assert_nonnull(sysctl_pid_max);
 
 
4717
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
 
4719
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
 
4721
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
 
4722
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
 
4724
  __attribute__((cleanup(cleanup_string)))
 
 
4725
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4726
  g_assert_nonnull(tempfilename);
 
 
4727
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4728
  g_assert_cmpint(questionfile, >, 0);
 
 
4729
  FILE *qf = fdopen(questionfile, "w");
 
 
4730
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
4731
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
 
4733
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4735
  char *const question_filename = strdup(tempfilename);
 
 
4736
  g_assert_nonnull(question_filename);
 
 
4737
  task_context task = {
 
 
4738
    .func=open_and_parse_question,
 
 
4739
    .question_filename=question_filename,
 
 
4741
    .password=&password,
 
 
4742
    .filename=question_filename,
 
 
4743
    .cancelled_filenames=&cancelled_filenames,
 
 
4744
    .current_time=¤t_time,
 
 
4745
    .mandos_client_exited=&mandos_client_exited,
 
 
4746
    .password_is_read=&password_is_read,
 
 
4748
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4749
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4751
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4755
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
 
4756
                                         test_fixture *fixture,
 
 
4757
                                         __attribute__((unused))
 
 
4758
                                         gconstpointer user_data){
 
 
4759
  __attribute__((cleanup(cleanup_close)))
 
 
4760
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4761
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4762
  __attribute__((cleanup(string_set_clear)))
 
 
4763
    string_set cancelled_filenames = {};
 
 
4764
  buffer password = {};
 
 
4765
  bool mandos_client_exited = false;
 
 
4766
  bool password_is_read = false;
 
 
4767
  __attribute__((cleanup(cleanup_queue)))
 
 
4768
    task_queue *queue = create_queue();
 
 
4769
  g_assert_nonnull(queue);
 
 
4770
  const mono_microsecs current_time = 0;
 
 
4772
  __attribute__((cleanup(cleanup_string)))
 
 
4773
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4774
  g_assert_nonnull(tempfilename);
 
 
4775
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4776
  g_assert_cmpint(questionfile, >, 0);
 
 
4777
  FILE *qf = fdopen(questionfile, "w");
 
 
4778
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
4779
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
 
4780
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4782
  char *const filename = strdup(tempfilename);
 
 
4783
  g_assert_nonnull(filename);
 
 
4784
  task_context task = {
 
 
4785
    .func=open_and_parse_question,
 
 
4786
    .question_filename=filename,
 
 
4788
    .password=&password,
 
 
4790
    .cancelled_filenames=&cancelled_filenames,
 
 
4791
    .current_time=¤t_time,
 
 
4792
    .mandos_client_exited=&mandos_client_exited,
 
 
4793
    .password_is_read=&password_is_read,
 
 
4795
  task.func(task, queue);
 
 
4796
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4798
  __attribute__((cleanup(cleanup_string)))
 
 
4799
    char *socket_filename = strdup("/nonexistent");
 
 
4800
  g_assert_nonnull(socket_filename);
 
 
4801
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4802
        .func=connect_question_socket,
 
 
4803
        .question_filename=tempfilename,
 
 
4804
        .filename=socket_filename,
 
 
4806
        .password=&password,
 
 
4807
        .current_time=¤t_time,
 
 
4808
        .mandos_client_exited=&mandos_client_exited,
 
 
4809
        .password_is_read=&password_is_read,
 
 
4812
  g_assert_true(queue->next_run != 0);
 
 
4814
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4818
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
 
4819
                                          test_fixture *fixture,
 
 
4820
                                          __attribute__((unused))
 
 
4821
                                          gconstpointer user_data){
 
 
4822
  __attribute__((cleanup(cleanup_close)))
 
 
4823
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4824
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4825
  __attribute__((cleanup(string_set_clear)))
 
 
4826
    string_set cancelled_filenames = {};
 
 
4827
  buffer password = {};
 
 
4828
  bool mandos_client_exited = false;
 
 
4829
  bool password_is_read = false;
 
 
4830
  __attribute__((cleanup(cleanup_queue)))
 
 
4831
    task_queue *queue = create_queue();
 
 
4832
  g_assert_nonnull(queue);
 
 
4833
  const mono_microsecs current_time = 0;
 
 
4835
  __attribute__((cleanup(cleanup_string)))
 
 
4836
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4837
  g_assert_nonnull(tempfilename);
 
 
4838
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4839
  g_assert_cmpint(questionfile, >, 0);
 
 
4840
  FILE *qf = fdopen(questionfile, "w");
 
 
4841
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
4842
                          PRIuMAX "\nNotAfter=\n",
 
 
4843
                          (uintmax_t)getpid()), >, 0);
 
 
4844
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4846
  char *const filename = strdup(tempfilename);
 
 
4847
  g_assert_nonnull(filename);
 
 
4848
  task_context task = {
 
 
4849
    .func=open_and_parse_question,
 
 
4850
    .question_filename=filename,
 
 
4852
    .password=&password,
 
 
4854
    .cancelled_filenames=&cancelled_filenames,
 
 
4855
    .current_time=¤t_time,
 
 
4856
    .mandos_client_exited=&mandos_client_exited,
 
 
4857
    .password_is_read=&password_is_read,
 
 
4859
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4860
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4862
  __attribute__((cleanup(cleanup_string)))
 
 
4863
    char *socket_filename = strdup("/nonexistent");
 
 
4864
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4865
        .func=connect_question_socket,
 
 
4866
        .question_filename=tempfilename,
 
 
4867
        .filename=socket_filename,
 
 
4869
        .password=&password,
 
 
4870
        .current_time=¤t_time,
 
 
4871
        .mandos_client_exited=&mandos_client_exited,
 
 
4872
        .password_is_read=&password_is_read,
 
 
4874
  g_assert_true(queue->next_run != 0);
 
 
4876
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4880
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
 
4882
                                                  const mono_microsecs
 
 
4884
                                                  const mono_microsecs
 
 
4886
  __attribute__((cleanup(cleanup_close)))
 
 
4887
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4888
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4889
  __attribute__((cleanup(string_set_clear)))
 
 
4890
    string_set cancelled_filenames = {};
 
 
4891
  buffer password = {};
 
 
4892
  bool mandos_client_exited = false;
 
 
4893
  bool password_is_read = false;
 
 
4894
  __attribute__((cleanup(cleanup_queue)))
 
 
4895
    task_queue *queue = create_queue();
 
 
4896
  g_assert_nonnull(queue);
 
 
4897
  queue->next_run = next_queue_run;
 
 
4899
  __attribute__((cleanup(cleanup_string)))
 
 
4900
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4901
  g_assert_nonnull(tempfilename);
 
 
4902
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4903
  g_assert_cmpint(questionfile, >, 0);
 
 
4904
  FILE *qf = fdopen(questionfile, "w");
 
 
4905
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
4906
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
 
4907
                          (uintmax_t)getpid(), notafter), >, 0);
 
 
4908
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4910
  char *const filename = strdup(tempfilename);
 
 
4911
  g_assert_nonnull(filename);
 
 
4912
  task_context task = {
 
 
4913
    .func=open_and_parse_question,
 
 
4914
    .question_filename=filename,
 
 
4916
    .password=&password,
 
 
4918
    .cancelled_filenames=&cancelled_filenames,
 
 
4919
    .current_time=¤t_time,
 
 
4920
    .mandos_client_exited=&mandos_client_exited,
 
 
4921
    .password_is_read=&password_is_read,
 
 
4923
  task.func(task, queue);
 
 
4925
  if(queue->length >= 1){
 
 
4926
    __attribute__((cleanup(cleanup_string)))
 
 
4927
      char *socket_filename = strdup("/nonexistent");
 
 
4928
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4929
          .func=connect_question_socket,
 
 
4930
          .filename=socket_filename,
 
 
4932
          .password=&password,
 
 
4933
          .current_time=¤t_time,
 
 
4934
          .cancelled_filenames=&cancelled_filenames,
 
 
4935
          .mandos_client_exited=&mandos_client_exited,
 
 
4936
          .password_is_read=&password_is_read,
 
 
4938
    g_assert_true(queue->next_run != 0);
 
 
4942
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4943
  } else if(current_time >= notafter) {
 
 
4944
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4946
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4947
          .func=cancel_old_question,
 
 
4948
          .question_filename=tempfilename,
 
 
4949
          .filename=tempfilename,
 
 
4951
          .cancelled_filenames=&cancelled_filenames,
 
 
4952
          .current_time=¤t_time,
 
 
4955
  g_assert_true(queue->next_run == 1);
 
 
4957
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4961
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
 
4962
                                        test_fixture *fixture,
 
 
4963
                                        __attribute__((unused))
 
 
4964
                                        gconstpointer user_data){
 
 
4965
  /* current_time, notafter, next_queue_run */
 
 
4966
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
 
4970
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
 
4971
                                        test_fixture *fixture,
 
 
4972
                                        __attribute__((unused))
 
 
4973
                                        gconstpointer user_data){
 
 
4974
  /* current_time, notafter, next_queue_run */
 
 
4975
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
 
4979
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
 
4980
                                          test_fixture *fixture,
 
 
4981
                                          __attribute__((unused))
 
 
4982
                                          gconstpointer user_data){
 
 
4983
  /* current_time, notafter, next_queue_run */
 
 
4984
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
 
4988
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
 
4989
                                          test_fixture *fixture,
 
 
4990
                                          __attribute__((unused))
 
 
4991
                                          gconstpointer user_data){
 
 
4992
  /* current_time, notafter, next_queue_run */
 
 
4993
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
 
4997
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
 
4998
                                            test_fixture *fixture,
 
 
4999
                                            __attribute__((unused))
 
 
5000
                                            gconstpointer user_data){
 
 
5001
  /* current_time, notafter, next_queue_run */
 
 
5002
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
 
5006
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
 
5007
                                           test_fixture *fixture,
 
 
5008
                                           __attribute__((unused))
 
 
5009
                                           gconstpointer user_data){
 
 
5010
  /* current_time, notafter, next_queue_run */
 
 
5011
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
 
5014
static void assert_cancel_old_question_param(const mono_microsecs
 
 
5016
                                             const mono_microsecs
 
 
5018
                                             const mono_microsecs
 
 
5020
                                             const mono_microsecs
 
 
5022
  __attribute__((cleanup(string_set_clear)))
 
 
5023
    string_set cancelled_filenames = {};
 
 
5024
  __attribute__((cleanup(cleanup_queue)))
 
 
5025
    task_queue *queue = create_queue();
 
 
5026
  g_assert_nonnull(queue);
 
 
5027
  queue->next_run = next_queue_run;
 
 
5029
  char *const question_filename = strdup("/nonexistent");
 
 
5030
  g_assert_nonnull(question_filename);
 
 
5031
  task_context task = {
 
 
5032
    .func=cancel_old_question,
 
 
5033
    .question_filename=question_filename,
 
 
5034
    .filename=question_filename,
 
 
5036
    .cancelled_filenames=&cancelled_filenames,
 
 
5037
    .current_time=¤t_time,
 
 
5039
  task.func(task, queue);
 
 
5041
  if(current_time >= notafter){
 
 
5042
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5043
    g_assert_true(string_set_contains(cancelled_filenames,
 
 
5046
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5047
          .func=cancel_old_question,
 
 
5048
          .question_filename=question_filename,
 
 
5049
          .filename=question_filename,
 
 
5051
          .cancelled_filenames=&cancelled_filenames,
 
 
5052
          .current_time=¤t_time,
 
 
5055
    g_assert_false(string_set_contains(cancelled_filenames,
 
 
5056
                                       question_filename));
 
 
5058
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
 
5059
                   (unsigned int)next_set_to);
 
 
5062
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
 
5063
                                           test_fixture *fixture,
 
 
5064
                                           __attribute__((unused))
 
 
5065
                                           gconstpointer user_data){
 
 
5066
  /* next_queue_run unset,
 
 
5067
     cancellation should happen because time has come,
 
 
5068
     next_queue_run should be unchanged */
 
 
5069
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5070
  assert_cancel_old_question_param(0, 1, 2, 0);
 
 
5073
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
 
5074
                                           test_fixture *fixture,
 
 
5075
                                           __attribute__((unused))
 
 
5076
                                           gconstpointer user_data){
 
 
5077
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
 
5078
     and current_time is not yet notafter or greater,
 
 
5079
     update value of next_queue_run to value of notafter */
 
 
5080
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5081
  assert_cancel_old_question_param(0, 2, 1, 2);
 
 
5084
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
 
5085
                                           test_fixture *fixture,
 
 
5086
                                           __attribute__((unused))
 
 
5087
                                           gconstpointer user_data){
 
 
5088
  /* next_queue_run 1,
 
 
5089
     cancellation should happen because time has come,
 
 
5090
     next_queue_run should be unchanged */
 
 
5091
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5092
  assert_cancel_old_question_param(1, 2, 3, 1);
 
 
5095
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
 
5096
                                           test_fixture *fixture,
 
 
5097
                                           __attribute__((unused))
 
 
5098
                                           gconstpointer user_data){
 
 
5099
  /* If next_queue_run is set,
 
 
5100
     and current_time is not yet notafter or greater,
 
 
5101
     and notafter is larger than next_queue_run
 
 
5102
     next_queue_run should be unchanged */
 
 
5103
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5104
  assert_cancel_old_question_param(1, 3, 2, 1);
 
 
5107
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
 
5108
                                           test_fixture *fixture,
 
 
5109
                                           __attribute__((unused))
 
 
5110
                                           gconstpointer user_data){
 
 
5111
  /* next_queue_run 2,
 
 
5112
     cancellation should happen because time has come,
 
 
5113
     next_queue_run should be unchanged */
 
 
5114
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5115
  assert_cancel_old_question_param(2, 1, 3, 2);
 
 
5118
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
 
5119
                                           test_fixture *fixture,
 
 
5120
                                           __attribute__((unused))
 
 
5121
                                           gconstpointer user_data){
 
 
5122
  /* If next_queue_run is set,
 
 
5123
     and current_time is not yet notafter or greater,
 
 
5124
     and notafter is larger than next_queue_run
 
 
5125
     next_queue_run should be unchanged */
 
 
5126
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5127
  assert_cancel_old_question_param(2, 3, 1, 2);
 
 
5130
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
 
5131
                                           test_fixture *fixture,
 
 
5132
                                           __attribute__((unused))
 
 
5133
                                           gconstpointer user_data){
 
 
5134
  /* next_queue_run 3,
 
 
5135
     cancellation should happen because time has come,
 
 
5136
     next_queue_run should be unchanged */
 
 
5137
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5138
  assert_cancel_old_question_param(3, 1, 2, 3);
 
 
5141
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
 
5142
                                           test_fixture *fixture,
 
 
5143
                                           __attribute__((unused))
 
 
5144
                                           gconstpointer user_data){
 
 
5145
  /* If next_queue_run is set,
 
 
5146
     and current_time is not yet notafter or greater,
 
 
5147
     and notafter is smaller than next_queue_run
 
 
5148
     update value of next_queue_run to value of notafter */
 
 
5149
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5150
  assert_cancel_old_question_param(3, 2, 1, 2);
 
 
5154
test_connect_question_socket_name_too_long(__attribute__((unused))
 
 
5155
                                           test_fixture *fixture,
 
 
5156
                                           __attribute__((unused))
 
 
5157
                                           gconstpointer user_data){
 
 
5158
  __attribute__((cleanup(cleanup_close)))
 
 
5159
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5160
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5161
  const char question_filename[] = "/nonexistent/question";
 
 
5162
  __attribute__((cleanup(string_set_clear)))
 
 
5163
    string_set cancelled_filenames = {};
 
 
5164
  __attribute__((cleanup(cleanup_queue)))
 
 
5165
    task_queue *queue = create_queue();
 
 
5166
  g_assert_nonnull(queue);
 
 
5167
  __attribute__((cleanup(cleanup_string)))
 
 
5168
    char *tempdir = make_temporary_directory();
 
 
5169
  g_assert_nonnull(tempdir);
 
 
5170
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
 
5171
  char socket_name[sizeof(unix_socket.sun_path)];
 
 
5172
  memset(socket_name, 'x', sizeof(socket_name));
 
 
5173
  socket_name[sizeof(socket_name)-1] = '\0';
 
 
5174
  char *filename = NULL;
 
 
5175
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5177
  g_assert_nonnull(filename);
 
 
5179
  task_context task = {
 
 
5180
    .func=connect_question_socket,
 
 
5181
    .question_filename=strdup(question_filename),
 
 
5183
    .password=(buffer[]){{}},
 
 
5185
    .cancelled_filenames=&cancelled_filenames,
 
 
5186
    .mandos_client_exited=(bool[]){false},
 
 
5187
    .password_is_read=(bool[]){false},
 
 
5188
    .current_time=(mono_microsecs[]){0},
 
 
5190
  g_assert_nonnull(task.question_filename);
 
 
5191
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5193
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5194
                                    question_filename));
 
 
5195
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5196
  g_assert_true(queue->next_run == 0);
 
 
5198
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5202
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
 
5203
                                               test_fixture *fixture,
 
 
5204
                                               __attribute__((unused))
 
 
5207
  __attribute__((cleanup(cleanup_close)))
 
 
5208
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5209
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5210
  const char question_filename[] = "/nonexistent/question";
 
 
5211
  __attribute__((cleanup(string_set_clear)))
 
 
5212
    string_set cancelled_filenames = {};
 
 
5213
  const mono_microsecs current_time = 3;
 
 
5214
  __attribute__((cleanup(cleanup_queue)))
 
 
5215
    task_queue *queue = create_queue();
 
 
5216
  g_assert_nonnull(queue);
 
 
5217
  __attribute__((cleanup(cleanup_string)))
 
 
5218
    char *tempdir = make_temporary_directory();
 
 
5219
  g_assert_nonnull(tempdir);
 
 
5220
  char socket_name[] = "nonexistent";
 
 
5221
  char *filename = NULL;
 
 
5222
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5224
  g_assert_nonnull(filename);
 
 
5226
  task_context task = {
 
 
5227
    .func=connect_question_socket,
 
 
5228
    .question_filename=strdup(question_filename),
 
 
5230
    .password=(buffer[]){{}},
 
 
5232
    .cancelled_filenames=&cancelled_filenames,
 
 
5233
    .mandos_client_exited=(bool[]){false},
 
 
5234
    .password_is_read=(bool[]){false},
 
 
5235
    .current_time=¤t_time,
 
 
5237
  g_assert_nonnull(task.question_filename);
 
 
5238
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5240
  g_assert_nonnull(find_matching_task(queue, task));
 
 
5242
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
5243
                                     question_filename));
 
 
5244
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5245
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5247
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5251
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
 
5252
                                            test_fixture *fixture,
 
 
5253
                                            __attribute__((unused))
 
 
5254
                                            gconstpointer user_data){
 
 
5255
  __attribute__((cleanup(cleanup_close)))
 
 
5256
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
 
5257
  __attribute__((cleanup(cleanup_string)))
 
 
5258
    char *const question_filename = strdup("/nonexistent/question");
 
 
5259
  g_assert_nonnull(question_filename);
 
 
5260
  __attribute__((cleanup(string_set_clear)))
 
 
5261
    string_set cancelled_filenames = {};
 
 
5262
  const mono_microsecs current_time = 5;
 
 
5263
  __attribute__((cleanup(cleanup_queue)))
 
 
5264
    task_queue *queue = create_queue();
 
 
5265
  g_assert_nonnull(queue);
 
 
5266
  __attribute__((cleanup(cleanup_string)))
 
 
5267
    char *tempdir = make_temporary_directory();
 
 
5268
  g_assert_nonnull(tempdir);
 
 
5269
  __attribute__((cleanup(cleanup_close)))
 
 
5270
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5271
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5272
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5273
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5274
  const char socket_name[] = "socket_name";
 
 
5275
  __attribute__((cleanup(cleanup_string)))
 
 
5276
    char *filename = NULL;
 
 
5277
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5279
  g_assert_nonnull(filename);
 
 
5280
  g_assert_cmpint((int)strlen(filename), <,
 
 
5281
                  (int)sizeof(sock_name.sun_path));
 
 
5282
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5283
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5284
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5285
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5286
  task_context task = {
 
 
5287
    .func=connect_question_socket,
 
 
5288
    .question_filename=strdup(question_filename),
 
 
5290
    .password=(buffer[]){{}},
 
 
5291
    .filename=strdup(filename),
 
 
5292
    .cancelled_filenames=&cancelled_filenames,
 
 
5293
    .mandos_client_exited=(bool[]){false},
 
 
5294
    .password_is_read=(bool[]){false},
 
 
5295
    .current_time=¤t_time,
 
 
5297
  g_assert_nonnull(task.question_filename);
 
 
5298
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5300
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5301
  const task_context *const added_task
 
 
5302
    = find_matching_task(queue, task);
 
 
5303
  g_assert_nonnull(added_task);
 
 
5304
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5306
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5307
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5311
void test_connect_question_socket_usable(__attribute__((unused))
 
 
5312
                                         test_fixture *fixture,
 
 
5313
                                         __attribute__((unused))
 
 
5314
                                         gconstpointer user_data){
 
 
5315
  __attribute__((cleanup(cleanup_close)))
 
 
5316
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5317
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5318
  __attribute__((cleanup(cleanup_string)))
 
 
5319
    char *const question_filename = strdup("/nonexistent/question");
 
 
5320
  g_assert_nonnull(question_filename);
 
 
5321
  __attribute__((cleanup(string_set_clear)))
 
 
5322
    string_set cancelled_filenames = {};
 
 
5323
  buffer password = {};
 
 
5324
  bool mandos_client_exited = false;
 
 
5325
  bool password_is_read = false;
 
 
5326
  const mono_microsecs current_time = 0;
 
 
5327
  __attribute__((cleanup(cleanup_queue)))
 
 
5328
    task_queue *queue = create_queue();
 
 
5329
  g_assert_nonnull(queue);
 
 
5330
  __attribute__((cleanup(cleanup_string)))
 
 
5331
    char *tempdir = make_temporary_directory();
 
 
5332
  g_assert_nonnull(tempdir);
 
 
5333
  __attribute__((cleanup(cleanup_close)))
 
 
5334
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5335
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5336
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5337
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5338
  const char socket_name[] = "socket_name";
 
 
5339
  __attribute__((cleanup(cleanup_string)))
 
 
5340
    char *filename = NULL;
 
 
5341
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5343
  g_assert_nonnull(filename);
 
 
5344
  g_assert_cmpint((int)strlen(filename), <,
 
 
5345
                  (int)sizeof(sock_name.sun_path));
 
 
5346
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5347
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5348
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5349
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5350
  task_context task = {
 
 
5351
    .func=connect_question_socket,
 
 
5352
    .question_filename=strdup(question_filename),
 
 
5354
    .password=&password,
 
 
5355
    .filename=strdup(filename),
 
 
5356
    .cancelled_filenames=&cancelled_filenames,
 
 
5357
    .mandos_client_exited=&mandos_client_exited,
 
 
5358
    .password_is_read=&password_is_read,
 
 
5359
    .current_time=¤t_time,
 
 
5361
  g_assert_nonnull(task.question_filename);
 
 
5362
  task.func(task, queue);
 
 
5364
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5365
  const task_context *const added_task
 
 
5366
    = find_matching_task(queue, (task_context){
 
 
5367
        .func=send_password_to_socket,
 
 
5368
        .question_filename=question_filename,
 
 
5371
        .password=&password,
 
 
5372
        .cancelled_filenames=&cancelled_filenames,
 
 
5373
        .mandos_client_exited=&mandos_client_exited,
 
 
5374
        .password_is_read=&password_is_read,
 
 
5375
        .current_time=¤t_time,
 
 
5377
  g_assert_nonnull(added_task);
 
 
5378
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5380
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5383
  const int fd = added_task->fd;
 
 
5384
  g_assert_cmpint(fd, >, 0);
 
 
5385
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
 
5388
  char write_data[PIPE_BUF];
 
 
5390
    /* Construct test password buffer */
 
 
5391
    /* Start with + since that is what the real procotol uses */
 
 
5392
    write_data[0] = '+';
 
 
5393
    /* Set a special character at string end just to mark the end */
 
 
5394
    write_data[sizeof(write_data)-2] = 'y';
 
 
5395
    /* Set NUL at buffer end, as suggested by the protocol */
 
 
5396
    write_data[sizeof(write_data)-1] = '\0';
 
 
5397
    /* Fill rest of password with 'x' */
 
 
5398
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
 
5399
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
 
5400
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
 
5403
  /* read from sock_fd */
 
 
5404
  char read_data[sizeof(write_data)];
 
 
5405
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
 
5406
                  ==, sizeof(read_data));
 
 
5408
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
 
5411
  /* writing to sock_fd should fail */
 
 
5412
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
 
5413
                       MSG_NOSIGNAL), <, 0);
 
 
5415
  /* reading from fd should fail */
 
 
5416
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
 
5417
                            MSG_NOSIGNAL), <, 0);
 
 
5419
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5420
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5424
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
 
5425
                                               test_fixture *fixture,
 
 
5426
                                               __attribute__((unused))
 
 
5429
  __attribute__((cleanup(cleanup_close)))
 
 
5430
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5431
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5432
  __attribute__((cleanup(cleanup_string)))
 
 
5433
    char *const question_filename = strdup("/nonexistent/question");
 
 
5434
  g_assert_nonnull(question_filename);
 
 
5435
  __attribute__((cleanup(cleanup_string)))
 
 
5436
    char *const filename = strdup("/nonexistent/socket");
 
 
5437
  g_assert_nonnull(filename);
 
 
5438
  __attribute__((cleanup(string_set_clear)))
 
 
5439
    string_set cancelled_filenames = {};
 
 
5440
  buffer password = {};
 
 
5441
  bool password_is_read = true;
 
 
5442
  __attribute__((cleanup(cleanup_queue)))
 
 
5443
    task_queue *queue = create_queue();
 
 
5444
  g_assert_nonnull(queue);
 
 
5446
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5447
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5449
  __attribute__((cleanup(cleanup_close)))
 
 
5450
    const int read_socket = socketfds[0];
 
 
5451
  const int write_socket = socketfds[1];
 
 
5452
  task_context task = {
 
 
5453
    .func=send_password_to_socket,
 
 
5454
    .question_filename=strdup(question_filename),
 
 
5455
    .filename=strdup(filename),
 
 
5458
    .password=&password,
 
 
5459
    .cancelled_filenames=&cancelled_filenames,
 
 
5460
    .mandos_client_exited=(bool[]){false},
 
 
5461
    .password_is_read=&password_is_read,
 
 
5462
    .current_time=(mono_microsecs[]){0},
 
 
5464
  g_assert_nonnull(task.question_filename);
 
 
5466
  task.func(task, queue);
 
 
5468
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5470
  const task_context *const added_task
 
 
5471
    = find_matching_task(queue, task);
 
 
5472
  g_assert_nonnull(added_task);
 
 
5473
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5474
  g_assert_true(password_is_read);
 
 
5476
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5477
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5482
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
 
5483
                                               test_fixture *fixture,
 
 
5484
                                               __attribute__((unused))
 
 
5487
  __attribute__((cleanup(cleanup_close)))
 
 
5488
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5489
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5490
  __attribute__((cleanup(cleanup_string)))
 
 
5491
    char *const question_filename = strdup("/nonexistent/question");
 
 
5492
  g_assert_nonnull(question_filename);
 
 
5493
  __attribute__((cleanup(cleanup_string)))
 
 
5494
    char *const filename = strdup("/nonexistent/socket");
 
 
5495
  __attribute__((cleanup(string_set_clear)))
 
 
5496
    string_set cancelled_filenames = {};
 
 
5497
  buffer password = {};
 
 
5498
  __attribute__((cleanup(cleanup_queue)))
 
 
5499
    task_queue *queue = create_queue();
 
 
5500
  g_assert_nonnull(queue);
 
 
5502
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5503
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5505
  __attribute__((cleanup(cleanup_close)))
 
 
5506
    const int read_socket = socketfds[0];
 
 
5507
  const int write_socket = socketfds[1];
 
 
5508
  task_context task = {
 
 
5509
    .func=send_password_to_socket,
 
 
5510
    .question_filename=strdup(question_filename),
 
 
5511
    .filename=strdup(filename),
 
 
5514
    .password=&password,
 
 
5515
    .cancelled_filenames=&cancelled_filenames,
 
 
5516
    .mandos_client_exited=(bool[]){false},
 
 
5517
    .password_is_read=(bool[]){false},
 
 
5518
    .current_time=(mono_microsecs[]){0},
 
 
5520
  g_assert_nonnull(task.question_filename);
 
 
5522
  task.func(task, queue);
 
 
5524
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5526
  const task_context *const added_task = find_matching_task(queue,
 
 
5528
  g_assert_nonnull(added_task);
 
 
5529
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5530
  g_assert_true(queue->next_run == 0);
 
 
5532
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5533
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5538
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
 
5539
                                           test_fixture *fixture,
 
 
5540
                                           __attribute__((unused))
 
 
5541
                                           gconstpointer user_data){
 
 
5542
  __attribute__((cleanup(cleanup_close)))
 
 
5543
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5544
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5545
  const char question_filename[] = "/nonexistent/question";
 
 
5546
  char *const filename = strdup("/nonexistent/socket");
 
 
5547
  __attribute__((cleanup(string_set_clear)))
 
 
5548
    string_set cancelled_filenames = {};
 
 
5549
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
 
5550
  __attribute__((cleanup(cleanup_buffer)))
 
 
5552
    .data=malloc(oversized),
 
 
5554
    .allocated=oversized,
 
 
5556
  g_assert_nonnull(password.data);
 
 
5557
  if(mlock(password.data, password.allocated) != 0){
 
 
5558
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5560
  /* Construct test password buffer */
 
 
5561
  /* Start with + since that is what the real procotol uses */
 
 
5562
  password.data[0] = '+';
 
 
5563
  /* Set a special character at string end just to mark the end */
 
 
5564
  password.data[oversized-3] = 'y';
 
 
5565
  /* Set NUL at buffer end, as suggested by the protocol */
 
 
5566
  password.data[oversized-2] = '\0';
 
 
5567
  /* Fill rest of password with 'x' */
 
 
5568
  memset(password.data+1, 'x', oversized-3);
 
 
5570
  __attribute__((cleanup(cleanup_queue)))
 
 
5571
    task_queue *queue = create_queue();
 
 
5572
  g_assert_nonnull(queue);
 
 
5574
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5575
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5577
  __attribute__((cleanup(cleanup_close)))
 
 
5578
    const int read_socket = socketfds[0];
 
 
5579
  __attribute__((cleanup(cleanup_close)))
 
 
5580
    const int write_socket = socketfds[1];
 
 
5581
  task_context task = {
 
 
5582
    .func=send_password_to_socket,
 
 
5583
    .question_filename=strdup(question_filename),
 
 
5587
    .password=&password,
 
 
5588
    .cancelled_filenames=&cancelled_filenames,
 
 
5589
    .mandos_client_exited=(bool[]){true},
 
 
5590
    .password_is_read=(bool[]){true},
 
 
5591
    .current_time=(mono_microsecs[]){0},
 
 
5593
  g_assert_nonnull(task.question_filename);
 
 
5595
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5597
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5598
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5599
                                    question_filename));
 
 
5602
static void test_send_password_to_socket_retry(__attribute__((unused))
 
 
5603
                                               test_fixture *fixture,
 
 
5604
                                               __attribute__((unused))
 
 
5607
  __attribute__((cleanup(cleanup_close)))
 
 
5608
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5609
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5610
  __attribute__((cleanup(cleanup_string)))
 
 
5611
    char *const question_filename = strdup("/nonexistent/question");
 
 
5612
  g_assert_nonnull(question_filename);
 
 
5613
  __attribute__((cleanup(cleanup_string)))
 
 
5614
    char *const filename = strdup("/nonexistent/socket");
 
 
5615
  g_assert_nonnull(filename);
 
 
5616
  __attribute__((cleanup(string_set_clear)))
 
 
5617
    string_set cancelled_filenames = {};
 
 
5618
  __attribute__((cleanup(cleanup_buffer)))
 
 
5619
    buffer password = {};
 
 
5621
  __attribute__((cleanup(cleanup_queue)))
 
 
5622
    task_queue *queue = create_queue();
 
 
5623
  g_assert_nonnull(queue);
 
 
5625
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5626
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5628
  __attribute__((cleanup(cleanup_close)))
 
 
5629
    const int read_socket = socketfds[0];
 
 
5630
  const int write_socket = socketfds[1];
 
 
5631
  /* Close the server side socket to force ECONNRESET on client */
 
 
5632
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
5633
  task_context task = {
 
 
5634
    .func=send_password_to_socket,
 
 
5635
    .question_filename=strdup(question_filename),
 
 
5636
    .filename=strdup(filename),
 
 
5639
    .password=&password,
 
 
5640
    .cancelled_filenames=&cancelled_filenames,
 
 
5641
    .mandos_client_exited=(bool[]){true},
 
 
5642
    .password_is_read=(bool[]){true},
 
 
5643
    .current_time=(mono_microsecs[]){0},
 
 
5645
  g_assert_nonnull(task.question_filename);
 
 
5647
  task.func(task, queue);
 
 
5649
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5651
  const task_context *const added_task = find_matching_task(queue,
 
 
5653
  g_assert_nonnull(added_task);
 
 
5654
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5656
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5661
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
 
5662
                                            test_fixture *fixture,
 
 
5663
                                            __attribute__((unused))
 
 
5664
                                            gconstpointer user_data){
 
 
5665
  __attribute__((cleanup(cleanup_close)))
 
 
5666
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
 
5667
  __attribute__((cleanup(cleanup_string)))
 
 
5668
    char *const question_filename = strdup("/nonexistent/question");
 
 
5669
  g_assert_nonnull(question_filename);
 
 
5670
  __attribute__((cleanup(cleanup_string)))
 
 
5671
    char *const filename = strdup("/nonexistent/socket");
 
 
5672
  g_assert_nonnull(filename);
 
 
5673
  __attribute__((cleanup(string_set_clear)))
 
 
5674
    string_set cancelled_filenames = {};
 
 
5675
  __attribute__((cleanup(cleanup_buffer)))
 
 
5676
    buffer password = {};
 
 
5678
  const mono_microsecs current_time = 11;
 
 
5679
  __attribute__((cleanup(cleanup_queue)))
 
 
5680
    task_queue *queue = create_queue();
 
 
5681
  g_assert_nonnull(queue);
 
 
5683
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5684
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5686
  __attribute__((cleanup(cleanup_close)))
 
 
5687
    const int read_socket = socketfds[0];
 
 
5688
  const int write_socket = socketfds[1];
 
 
5689
  /* Close the server side socket to force ECONNRESET on client */
 
 
5690
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
5691
  task_context task = {
 
 
5692
    .func=send_password_to_socket,
 
 
5693
    .question_filename=strdup(question_filename),
 
 
5694
    .filename=strdup(filename),
 
 
5697
    .password=&password,
 
 
5698
    .cancelled_filenames=&cancelled_filenames,
 
 
5699
    .mandos_client_exited=(bool[]){true},
 
 
5700
    .password_is_read=(bool[]){true},
 
 
5701
    .current_time=¤t_time,
 
 
5703
  g_assert_nonnull(task.question_filename);
 
 
5705
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5707
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5709
  const task_context *const added_task = find_matching_task(queue,
 
 
5711
  g_assert_nonnull(added_task);
 
 
5712
  g_assert_true(queue->next_run == current_time + 1000000);
 
 
5713
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5716
static void assert_send_password_to_socket_password(buffer password){
 
 
5717
  __attribute__((cleanup(cleanup_close)))
 
 
5718
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5719
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5720
  char *const question_filename = strdup("/nonexistent/question");
 
 
5721
  g_assert_nonnull(question_filename);
 
 
5722
  char *const filename = strdup("/nonexistent/socket");
 
 
5723
  g_assert_nonnull(filename);
 
 
5724
  __attribute__((cleanup(string_set_clear)))
 
 
5725
    string_set cancelled_filenames = {};
 
 
5727
  __attribute__((cleanup(cleanup_queue)))
 
 
5728
    task_queue *queue = create_queue();
 
 
5729
  g_assert_nonnull(queue);
 
 
5731
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5732
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5734
  __attribute__((cleanup(cleanup_close)))
 
 
5735
    const int read_socket = socketfds[0];
 
 
5736
  const int write_socket = socketfds[1];
 
 
5737
  task_context task = {
 
 
5738
    .func=send_password_to_socket,
 
 
5739
    .question_filename=question_filename,
 
 
5743
    .password=&password,
 
 
5744
    .cancelled_filenames=&cancelled_filenames,
 
 
5745
    .mandos_client_exited=(bool[]){true},
 
 
5746
    .password_is_read=(bool[]){true},
 
 
5747
    .current_time=(mono_microsecs[]){0},
 
 
5750
  char *expected_written_data = malloc(password.length + 2);
 
 
5751
  g_assert_nonnull(expected_written_data);
 
 
5752
  expected_written_data[0] = '+';
 
 
5753
  expected_written_data[password.length + 1] = '\0';
 
 
5754
  if(password.length > 0){
 
 
5755
    g_assert_nonnull(password.data);
 
 
5756
    memcpy(expected_written_data + 1, password.data, password.length);
 
 
5759
  task.func(task, queue);
 
 
5762
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
 
5763
                  (int)(password.length + 2));
 
 
5764
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5766
  g_assert_true(memcmp(expected_written_data, buf,
 
 
5767
                       password.length + 2) == 0);
 
 
5769
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
 
5771
  free(expected_written_data);
 
 
5775
test_send_password_to_socket_null_password(__attribute__((unused))
 
 
5776
                                           test_fixture *fixture,
 
 
5777
                                           __attribute__((unused))
 
 
5778
                                           gconstpointer user_data){
 
 
5779
  __attribute__((cleanup(cleanup_buffer)))
 
 
5780
    buffer password = {};
 
 
5781
  assert_send_password_to_socket_password(password);
 
 
5785
test_send_password_to_socket_empty_password(__attribute__((unused))
 
 
5786
                                            test_fixture *fixture,
 
 
5787
                                            __attribute__((unused))
 
 
5788
                                            gconstpointer user_data){
 
 
5789
  __attribute__((cleanup(cleanup_buffer)))
 
 
5791
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
 
5793
    .allocated=0,               /* deliberate lie */
 
 
5795
  g_assert_nonnull(password.data);
 
 
5796
  assert_send_password_to_socket_password(password);
 
 
5800
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
 
5801
                                            test_fixture *fixture,
 
 
5802
                                            __attribute__((unused))
 
 
5803
                                            gconstpointer user_data){
 
 
5804
  __attribute__((cleanup(cleanup_buffer)))
 
 
5810
  if(mlock(password.data, password.allocated) != 0){
 
 
5811
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5813
  assert_send_password_to_socket_password(password);
 
 
5817
test_send_password_to_socket_text_password(__attribute__((unused))
 
 
5818
                                           test_fixture *fixture,
 
 
5819
                                           __attribute__((unused))
 
 
5820
                                           gconstpointer user_data){
 
 
5821
  const char dummy_test_password[] = "dummy test password";
 
 
5822
  __attribute__((cleanup(cleanup_buffer)))
 
 
5824
    .data = strdup(dummy_test_password),
 
 
5825
    .length = strlen(dummy_test_password),
 
 
5826
    .allocated = sizeof(dummy_test_password),
 
 
5828
  if(mlock(password.data, password.allocated) != 0){
 
 
5829
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5831
  assert_send_password_to_socket_password(password);
 
 
5835
test_send_password_to_socket_binary_password(__attribute__((unused))
 
 
5836
                                             test_fixture *fixture,
 
 
5837
                                             __attribute__((unused))
 
 
5838
                                             gconstpointer user_data){
 
 
5839
  __attribute__((cleanup(cleanup_buffer)))
 
 
5845
  g_assert_nonnull(password.data);
 
 
5846
  if(mlock(password.data, password.allocated) != 0){
 
 
5847
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5849
  char c = 1;                   /* Start at 1, avoiding NUL */
 
 
5850
  for(int i=0; i < 255; i++){
 
 
5851
    password.data[i] = c++;
 
 
5853
  assert_send_password_to_socket_password(password);
 
 
5857
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
 
5858
                                              test_fixture *fixture,
 
 
5859
                                              __attribute__((unused))
 
 
5862
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
 
5863
  __attribute__((cleanup(cleanup_buffer)))
 
 
5865
    .data=malloc(sizeof(test_password)),
 
 
5866
    .length=sizeof(test_password),
 
 
5867
    .allocated=sizeof(test_password),
 
 
5869
  g_assert_nonnull(password.data);
 
 
5870
  if(mlock(password.data, password.allocated) !=0){
 
 
5871
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
5873
  memcpy(password.data, test_password, password.allocated);
 
 
5874
  assert_send_password_to_socket_password(password);
 
 
5877
static bool assert_add_existing_questions_to_devnull(task_queue
 
 
5890
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
 
5891
                                               test_fixture *fixture,
 
 
5892
                                               __attribute__((unused))
 
 
5895
  __attribute__((cleanup(cleanup_queue)))
 
 
5896
    task_queue *queue = create_queue();
 
 
5897
  g_assert_nonnull(queue);
 
 
5898
  __attribute__((cleanup(cleanup_close)))
 
 
5899
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5900
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5901
  __attribute__((cleanup(string_set_clear)))
 
 
5902
    string_set cancelled_filenames = {};
 
 
5904
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
5907
                  (buffer[]){{}}, /* password */
 
 
5908
                  &cancelled_filenames,
 
 
5909
                  (mono_microsecs[]){0}, /* current_time */
 
 
5910
                  (bool[]){false},       /* mandos_client_exited */
 
 
5911
                  (bool[]){false},       /* password_is_read */
 
 
5912
                  "/nonexistent"));      /* dirname */
 
 
5914
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5918
bool assert_add_existing_questions_to_devnull(task_queue
 
 
5925
                                              *cancelled_filenames,
 
 
5926
                                              const mono_microsecs
 
 
5927
                                              *const current_time,
 
 
5929
                                              mandos_client_exited,
 
 
5934
  __attribute__((cleanup(cleanup_close)))
 
 
5935
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
 
5936
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
5937
  __attribute__((cleanup(cleanup_close)))
 
 
5938
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
5939
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
5940
  dup2(devnull_fd, STDERR_FILENO);
 
 
5941
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
 
5942
                                          cancelled_filenames,
 
 
5944
                                          mandos_client_exited,
 
 
5945
                                          password_is_read, dirname);
 
 
5946
  dup2(real_stderr_fd, STDERR_FILENO);
 
 
5951
void test_add_existing_questions_no_questions(__attribute__((unused))
 
 
5952
                                              test_fixture *fixture,
 
 
5953
                                              __attribute__((unused))
 
 
5956
  __attribute__((cleanup(cleanup_queue)))
 
 
5957
    task_queue *queue = create_queue();
 
 
5958
  g_assert_nonnull(queue);
 
 
5959
  __attribute__((cleanup(cleanup_close)))
 
 
5960
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5961
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5962
  __attribute__((cleanup(string_set_clear)))
 
 
5963
    string_set cancelled_filenames = {};
 
 
5964
  __attribute__((cleanup(cleanup_string)))
 
 
5965
    char *tempdir = make_temporary_directory();
 
 
5966
  g_assert_nonnull(tempdir);
 
 
5968
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
5971
                  (buffer[]){{}}, /* password */
 
 
5972
                  &cancelled_filenames,
 
 
5973
                  (mono_microsecs[]){0}, /* current_time */
 
 
5974
                  (bool[]){false},       /* mandos_client_exited */
 
 
5975
                  (bool[]){false},       /* password_is_read */
 
 
5978
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5980
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5983
static char *make_question_file_in_directory(const char *const);
 
 
5986
void test_add_existing_questions_one_question(__attribute__((unused))
 
 
5987
                                              test_fixture *fixture,
 
 
5988
                                              __attribute__((unused))
 
 
5991
  __attribute__((cleanup(cleanup_queue)))
 
 
5992
    task_queue *queue = create_queue();
 
 
5993
  g_assert_nonnull(queue);
 
 
5994
  __attribute__((cleanup(cleanup_close)))
 
 
5995
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5996
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5997
  __attribute__((cleanup(cleanup_buffer)))
 
 
5998
    buffer password = {};
 
 
5999
  __attribute__((cleanup(string_set_clear)))
 
 
6000
    string_set cancelled_filenames = {};
 
 
6001
  const mono_microsecs current_time = 0;
 
 
6002
  bool mandos_client_exited = false;
 
 
6003
  bool password_is_read = false;
 
 
6004
  __attribute__((cleanup(cleanup_string)))
 
 
6005
    char *tempdir = make_temporary_directory();
 
 
6006
  g_assert_nonnull(tempdir);
 
 
6007
  __attribute__((cleanup(cleanup_string)))
 
 
6008
    char *question_filename
 
 
6009
    = make_question_file_in_directory(tempdir);
 
 
6010
  g_assert_nonnull(question_filename);
 
 
6012
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6016
                 &cancelled_filenames,
 
 
6018
                 &mandos_client_exited,
 
 
6022
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6024
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6025
        .func=open_and_parse_question,
 
 
6027
        .filename=question_filename,
 
 
6028
        .question_filename=question_filename,
 
 
6029
        .password=&password,
 
 
6030
        .cancelled_filenames=&cancelled_filenames,
 
 
6031
        .current_time=¤t_time,
 
 
6032
        .mandos_client_exited=&mandos_client_exited,
 
 
6033
        .password_is_read=&password_is_read,
 
 
6036
  g_assert_true(queue->next_run == 1);
 
 
6038
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6039
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6042
static char *make_question_file_in_directory(const char
 
 
6044
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
 
6048
void test_add_existing_questions_two_questions(__attribute__((unused))
 
 
6049
                                               test_fixture *fixture,
 
 
6050
                                               __attribute__((unused))
 
 
6053
  __attribute__((cleanup(cleanup_queue)))
 
 
6054
    task_queue *queue = create_queue();
 
 
6055
  g_assert_nonnull(queue);
 
 
6056
  __attribute__((cleanup(cleanup_close)))
 
 
6057
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6058
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6059
  __attribute__((cleanup(cleanup_buffer)))
 
 
6060
    buffer password = {};
 
 
6061
  __attribute__((cleanup(string_set_clear)))
 
 
6062
    string_set cancelled_filenames = {};
 
 
6063
  const mono_microsecs current_time = 0;
 
 
6064
  bool mandos_client_exited = false;
 
 
6065
  bool password_is_read = false;
 
 
6066
  __attribute__((cleanup(cleanup_string)))
 
 
6067
    char *tempdir = make_temporary_directory();
 
 
6068
  g_assert_nonnull(tempdir);
 
 
6069
  __attribute__((cleanup(cleanup_string)))
 
 
6070
    char *question_filename1
 
 
6071
    = make_question_file_in_directory(tempdir);
 
 
6072
  g_assert_nonnull(question_filename1);
 
 
6073
  __attribute__((cleanup(cleanup_string)))
 
 
6074
    char *question_filename2
 
 
6075
    = make_question_file_in_directory(tempdir);
 
 
6076
  g_assert_nonnull(question_filename2);
 
 
6078
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6082
                 &cancelled_filenames,
 
 
6084
                 &mandos_client_exited,
 
 
6088
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
6090
  g_assert_true(queue->next_run == 1);
 
 
6092
  __attribute__((cleanup(string_set_clear)))
 
 
6093
    string_set seen_questions = {};
 
 
6095
  bool queue_contains_question_opener(char *const question_filename){
 
 
6096
    return(find_matching_task(queue, (task_context){
 
 
6097
          .func=open_and_parse_question,
 
 
6099
          .question_filename=question_filename,
 
 
6100
          .password=&password,
 
 
6101
          .cancelled_filenames=&cancelled_filenames,
 
 
6102
          .current_time=¤t_time,
 
 
6103
          .mandos_client_exited=&mandos_client_exited,
 
 
6104
          .password_is_read=&password_is_read,
 
 
6108
  g_assert_true(queue_contains_question_opener(question_filename1));
 
 
6109
  g_assert_true(queue_contains_question_opener(question_filename2));
 
 
6111
  g_assert_true(queue->next_run == 1);
 
 
6113
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6114
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6115
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6119
test_add_existing_questions_non_questions(__attribute__((unused))
 
 
6120
                                          test_fixture *fixture,
 
 
6121
                                          __attribute__((unused))
 
 
6122
                                          gconstpointer user_data){
 
 
6123
  __attribute__((cleanup(cleanup_queue)))
 
 
6124
    task_queue *queue = create_queue();
 
 
6125
  g_assert_nonnull(queue);
 
 
6126
  __attribute__((cleanup(cleanup_close)))
 
 
6127
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6128
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6129
  __attribute__((cleanup(string_set_clear)))
 
 
6130
    string_set cancelled_filenames = {};
 
 
6131
  __attribute__((cleanup(cleanup_string)))
 
 
6132
    char *tempdir = make_temporary_directory();
 
 
6133
  g_assert_nonnull(tempdir);
 
 
6134
  __attribute__((cleanup(cleanup_string)))
 
 
6135
    char *question_filename1
 
 
6136
    = make_temporary_file_in_directory(tempdir);
 
 
6137
  g_assert_nonnull(question_filename1);
 
 
6138
  __attribute__((cleanup(cleanup_string)))
 
 
6139
    char *question_filename2
 
 
6140
    = make_temporary_file_in_directory(tempdir);
 
 
6141
  g_assert_nonnull(question_filename2);
 
 
6143
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6146
                  (buffer[]){{}}, /* password */
 
 
6147
                  &cancelled_filenames,
 
 
6148
                  (mono_microsecs[]){0}, /* current_time */
 
 
6149
                  (bool[]){false},       /* mandos_client_exited */
 
 
6150
                  (bool[]){false},       /* password_is_read */
 
 
6153
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6155
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6156
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6157
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6161
test_add_existing_questions_both_types(__attribute__((unused))
 
 
6162
                                       test_fixture *fixture,
 
 
6163
                                       __attribute__((unused))
 
 
6164
                                       gconstpointer user_data){
 
 
6165
  __attribute__((cleanup(cleanup_queue)))
 
 
6166
    task_queue *queue = create_queue();
 
 
6167
  g_assert_nonnull(queue);
 
 
6168
  __attribute__((cleanup(cleanup_close)))
 
 
6169
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6170
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6171
  __attribute__((cleanup(cleanup_buffer)))
 
 
6172
    buffer password = {};
 
 
6173
  __attribute__((cleanup(string_set_clear)))
 
 
6174
    string_set cancelled_filenames = {};
 
 
6175
  const mono_microsecs current_time = 0;
 
 
6176
  bool mandos_client_exited = false;
 
 
6177
  bool password_is_read = false;
 
 
6178
  __attribute__((cleanup(cleanup_string)))
 
 
6179
    char *tempdir = make_temporary_directory();
 
 
6180
  g_assert_nonnull(tempdir);
 
 
6181
  __attribute__((cleanup(cleanup_string)))
 
 
6182
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
 
6183
  g_assert_nonnull(tempfilename1);
 
 
6184
  __attribute__((cleanup(cleanup_string)))
 
 
6185
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
 
6186
  g_assert_nonnull(tempfilename2);
 
 
6187
  __attribute__((cleanup(cleanup_string)))
 
 
6188
    char *question_filename
 
 
6189
    = make_question_file_in_directory(tempdir);
 
 
6190
  g_assert_nonnull(question_filename);
 
 
6192
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6196
                 &cancelled_filenames,
 
 
6198
                 &mandos_client_exited,
 
 
6202
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6204
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6205
        .func=open_and_parse_question,
 
 
6207
        .filename=question_filename,
 
 
6208
        .question_filename=question_filename,
 
 
6209
        .password=&password,
 
 
6210
        .cancelled_filenames=&cancelled_filenames,
 
 
6211
        .current_time=¤t_time,
 
 
6212
        .mandos_client_exited=&mandos_client_exited,
 
 
6213
        .password_is_read=&password_is_read,
 
 
6216
  g_assert_true(queue->next_run == 1);
 
 
6218
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
 
6219
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
 
6220
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6221
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6224
static void test_wait_for_event_timeout(__attribute__((unused))
 
 
6225
                                        test_fixture *fixture,
 
 
6226
                                        __attribute__((unused))
 
 
6227
                                        gconstpointer user_data){
 
 
6228
  __attribute__((cleanup(cleanup_close)))
 
 
6229
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6230
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6232
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
 
6235
static void test_wait_for_event_event(__attribute__((unused))
 
 
6236
                                      test_fixture *fixture,
 
 
6237
                                      __attribute__((unused))
 
 
6238
                                      gconstpointer user_data){
 
 
6239
  __attribute__((cleanup(cleanup_close)))
 
 
6240
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6241
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6243
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6244
  __attribute__((cleanup(cleanup_close)))
 
 
6245
    const int read_pipe = pipefds[0];
 
 
6246
  __attribute__((cleanup(cleanup_close)))
 
 
6247
    const int write_pipe = pipefds[1];
 
 
6248
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
 
6249
                            &(struct epoll_event)
 
 
6250
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
 
6251
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
 
6253
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6256
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
 
6257
                                        __attribute__((unused))
 
 
6258
                                        gconstpointer user_data){
 
 
6259
  const pid_t pid = fork();
 
 
6260
  if(pid == 0){         /* Child */
 
 
6261
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
6262
      _exit(EXIT_FAILURE);
 
 
6264
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
6265
      _exit(EXIT_FAILURE);
 
 
6269
  g_assert_true(pid != -1);
 
 
6270
  __attribute__((cleanup(cleanup_close)))
 
 
6271
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6272
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6274
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6277
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
 
6278
  g_assert_true(WIFEXITED(status));
 
 
6279
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
 
6282
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
 
6283
                                           test_fixture *fixture,
 
 
6284
                                           __attribute__((unused))
 
 
6285
                                           gconstpointer user_data){
 
 
6286
  __attribute__((cleanup(cleanup_queue)))
 
 
6287
    task_queue *queue = create_queue();
 
 
6288
  g_assert_nonnull(queue);
 
 
6289
  queue->next_run = 1;
 
 
6290
  __attribute__((cleanup(cleanup_close)))
 
 
6291
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6292
  __attribute__((cleanup(string_set_clear)))
 
 
6293
    string_set cancelled_filenames = {};
 
 
6294
  bool quit_now = false;
 
 
6296
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6297
  g_assert_false(quit_now);
 
 
6298
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
 
6302
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
 
6303
                                               test_fixture *fixture,
 
 
6304
                                               __attribute__((unused))
 
 
6307
  __attribute__((cleanup(cleanup_queue)))
 
 
6308
    task_queue *queue = create_queue();
 
 
6309
  g_assert_nonnull(queue);
 
 
6310
  __attribute__((cleanup(string_set_clear)))
 
 
6311
    string_set cancelled_filenames = {};
 
 
6312
  bool quit_now = false;
 
 
6313
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6314
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6315
                               question_filename));
 
 
6317
  g_assert_true(add_to_queue(queue,
 
 
6318
                             (task_context){ .func=dummy_func }));
 
 
6320
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6321
  g_assert_false(quit_now);
 
 
6322
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6323
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
6324
                                     question_filename));
 
 
6328
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
 
6329
                                              test_fixture *fixture,
 
 
6330
                                              __attribute__((unused))
 
 
6333
  __attribute__((cleanup(cleanup_queue)))
 
 
6334
    task_queue *queue = create_queue();
 
 
6335
  g_assert_nonnull(queue);
 
 
6336
  __attribute__((cleanup(string_set_clear)))
 
 
6337
    string_set cancelled_filenames = {};
 
 
6338
  bool quit_now = false;
 
 
6340
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6341
  __attribute__((cleanup(cleanup_close)))
 
 
6342
    const int read_pipe = pipefds[0];
 
 
6343
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
6344
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6345
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6346
                               question_filename));
 
 
6347
  __attribute__((nonnull))
 
 
6348
    void quit_func(const task_context task,
 
 
6349
                   __attribute__((unused)) task_queue *const q){
 
 
6350
    g_assert_nonnull(task.quit_now);
 
 
6351
    *task.quit_now = true;
 
 
6353
  task_context task = {
 
 
6355
    .question_filename=strdup(question_filename),
 
 
6356
    .quit_now=&quit_now,
 
 
6359
  g_assert_nonnull(task.question_filename);
 
 
6361
  g_assert_true(add_to_queue(queue, task));
 
 
6363
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6364
  g_assert_false(quit_now);
 
 
6366
  /* read_pipe should be closed already */
 
 
6368
  bool read_pipe_closed = (close(read_pipe) == -1);
 
 
6369
  read_pipe_closed &= (errno == EBADF);
 
 
6370
  g_assert_true(read_pipe_closed);
 
 
6373
static void test_run_queue_one_task(__attribute__((unused))
 
 
6374
                                    test_fixture *fixture,
 
 
6375
                                    __attribute__((unused))
 
 
6376
                                    gconstpointer user_data){
 
 
6377
  __attribute__((cleanup(cleanup_queue)))
 
 
6378
    task_queue *queue = create_queue();
 
 
6379
  g_assert_nonnull(queue);
 
 
6380
  __attribute__((cleanup(string_set_clear)))
 
 
6381
    string_set cancelled_filenames = {};
 
 
6382
  bool quit_now = false;
 
 
6384
  __attribute__((nonnull))
 
 
6385
    void next_run_func(__attribute__((unused))
 
 
6386
                       const task_context task,
 
 
6387
                       task_queue *const q){
 
 
6391
  task_context task = {
 
 
6392
    .func=next_run_func,
 
 
6394
  g_assert_true(add_to_queue(queue, task));
 
 
6396
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6397
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6398
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6401
static void test_run_queue_two_tasks(__attribute__((unused))
 
 
6402
                                     test_fixture *fixture,
 
 
6403
                                     __attribute__((unused))
 
 
6404
                                     gconstpointer user_data){
 
 
6405
  __attribute__((cleanup(cleanup_queue)))
 
 
6406
    task_queue *queue = create_queue();
 
 
6407
  g_assert_nonnull(queue);
 
 
6408
  queue->next_run = 1;
 
 
6409
  __attribute__((cleanup(string_set_clear)))
 
 
6410
    string_set cancelled_filenames = {};
 
 
6411
  bool quit_now = false;
 
 
6412
  bool mandos_client_exited = false;
 
 
6414
  __attribute__((nonnull))
 
 
6415
    void next_run_func(__attribute__((unused))
 
 
6416
                       const task_context task,
 
 
6417
                       task_queue *const q){
 
 
6421
  __attribute__((nonnull))
 
 
6422
    void exited_func(const task_context task,
 
 
6423
                     __attribute__((unused)) task_queue *const q){
 
 
6424
    *task.mandos_client_exited = true;
 
 
6427
  task_context task1 = {
 
 
6428
    .func=next_run_func,
 
 
6430
  g_assert_true(add_to_queue(queue, task1));
 
 
6432
  task_context task2 = {
 
 
6434
    .mandos_client_exited=&mandos_client_exited,
 
 
6436
  g_assert_true(add_to_queue(queue, task2));
 
 
6438
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6439
  g_assert_false(quit_now);
 
 
6440
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6441
  g_assert_true(mandos_client_exited);
 
 
6442
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6445
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
 
6446
                                          test_fixture *fixture,
 
 
6447
                                          __attribute__((unused))
 
 
6448
                                          gconstpointer user_data){
 
 
6449
  __attribute__((cleanup(cleanup_queue)))
 
 
6450
    task_queue *queue = create_queue();
 
 
6451
  g_assert_nonnull(queue);
 
 
6452
  __attribute__((cleanup(string_set_clear)))
 
 
6453
    string_set cancelled_filenames = {};
 
 
6454
  bool quit_now = false;
 
 
6455
  bool mandos_client_exited = false;
 
 
6456
  bool password_is_read = false;
 
 
6458
  __attribute__((nonnull))
 
 
6459
    void set_exited_func(const task_context task,
 
 
6460
                         __attribute__((unused)) task_queue *const q){
 
 
6461
    *task.mandos_client_exited = true;
 
 
6462
    *task.quit_now = true;
 
 
6464
  task_context task1 = {
 
 
6465
    .func=set_exited_func,
 
 
6466
    .quit_now=&quit_now,
 
 
6467
    .mandos_client_exited=&mandos_client_exited,
 
 
6469
  g_assert_true(add_to_queue(queue, task1));
 
 
6471
  __attribute__((nonnull))
 
 
6472
    void set_read_func(const task_context task,
 
 
6473
                       __attribute__((unused)) task_queue *const q){
 
 
6474
    *task.quit_now = true;
 
 
6475
    *task.password_is_read = true;
 
 
6477
  task_context task2 = {
 
 
6478
    .func=set_read_func,
 
 
6479
    .quit_now=&quit_now,
 
 
6480
    .password_is_read=&password_is_read,
 
 
6482
  g_assert_true(add_to_queue(queue, task2));
 
 
6484
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6485
  g_assert_true(quit_now);
 
 
6486
  g_assert_true(mandos_client_exited xor password_is_read);
 
 
6487
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6490
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
 
6491
                                             test_fixture *fixture,
 
 
6492
                                             __attribute__((unused))
 
 
6493
                                             gconstpointer user_data){
 
 
6494
  __attribute__((cleanup(cleanup_queue)))
 
 
6495
    task_queue *queue = create_queue();
 
 
6496
  g_assert_nonnull(queue);
 
 
6497
  __attribute__((cleanup(string_set_clear)))
 
 
6498
    string_set cancelled_filenames = {};
 
 
6500
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6501
  __attribute__((cleanup(cleanup_close)))
 
 
6502
    const int read_pipe = pipefds[0];
 
 
6503
  __attribute__((cleanup(cleanup_close)))
 
 
6504
    const int write_pipe = pipefds[1];
 
 
6505
  bool quit_now = false;
 
 
6507
  __attribute__((nonnull))
 
 
6508
    void read_func(const task_context task,
 
 
6509
                   __attribute__((unused)) task_queue *const q){
 
 
6510
    *task.quit_now = true;
 
 
6512
  task_context task1 = {
 
 
6514
    .quit_now=&quit_now,
 
 
6517
  g_assert_true(add_to_queue(queue, task1));
 
 
6519
  __attribute__((nonnull))
 
 
6520
    void write_func(const task_context task,
 
 
6521
                    __attribute__((unused)) task_queue *const q){
 
 
6522
    *task.quit_now = true;
 
 
6524
  task_context task2 = {
 
 
6526
    .quit_now=&quit_now,
 
 
6529
  g_assert_true(add_to_queue(queue, task2));
 
 
6531
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6532
  g_assert_true(quit_now);
 
 
6534
  /* Either read_pipe or write_pipe should be closed already */
 
 
6536
  bool close_read_pipe = (close(read_pipe) == -1);
 
 
6537
  close_read_pipe &= (errno == EBADF);
 
 
6539
  bool close_write_pipe = (close(write_pipe) == -1);
 
 
6540
  close_write_pipe &= (errno == EBADF);
 
 
6541
  g_assert_true(close_read_pipe xor close_write_pipe);
 
 
6542
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6545
static void test_setup_signal_handler(__attribute__((unused))
 
 
6546
                                      test_fixture *fixture,
 
 
6547
                                      __attribute__((unused))
 
 
6548
                                      gconstpointer user_data){
 
 
6549
  /* Save current SIGCHLD action, whatever it is */
 
 
6550
  struct sigaction expected_sigchld_action;
 
 
6551
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
6554
  /* Act; i.e. run the setup_signal_handler() function */
 
 
6555
  struct sigaction actual_old_sigchld_action;
 
 
6556
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
 
6558
  /* Check that the function correctly set "actual_old_sigchld_action"
 
 
6559
     to the same values as the previously saved
 
 
6560
     "expected_sigchld_action" */
 
 
6561
  /* Check member sa_handler */
 
 
6562
  g_assert_true(actual_old_sigchld_action.sa_handler
 
 
6563
                == expected_sigchld_action.sa_handler);
 
 
6564
  /* Check member sa_mask */
 
 
6565
  for(int signum = 1; signum < NSIG; signum++){
 
 
6566
    const int expected_old_block_state
 
 
6567
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
6568
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
6569
    const int actual_old_block_state
 
 
6570
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
 
6571
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
6572
    g_assert_cmpint(actual_old_block_state,
 
 
6573
                    ==, expected_old_block_state);
 
 
6575
  /* Check member sa_flags */
 
 
6576
  g_assert_true((actual_old_sigchld_action.sa_flags
 
 
6577
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
6578
                == (expected_sigchld_action.sa_flags
 
 
6579
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
6581
  /* Retrieve the current signal handler for SIGCHLD as set by
 
 
6582
     setup_signal_handler() */
 
 
6583
  struct sigaction actual_new_sigchld_action;
 
 
6584
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
6585
                            &actual_new_sigchld_action), ==, 0);
 
 
6586
  /* Check that the signal handler (member sa_handler) is correctly
 
 
6587
     set to the "handle_sigchld" function */
 
 
6588
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
 
6589
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
 
6590
  g_assert_true(actual_new_sigchld_action.sa_handler
 
 
6592
  /* Check (in member sa_mask) that at least a handful of signals are
 
 
6593
     actually blocked during the signal handler */
 
 
6594
  for(int signum = 1; signum < NSIG; signum++){
 
 
6595
    int actual_new_block_state;
 
 
6601
      actual_new_block_state
 
 
6602
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
 
6603
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
 
6605
    case SIGKILL:               /* non-blockable */
 
 
6606
    case SIGSTOP:               /* non-blockable */
 
 
6607
    case SIGCHLD:               /* always blocked */
 
 
6612
  /* Check member sa_flags */
 
 
6613
  g_assert_true((actual_new_sigchld_action.sa_flags
 
 
6614
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
6615
                == (SA_NOCLDSTOP | SA_RESTART));
 
 
6617
  /* Restore signal handler */
 
 
6618
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
 
6622
static void test_restore_signal_handler(__attribute__((unused))
 
 
6623
                                        test_fixture *fixture,
 
 
6624
                                        __attribute__((unused))
 
 
6625
                                        gconstpointer user_data){
 
 
6626
  /* Save current SIGCHLD action, whatever it is */
 
 
6627
  struct sigaction expected_sigchld_action;
 
 
6628
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
6630
  /* Since we haven't established a signal handler yet, there should
 
 
6631
     not be one established.  But another test may have relied on
 
 
6632
     restore_signal_handler() to restore the signal handler, and if
 
 
6633
     restore_signal_handler() is buggy (which we should be prepared
 
 
6634
     for in this test) the signal handler may not have been restored
 
 
6635
     properly; check for this: */
 
 
6636
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
 
6638
  /* Establish a signal handler */
 
 
6639
  struct sigaction sigchld_action = {
 
 
6640
    .sa_handler=handle_sigchld,
 
 
6641
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
6643
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
 
6644
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
 
6646
  /* Act; i.e. run the restore_signal_handler() function */
 
 
6647
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
 
6649
  /* Retrieve the restored signal handler data */
 
 
6650
  struct sigaction actual_restored_sigchld_action;
 
 
6651
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
6652
                            &actual_restored_sigchld_action), ==, 0);
 
 
6654
  /* Check that the function correctly restored the signal action, as
 
 
6655
     saved in "actual_restored_sigchld_action", to the same values as
 
 
6656
     the previously saved "expected_sigchld_action" */
 
 
6657
  /* Check member sa_handler */
 
 
6658
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
 
6659
                == expected_sigchld_action.sa_handler);
 
 
6660
  /* Check member sa_mask */
 
 
6661
  for(int signum = 1; signum < NSIG; signum++){
 
 
6662
    const int expected_old_block_state
 
 
6663
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
6664
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
6665
    const int actual_restored_block_state
 
 
6666
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
 
6667
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
 
6668
    g_assert_cmpint(actual_restored_block_state,
 
 
6669
                    ==, expected_old_block_state);
 
 
6671
  /* Check member sa_flags */
 
 
6672
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
 
6673
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
6674
                == (expected_sigchld_action.sa_flags
 
 
6675
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
6678
static void test_block_sigchld(__attribute__((unused))
 
 
6679
                               test_fixture *fixture,
 
 
6680
                               __attribute__((unused))
 
 
6681
                               gconstpointer user_data){
 
 
6682
  /* Save original signal mask */
 
 
6683
  sigset_t expected_sigmask;
 
 
6684
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
 
6687
  /* Make sure SIGCHLD is unblocked for this test */
 
 
6688
  sigset_t sigchld_sigmask;
 
 
6689
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
6690
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
6691
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
 
6694
  /* Act; i.e. run the block_sigchld() function */
 
 
6695
  sigset_t actual_old_sigmask;
 
 
6696
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
 
6698
  /* Check the actual_old_sigmask; it should be the same as the
 
 
6699
     previously saved signal mask "expected_sigmask". */
 
 
6700
  for(int signum = 1; signum < NSIG; signum++){
 
 
6701
    const int expected_old_block_state
 
 
6702
      = sigismember(&expected_sigmask, signum);
 
 
6703
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
6704
    const int actual_old_block_state
 
 
6705
      = sigismember(&actual_old_sigmask, signum);
 
 
6706
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
6707
    g_assert_cmpint(actual_old_block_state,
 
 
6708
                    ==, expected_old_block_state);
 
 
6711
  /* Retrieve the newly set signal mask */
 
 
6712
  sigset_t actual_sigmask;
 
 
6713
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
 
6715
  /* SIGCHLD should be blocked */
 
 
6716
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
 
6718
  /* Restore signal mask */
 
 
6719
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
 
6723
static void test_restore_sigmask(__attribute__((unused))
 
 
6724
                                 test_fixture *fixture,
 
 
6725
                                 __attribute__((unused))
 
 
6726
                                 gconstpointer user_data){
 
 
6727
  /* Save original signal mask */
 
 
6728
  sigset_t orig_sigmask;
 
 
6729
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
 
6731
  /* Make sure SIGCHLD is blocked for this test */
 
 
6732
  sigset_t sigchld_sigmask;
 
 
6733
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
6734
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
6735
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
 
6738
  /* Act; i.e. run the restore_sigmask() function */
 
 
6739
  g_assert_true(restore_sigmask(&orig_sigmask));
 
 
6741
  /* Retrieve the newly restored signal mask */
 
 
6742
  sigset_t restored_sigmask;
 
 
6743
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
 
6746
  /* Check the restored_sigmask; it should be the same as the
 
 
6747
     previously saved signal mask "orig_sigmask". */
 
 
6748
  for(int signum = 1; signum < NSIG; signum++){
 
 
6749
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
 
6750
    g_assert_cmpint(orig_block_state, >=, 0);
 
 
6751
    const int restored_block_state = sigismember(&restored_sigmask,
 
 
6753
    g_assert_cmpint(restored_block_state, >=, 0);
 
 
6754
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
 
6757
  /* Restore signal mask */
 
 
6758
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
 
6762
static void test_parse_arguments_noargs(__attribute__((unused))
 
 
6763
                                        test_fixture *fixture,
 
 
6764
                                        __attribute__((unused))
 
 
6765
                                        gconstpointer user_data){
 
 
6769
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6771
  char *agent_directory = NULL;
 
 
6772
  char *helper_directory = NULL;
 
 
6775
  char *mandos_argz = NULL;
 
 
6776
  size_t mandos_argz_length = 0;
 
 
6778
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
6779
                                &helper_directory, &user, &group,
 
 
6780
                                &mandos_argz, &mandos_argz_length));
 
 
6781
  g_assert_null(agent_directory);
 
 
6782
  g_assert_null(helper_directory);
 
 
6783
  g_assert_true(user == 0);
 
 
6784
  g_assert_true(group == 0);
 
 
6785
  g_assert_null(mandos_argz);
 
 
6786
  g_assert_true(mandos_argz_length == 0);
 
 
6788
  for(char **arg = argv; *arg != NULL; arg++){
 
 
6793
__attribute__((nonnull))
 
 
6794
static bool parse_arguments_devnull(int argc, char *argv[],
 
 
6795
                                    const bool exit_failure,
 
 
6796
                                    char **agent_directory,
 
 
6797
                                    char **helper_directory,
 
 
6801
                                    size_t *mandos_argz_length){
 
 
6803
  FILE *real_stderr = stderr;
 
 
6804
  FILE *devnull = fopen("/dev/null", "we");
 
 
6805
  g_assert_nonnull(devnull);
 
 
6808
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
 
6810
                                   helper_directory, user, group,
 
 
6811
                                   mandos_argz, mandos_argz_length);
 
 
6812
  const error_t saved_errno = errno;
 
 
6814
  stderr = real_stderr;
 
 
6815
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
6817
  errno = saved_errno;
 
 
6822
static void test_parse_arguments_invalid(__attribute__((unused))
 
 
6823
                                         test_fixture *fixture,
 
 
6824
                                         __attribute__((unused))
 
 
6825
                                         gconstpointer user_data){
 
 
6828
    strdup("--invalid"),
 
 
6830
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6832
  char *agent_directory = NULL;
 
 
6833
  char *helper_directory = NULL;
 
 
6836
  char *mandos_argz = NULL;
 
 
6837
  size_t mandos_argz_length = 0;
 
 
6839
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
6841
                                         &helper_directory, &user,
 
 
6842
                                         &group, &mandos_argz,
 
 
6843
                                         &mandos_argz_length));
 
 
6845
  g_assert_true(errno == EINVAL);
 
 
6846
  g_assert_null(agent_directory);
 
 
6847
  g_assert_null(helper_directory);
 
 
6848
  g_assert_null(mandos_argz);
 
 
6849
  g_assert_true(mandos_argz_length == 0);
 
 
6851
  for(char **arg = argv; *arg != NULL; arg++){
 
 
6856
static void test_parse_arguments_long_dir(__attribute__((unused))
 
 
6857
                                          test_fixture *fixture,
 
 
6858
                                          __attribute__((unused))
 
 
6859
                                          gconstpointer user_data){
 
 
6862
    strdup("--agent-directory"),
 
 
6865
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6867
  __attribute__((cleanup(cleanup_string)))
 
 
6868
    char *agent_directory = NULL;
 
 
6869
  char *helper_directory = NULL;
 
 
6872
  __attribute__((cleanup(cleanup_string)))
 
 
6873
    char *mandos_argz = NULL;
 
 
6874
  size_t mandos_argz_length = 0;
 
 
6876
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
6877
                                &helper_directory, &user, &group,
 
 
6878
                                &mandos_argz, &mandos_argz_length));
 
 
6880
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
6881
  g_assert_null(helper_directory);
 
 
6882
  g_assert_true(user == 0);
 
 
6883
  g_assert_true(group == 0);
 
 
6884
  g_assert_null(mandos_argz);
 
 
6885
  g_assert_true(mandos_argz_length == 0);
 
 
6887
  for(char **arg = argv; *arg != NULL; arg++){
 
 
6892
static void test_parse_arguments_short_dir(__attribute__((unused))
 
 
6893
                                           test_fixture *fixture,
 
 
6894
                                           __attribute__((unused))
 
 
6895
                                           gconstpointer user_data){
 
 
6901
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6903
  __attribute__((cleanup(cleanup_string)))
 
 
6904
    char *agent_directory = NULL;
 
 
6905
  char *helper_directory = NULL;
 
 
6908
  __attribute__((cleanup(cleanup_string)))
 
 
6909
    char *mandos_argz = NULL;
 
 
6910
  size_t mandos_argz_length = 0;
 
 
6912
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
6913
                                &helper_directory, &user, &group,
 
 
6914
                                &mandos_argz, &mandos_argz_length));
 
 
6916
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
6917
  g_assert_null(helper_directory);
 
 
6918
  g_assert_true(user == 0);
 
 
6919
  g_assert_true(group == 0);
 
 
6920
  g_assert_null(mandos_argz);
 
 
6921
  g_assert_true(mandos_argz_length == 0);
 
 
6923
  for(char **arg = argv; *arg != NULL; arg++){
 
 
6929
void test_parse_arguments_helper_directory(__attribute__((unused))
 
 
6930
                                           test_fixture *fixture,
 
 
6931
                                           __attribute__((unused))
 
 
6932
                                           gconstpointer user_data){
 
 
6935
    strdup("--helper-directory"),
 
 
6938
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6940
  char *agent_directory = NULL;
 
 
6941
  __attribute__((cleanup(cleanup_string)))
 
 
6942
    char *helper_directory = NULL;
 
 
6945
  __attribute__((cleanup(cleanup_string)))
 
 
6946
    char *mandos_argz = NULL;
 
 
6947
  size_t mandos_argz_length = 0;
 
 
6949
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
6950
                                &helper_directory, &user, &group,
 
 
6951
                                &mandos_argz, &mandos_argz_length));
 
 
6953
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
6954
  g_assert_null(agent_directory);
 
 
6955
  g_assert_true(user == 0);
 
 
6956
  g_assert_true(group == 0);
 
 
6957
  g_assert_null(mandos_argz);
 
 
6958
  g_assert_true(mandos_argz_length == 0);
 
 
6960
  for(char **arg = argv; *arg != NULL; arg++){
 
 
6966
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
 
6967
                                            test_fixture *fixture,
 
 
6968
                                            __attribute__((unused))
 
 
6969
                                            gconstpointer user_data){
 
 
6972
    strdup("--plugin-helper-dir"),
 
 
6975
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
6977
  char *agent_directory = NULL;
 
 
6978
  __attribute__((cleanup(cleanup_string)))
 
 
6979
    char *helper_directory = NULL;
 
 
6982
  __attribute__((cleanup(cleanup_string)))
 
 
6983
    char *mandos_argz = NULL;
 
 
6984
  size_t mandos_argz_length = 0;
 
 
6986
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
6987
                                &helper_directory, &user, &group,
 
 
6988
                                &mandos_argz, &mandos_argz_length));
 
 
6990
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
6991
  g_assert_null(agent_directory);
 
 
6992
  g_assert_true(user == 0);
 
 
6993
  g_assert_true(group == 0);
 
 
6994
  g_assert_null(mandos_argz);
 
 
6995
  g_assert_true(mandos_argz_length == 0);
 
 
6997
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7002
static void test_parse_arguments_user(__attribute__((unused))
 
 
7003
                                      test_fixture *fixture,
 
 
7004
                                      __attribute__((unused))
 
 
7005
                                      gconstpointer user_data){
 
 
7011
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7013
  char *agent_directory = NULL;
 
 
7014
  __attribute__((cleanup(cleanup_string)))
 
 
7015
    char *helper_directory = NULL;
 
 
7018
  __attribute__((cleanup(cleanup_string)))
 
 
7019
    char *mandos_argz = NULL;
 
 
7020
  size_t mandos_argz_length = 0;
 
 
7022
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7023
                                &helper_directory, &user, &group,
 
 
7024
                                &mandos_argz, &mandos_argz_length));
 
 
7026
  g_assert_null(helper_directory);
 
 
7027
  g_assert_null(agent_directory);
 
 
7028
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
 
7029
  g_assert_true(group == 0);
 
 
7030
  g_assert_null(mandos_argz);
 
 
7031
  g_assert_true(mandos_argz_length == 0);
 
 
7033
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7038
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
 
7039
                                              test_fixture *fixture,
 
 
7040
                                              __attribute__((unused))
 
 
7048
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7050
  char *agent_directory = NULL;
 
 
7051
  __attribute__((cleanup(cleanup_string)))
 
 
7052
    char *helper_directory = NULL;
 
 
7055
  __attribute__((cleanup(cleanup_string)))
 
 
7056
    char *mandos_argz = NULL;
 
 
7057
  size_t mandos_argz_length = 0;
 
 
7059
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7061
                                         &helper_directory, &user,
 
 
7062
                                         &group, &mandos_argz,
 
 
7063
                                         &mandos_argz_length));
 
 
7065
  g_assert_null(helper_directory);
 
 
7066
  g_assert_null(agent_directory);
 
 
7067
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7068
  g_assert_true(group == 0);
 
 
7069
  g_assert_null(mandos_argz);
 
 
7070
  g_assert_true(mandos_argz_length == 0);
 
 
7072
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7078
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
 
7079
                                            test_fixture *fixture,
 
 
7080
                                            __attribute__((unused))
 
 
7081
                                            gconstpointer user_data){
 
 
7087
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7089
  char *agent_directory = NULL;
 
 
7090
  __attribute__((cleanup(cleanup_string)))
 
 
7091
    char *helper_directory = NULL;
 
 
7094
  __attribute__((cleanup(cleanup_string)))
 
 
7095
    char *mandos_argz = NULL;
 
 
7096
  size_t mandos_argz_length = 0;
 
 
7098
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7100
                                         &helper_directory, &user,
 
 
7101
                                         &group, &mandos_argz,
 
 
7102
                                         &mandos_argz_length));
 
 
7104
  g_assert_null(helper_directory);
 
 
7105
  g_assert_null(agent_directory);
 
 
7106
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7107
  g_assert_true(group == 0);
 
 
7108
  g_assert_null(mandos_argz);
 
 
7109
  g_assert_true(mandos_argz_length == 0);
 
 
7111
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7116
static void test_parse_arguments_group(__attribute__((unused))
 
 
7117
                                       test_fixture *fixture,
 
 
7118
                                       __attribute__((unused))
 
 
7119
                                       gconstpointer user_data){
 
 
7125
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7127
  char *agent_directory = NULL;
 
 
7128
  __attribute__((cleanup(cleanup_string)))
 
 
7129
    char *helper_directory = NULL;
 
 
7132
  __attribute__((cleanup(cleanup_string)))
 
 
7133
    char *mandos_argz = NULL;
 
 
7134
  size_t mandos_argz_length = 0;
 
 
7136
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7137
                                &helper_directory, &user, &group,
 
 
7138
                                &mandos_argz, &mandos_argz_length));
 
 
7140
  g_assert_null(helper_directory);
 
 
7141
  g_assert_null(agent_directory);
 
 
7142
  g_assert_true(user == 0);
 
 
7143
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
 
7144
  g_assert_null(mandos_argz);
 
 
7145
  g_assert_true(mandos_argz_length == 0);
 
 
7147
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7152
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
 
7153
                                               test_fixture *fixture,
 
 
7154
                                               __attribute__((unused))
 
 
7162
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7164
  char *agent_directory = NULL;
 
 
7165
  __attribute__((cleanup(cleanup_string)))
 
 
7166
    char *helper_directory = NULL;
 
 
7169
  __attribute__((cleanup(cleanup_string)))
 
 
7170
    char *mandos_argz = NULL;
 
 
7171
  size_t mandos_argz_length = 0;
 
 
7173
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7175
                                         &helper_directory, &user,
 
 
7176
                                         &group, &mandos_argz,
 
 
7177
                                         &mandos_argz_length));
 
 
7179
  g_assert_null(helper_directory);
 
 
7180
  g_assert_null(agent_directory);
 
 
7181
  g_assert_true(user == 0);
 
 
7182
  g_assert_true(group == 0);
 
 
7183
  g_assert_null(mandos_argz);
 
 
7184
  g_assert_true(mandos_argz_length == 0);
 
 
7186
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7192
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
 
7193
                                             test_fixture *fixture,
 
 
7194
                                             __attribute__((unused))
 
 
7195
                                             gconstpointer user_data){
 
 
7201
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7203
  char *agent_directory = NULL;
 
 
7204
  __attribute__((cleanup(cleanup_string)))
 
 
7205
    char *helper_directory = NULL;
 
 
7208
  __attribute__((cleanup(cleanup_string)))
 
 
7209
    char *mandos_argz = NULL;
 
 
7210
  size_t mandos_argz_length = 0;
 
 
7212
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7214
                                         &helper_directory, &user,
 
 
7215
                                         &group, &mandos_argz,
 
 
7216
                                         &mandos_argz_length));
 
 
7218
  g_assert_null(helper_directory);
 
 
7219
  g_assert_null(agent_directory);
 
 
7220
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
 
7221
  g_assert_true(group == 0);
 
 
7222
  g_assert_null(mandos_argz);
 
 
7223
  g_assert_true(mandos_argz_length == 0);
 
 
7225
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7230
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
 
7231
                                               test_fixture *fixture,
 
 
7232
                                               __attribute__((unused))
 
 
7237
    strdup("mandos-client"),
 
 
7239
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7241
  __attribute__((cleanup(cleanup_string)))
 
 
7242
    char *agent_directory = NULL;
 
 
7243
  __attribute__((cleanup(cleanup_string)))
 
 
7244
    char *helper_directory = NULL;
 
 
7247
  __attribute__((cleanup(cleanup_string)))
 
 
7248
    char *mandos_argz = NULL;
 
 
7249
  size_t mandos_argz_length = 0;
 
 
7251
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7252
                                &helper_directory, &user, &group,
 
 
7253
                                &mandos_argz, &mandos_argz_length));
 
 
7255
  g_assert_null(agent_directory);
 
 
7256
  g_assert_null(helper_directory);
 
 
7257
  g_assert_true(user == 0);
 
 
7258
  g_assert_true(group == 0);
 
 
7259
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
 
7260
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7261
                                            mandos_argz_length),
 
 
7264
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7269
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
 
7270
                                             test_fixture *fixture,
 
 
7271
                                             __attribute__((unused))
 
 
7272
                                             gconstpointer user_data){
 
 
7275
    strdup("mandos-client"),
 
 
7280
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7282
  __attribute__((cleanup(cleanup_string)))
 
 
7283
    char *agent_directory = NULL;
 
 
7284
  __attribute__((cleanup(cleanup_string)))
 
 
7285
    char *helper_directory = NULL;
 
 
7288
  __attribute__((cleanup(cleanup_string)))
 
 
7289
    char *mandos_argz = NULL;
 
 
7290
  size_t mandos_argz_length = 0;
 
 
7292
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7293
                                &helper_directory, &user, &group,
 
 
7294
                                &mandos_argz, &mandos_argz_length));
 
 
7296
  g_assert_null(agent_directory);
 
 
7297
  g_assert_null(helper_directory);
 
 
7298
  g_assert_true(user == 0);
 
 
7299
  g_assert_true(group == 0);
 
 
7300
  char *marg = mandos_argz;
 
 
7301
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7302
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7303
  g_assert_cmpstr(marg, ==, "one");
 
 
7304
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7305
  g_assert_cmpstr(marg, ==, "two");
 
 
7306
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7307
  g_assert_cmpstr(marg, ==, "three");
 
 
7308
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7309
                                            mandos_argz_length),
 
 
7312
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7317
static void test_parse_arguments_all_args(__attribute__((unused))
 
 
7318
                                          test_fixture *fixture,
 
 
7319
                                          __attribute__((unused))
 
 
7320
                                          gconstpointer user_data){
 
 
7323
    strdup("--agent-directory"),
 
 
7325
    strdup("--helper-directory"),
 
 
7331
    strdup("mandos-client"),
 
 
7336
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7338
  __attribute__((cleanup(cleanup_string)))
 
 
7339
    char *agent_directory = NULL;
 
 
7340
  __attribute__((cleanup(cleanup_string)))
 
 
7341
    char *helper_directory = NULL;
 
 
7344
  __attribute__((cleanup(cleanup_string)))
 
 
7345
    char *mandos_argz = NULL;
 
 
7346
  size_t mandos_argz_length = 0;
 
 
7348
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7349
                                &helper_directory, &user, &group,
 
 
7350
                                &mandos_argz, &mandos_argz_length));
 
 
7352
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7353
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7354
  g_assert_true(user == 1);
 
 
7355
  g_assert_true(group == 2);
 
 
7356
  char *marg = mandos_argz;
 
 
7357
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7358
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7359
  g_assert_cmpstr(marg, ==, "one");
 
 
7360
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7361
  g_assert_cmpstr(marg, ==, "two");
 
 
7362
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7363
  g_assert_cmpstr(marg, ==, "three");
 
 
7364
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7365
                                            mandos_argz_length),
 
 
7368
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7373
static void test_parse_arguments_mixed(__attribute__((unused))
 
 
7374
                                       test_fixture *fixture,
 
 
7375
                                       __attribute__((unused))
 
 
7376
                                       gconstpointer user_data){
 
 
7379
    strdup("mandos-client"),
 
 
7383
    strdup("--agent-directory"),
 
 
7387
    strdup("--helper-directory=/var/tmp"),
 
 
7389
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7391
  __attribute__((cleanup(cleanup_string)))
 
 
7392
    char *agent_directory = NULL;
 
 
7393
  __attribute__((cleanup(cleanup_string)))
 
 
7394
    char *helper_directory = NULL;
 
 
7397
  __attribute__((cleanup(cleanup_string)))
 
 
7398
    char *mandos_argz = NULL;
 
 
7399
  size_t mandos_argz_length = 0;
 
 
7401
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7402
                                &helper_directory, &user, &group,
 
 
7403
                                &mandos_argz, &mandos_argz_length));
 
 
7405
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7406
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7407
  g_assert_true(user == 1);
 
 
7408
  g_assert_true(group == 0);
 
 
7409
  char *marg = mandos_argz;
 
 
7410
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7411
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7412
  g_assert_cmpstr(marg, ==, "one");
 
 
7413
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7414
  g_assert_cmpstr(marg, ==, "two");
 
 
7415
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7416
  g_assert_cmpstr(marg, ==, "three");
 
 
7417
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7418
                                            mandos_argz_length),
 
 
7421
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7426
/* End of tests section */
 
 
7428
/* Test boilerplate section; New tests should be added to the test
 
 
7429
   suite definition here, in the "run_tests" function.
 
 
7431
   Finally, this section also contains the should_only_run_tests()
 
 
7432
   function used by main() for deciding if tests should be run or to
 
 
7435
__attribute__((cold))
 
 
7436
static bool run_tests(int argc, char *argv[]){
 
 
7437
  g_test_init(&argc, &argv, NULL);
 
 
7439
  /* A macro to add a test with no setup or teardown functions */
 
 
7440
#define test_add(testpath, testfunc)                    \
 
 
7442
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
 
7443
               (testfunc), NULL);                       \
 
 
7446
  /* Test the signal-related functions first, since some other tests
 
 
7447
     depend on these functions in their setups and teardowns */
 
 
7448
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
 
7449
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
 
7450
  test_add("/signal-handling/block", test_block_sigchld);
 
 
7451
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
 
7453
  /* Regular non-signal-related tests; these use no setups or
 
 
7455
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
 
7456
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
 
7457
  test_add("/parse_arguments/long-dir",
 
 
7458
           test_parse_arguments_long_dir);
 
 
7459
  test_add("/parse_arguments/short-dir",
 
 
7460
           test_parse_arguments_short_dir);
 
 
7461
  test_add("/parse_arguments/helper-directory",
 
 
7462
           test_parse_arguments_helper_directory);
 
 
7463
  test_add("/parse_arguments/plugin-helper-dir",
 
 
7464
           test_parse_arguments_plugin_helper_dir);
 
 
7465
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
 
7466
  test_add("/parse_arguments/user-invalid",
 
 
7467
           test_parse_arguments_user_invalid);
 
 
7468
  test_add("/parse_arguments/user-zero-invalid",
 
 
7469
           test_parse_arguments_user_zero_invalid);
 
 
7470
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
 
7471
  test_add("/parse_arguments/group-invalid",
 
 
7472
           test_parse_arguments_group_invalid);
 
 
7473
  test_add("/parse_arguments/group-zero-invalid",
 
 
7474
           test_parse_arguments_group_zero_invalid);
 
 
7475
  test_add("/parse_arguments/mandos-noargs",
 
 
7476
           test_parse_arguments_mandos_noargs);
 
 
7477
  test_add("/parse_arguments/mandos-args",
 
 
7478
           test_parse_arguments_mandos_args);
 
 
7479
  test_add("/parse_arguments/all-args",
 
 
7480
           test_parse_arguments_all_args);
 
 
7481
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
 
7482
  test_add("/queue/create", test_create_queue);
 
 
7483
  test_add("/queue/add", test_add_to_queue);
 
 
7484
  test_add("/queue/has_question/empty",
 
 
7485
           test_queue_has_question_empty);
 
 
7486
  test_add("/queue/has_question/false",
 
 
7487
           test_queue_has_question_false);
 
 
7488
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
 
7489
  test_add("/queue/has_question/false2",
 
 
7490
           test_queue_has_question_false2);
 
 
7491
  test_add("/queue/has_question/true2",
 
 
7492
           test_queue_has_question_true2);
 
 
7493
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
 
7494
  test_add("/string_set/net-set-contains-nothing",
 
 
7495
           test_string_set_new_set_contains_nothing);
 
 
7496
  test_add("/string_set/with-added-string-contains-it",
 
 
7497
           test_string_set_with_added_string_contains_it);
 
 
7498
  test_add("/string_set/cleared-does-not-contain-string",
 
 
7499
           test_string_set_cleared_does_not_contain_str);
 
 
7500
  test_add("/string_set/swap/one-with-empty",
 
 
7501
           test_string_set_swap_one_with_empty);
 
 
7502
  test_add("/string_set/swap/empty-with-one",
 
 
7503
           test_string_set_swap_empty_with_one);
 
 
7504
  test_add("/string_set/swap/one-with-one",
 
 
7505
           test_string_set_swap_one_with_one);
 
 
7507
  /* A macro to add a test using the setup and teardown functions */
 
 
7508
#define test_add_st(path, func)                                 \
 
 
7510
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
 
7514
  /* Signal-related tests; these use setups and teardowns which
 
 
7515
     establish, during each test run, a signal handler for, and a
 
 
7516
     signal mask blocking, the SIGCHLD signal, just like main() */
 
 
7517
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
 
7518
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
 
7519
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
 
7520
  test_add_st("/run_queue/zeroes-next-run",
 
 
7521
              test_run_queue_zeroes_next_run);
 
 
7522
  test_add_st("/run_queue/clears-cancelled_filenames",
 
 
7523
              test_run_queue_clears_cancelled_filenames);
 
 
7524
  test_add_st("/run_queue/skips-cancelled-filenames",
 
 
7525
              test_run_queue_skips_cancelled_filenames);
 
 
7526
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
 
7527
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
 
7528
  test_add_st("/run_queue/two-tasks/quit",
 
 
7529
              test_run_queue_two_tasks_quit);
 
 
7530
  test_add_st("/run_queue/two-tasks-cleanup",
 
 
7531
              test_run_queue_two_tasks_cleanup);
 
 
7532
  test_add_st("/task-creators/start_mandos_client",
 
 
7533
              test_start_mandos_client);
 
 
7534
  test_add_st("/task-creators/start_mandos_client/execv",
 
 
7535
              test_start_mandos_client_execv);
 
 
7536
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
 
7537
              test_start_mandos_client_suid_euid);
 
 
7538
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
 
7539
              test_start_mandos_client_suid_egid);
 
 
7540
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
 
7541
              test_start_mandos_client_suid_ruid);
 
 
7542
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
 
7543
              test_start_mandos_client_suid_rgid);
 
 
7544
  test_add_st("/task-creators/start_mandos_client/read",
 
 
7545
              test_start_mandos_client_read);
 
 
7546
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
 
7547
              test_start_mandos_client_helper_directory);
 
 
7548
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
 
7549
              test_start_mandos_client_sigmask);
 
 
7550
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
 
7551
              test_wait_for_mandos_client_exit_badpid);
 
 
7552
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
 
7553
              test_wait_for_mandos_client_exit_noexit);
 
 
7554
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
 
7555
              test_wait_for_mandos_client_exit_success);
 
 
7556
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
 
7557
              test_wait_for_mandos_client_exit_failure);
 
 
7558
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
 
7559
              test_wait_for_mandos_client_exit_killed);
 
 
7560
  test_add_st("/task/read_mandos_client_output/readerror",
 
 
7561
              test_read_mandos_client_output_readerror);
 
 
7562
  test_add_st("/task/read_mandos_client_output/nodata",
 
 
7563
              test_read_mandos_client_output_nodata);
 
 
7564
  test_add_st("/task/read_mandos_client_output/eof",
 
 
7565
              test_read_mandos_client_output_eof);
 
 
7566
  test_add_st("/task/read_mandos_client_output/once",
 
 
7567
              test_read_mandos_client_output_once);
 
 
7568
  test_add_st("/task/read_mandos_client_output/malloc",
 
 
7569
              test_read_mandos_client_output_malloc);
 
 
7570
  test_add_st("/task/read_mandos_client_output/append",
 
 
7571
              test_read_mandos_client_output_append);
 
 
7572
  test_add_st("/task-creators/add_inotify_dir_watch",
 
 
7573
              test_add_inotify_dir_watch);
 
 
7574
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
 
7575
              test_add_inotify_dir_watch_fail);
 
 
7576
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
 
7577
              test_add_inotify_dir_watch_EAGAIN);
 
 
7578
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
 
7579
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
 
7580
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
 
7581
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
 
7582
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
 
7583
              test_add_inotify_dir_watch_IN_DELETE);
 
 
7584
  test_add_st("/task/read_inotify_event/readerror",
 
 
7585
              test_read_inotify_event_readerror);
 
 
7586
  test_add_st("/task/read_inotify_event/bad-epoll",
 
 
7587
              test_read_inotify_event_bad_epoll);
 
 
7588
  test_add_st("/task/read_inotify_event/nodata",
 
 
7589
              test_read_inotify_event_nodata);
 
 
7590
  test_add_st("/task/read_inotify_event/eof",
 
 
7591
              test_read_inotify_event_eof);
 
 
7592
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
 
7593
              test_read_inotify_event_IN_CLOSE_WRITE);
 
 
7594
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
 
7595
              test_read_inotify_event_IN_MOVED_TO);
 
 
7596
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
 
7597
              test_read_inotify_event_IN_DELETE);
 
 
7598
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
 
7599
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
 
7600
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
 
7601
              test_read_inotify_event_IN_MOVED_TO_badname);
 
 
7602
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
 
7603
              test_read_inotify_event_IN_DELETE_badname);
 
 
7604
  test_add_st("/task/open_and_parse_question/ENOENT",
 
 
7605
              test_open_and_parse_question_ENOENT);
 
 
7606
  test_add_st("/task/open_and_parse_question/EIO",
 
 
7607
              test_open_and_parse_question_EIO);
 
 
7608
  test_add_st("/task/open_and_parse_question/parse-error",
 
 
7609
              test_open_and_parse_question_parse_error);
 
 
7610
  test_add_st("/task/open_and_parse_question/nosocket",
 
 
7611
              test_open_and_parse_question_nosocket);
 
 
7612
  test_add_st("/task/open_and_parse_question/badsocket",
 
 
7613
              test_open_and_parse_question_badsocket);
 
 
7614
  test_add_st("/task/open_and_parse_question/nopid",
 
 
7615
              test_open_and_parse_question_nopid);
 
 
7616
  test_add_st("/task/open_and_parse_question/badpid",
 
 
7617
              test_open_and_parse_question_badpid);
 
 
7618
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
 
7619
              test_open_and_parse_question_noexist_pid);
 
 
7620
  test_add_st("/task/open_and_parse_question/no-notafter",
 
 
7621
              test_open_and_parse_question_no_notafter);
 
 
7622
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
 
7623
              test_open_and_parse_question_bad_notafter);
 
 
7624
  test_add_st("/task/open_and_parse_question/notafter-0",
 
 
7625
              test_open_and_parse_question_notafter_0);
 
 
7626
  test_add_st("/task/open_and_parse_question/notafter-1",
 
 
7627
              test_open_and_parse_question_notafter_1);
 
 
7628
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
 
7629
              test_open_and_parse_question_notafter_1_1);
 
 
7630
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
 
7631
              test_open_and_parse_question_notafter_1_2);
 
 
7632
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
 
7633
              test_open_and_parse_question_equal_notafter);
 
 
7634
  test_add_st("/task/open_and_parse_question/late-notafter",
 
 
7635
              test_open_and_parse_question_late_notafter);
 
 
7636
  test_add_st("/task/cancel_old_question/0-1-2",
 
 
7637
              test_cancel_old_question_0_1_2);
 
 
7638
  test_add_st("/task/cancel_old_question/0-2-1",
 
 
7639
              test_cancel_old_question_0_2_1);
 
 
7640
  test_add_st("/task/cancel_old_question/1-2-3",
 
 
7641
              test_cancel_old_question_1_2_3);
 
 
7642
  test_add_st("/task/cancel_old_question/1-3-2",
 
 
7643
              test_cancel_old_question_1_3_2);
 
 
7644
  test_add_st("/task/cancel_old_question/2-1-3",
 
 
7645
              test_cancel_old_question_2_1_3);
 
 
7646
  test_add_st("/task/cancel_old_question/2-3-1",
 
 
7647
              test_cancel_old_question_2_3_1);
 
 
7648
  test_add_st("/task/cancel_old_question/3-1-2",
 
 
7649
              test_cancel_old_question_3_1_2);
 
 
7650
  test_add_st("/task/cancel_old_question/3-2-1",
 
 
7651
              test_cancel_old_question_3_2_1);
 
 
7652
  test_add_st("/task/connect_question_socket/name-too-long",
 
 
7653
              test_connect_question_socket_name_too_long);
 
 
7654
  test_add_st("/task/connect_question_socket/connect-fail",
 
 
7655
              test_connect_question_socket_connect_fail);
 
 
7656
  test_add_st("/task/connect_question_socket/bad-epoll",
 
 
7657
              test_connect_question_socket_bad_epoll);
 
 
7658
  test_add_st("/task/connect_question_socket/usable",
 
 
7659
              test_connect_question_socket_usable);
 
 
7660
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
 
7661
              test_send_password_to_socket_client_not_exited);
 
 
7662
  test_add_st("/task/send_password_to_socket/password-not-read",
 
 
7663
              test_send_password_to_socket_password_not_read);
 
 
7664
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
 
7665
              test_send_password_to_socket_EMSGSIZE);
 
 
7666
  test_add_st("/task/send_password_to_socket/retry",
 
 
7667
              test_send_password_to_socket_retry);
 
 
7668
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
 
7669
              test_send_password_to_socket_bad_epoll);
 
 
7670
  test_add_st("/task/send_password_to_socket/null-password",
 
 
7671
              test_send_password_to_socket_null_password);
 
 
7672
  test_add_st("/task/send_password_to_socket/empty-password",
 
 
7673
              test_send_password_to_socket_empty_password);
 
 
7674
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
 
7675
              test_send_password_to_socket_empty_str_pass);
 
 
7676
  test_add_st("/task/send_password_to_socket/text-password",
 
 
7677
              test_send_password_to_socket_text_password);
 
 
7678
  test_add_st("/task/send_password_to_socket/binary-password",
 
 
7679
              test_send_password_to_socket_binary_password);
 
 
7680
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
 
7681
              test_send_password_to_socket_nuls_in_password);
 
 
7682
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
 
7683
              test_add_existing_questions_ENOENT);
 
 
7684
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
 
7685
              test_add_existing_questions_no_questions);
 
 
7686
  test_add_st("/task-creators/add_existing_questions/one-question",
 
 
7687
              test_add_existing_questions_one_question);
 
 
7688
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
 
7689
              test_add_existing_questions_two_questions);
 
 
7690
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
 
7691
              test_add_existing_questions_non_questions);
 
 
7692
  test_add_st("/task-creators/add_existing_questions/both-types",
 
 
7693
              test_add_existing_questions_both_types);
 
 
7695
  return g_test_run() == 0;
 
 
7698
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
 
7699
  GOptionContext *context = g_option_context_new("");
 
 
7701
  g_option_context_set_help_enabled(context, FALSE);
 
 
7702
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
 
7704
  gboolean run_tests = FALSE;
 
 
7705
  GOptionEntry entries[] = {
 
 
7706
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
 
7707
      &run_tests, "Run tests", NULL },
 
 
7710
  g_option_context_add_main_entries(context, entries, NULL);
 
 
7712
  GError *error = NULL;
 
 
7714
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
 
7715
    g_option_context_free(context);
 
 
7716
    g_error("Failed to parse options: %s", error->message);
 
 
7719
  g_option_context_free(context);
 
 
7720
  return run_tests != FALSE;