1
/* -*- coding: utf-8; lexical-binding: t -*- */
 
 
3
 * Mandos password agent - Simple password agent to run Mandos client
 
 
5
 * Copyright © 2019-2022 Teddy Hogeborn
 
 
6
 * Copyright © 2019-2022 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>.
 
 
26
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
 
 
27
                                   setresuid(), asprintf(), getline(),
 
 
29
#include <inttypes.h>           /* uintmax_t, strtoumax(), PRIuMAX,
 
 
30
                                   PRIdMAX, intmax_t, uint32_t,
 
 
31
                                   SCNx32, SCNuMAX, SCNxMAX */
 
 
32
#include <stddef.h>             /* size_t, NULL */
 
 
33
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
 
 
35
#include <stdbool.h>            /* bool, true, false */
 
 
36
#include <signal.h>             /* struct sigaction, sigset_t,
 
 
37
                                   sigemptyset(), sigaddset(),
 
 
38
                                   SIGCHLD, pthread_sigmask(),
 
 
39
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
 
 
40
                                   SA_NOCLDSTOP, sigfillset(), kill(),
 
 
41
                                   SIGTERM, sigdelset(), SIGKILL,
 
 
42
                                   NSIG, sigismember(), SA_ONSTACK,
 
 
43
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
 
 
44
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
 
45
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
 
46
                                   fork(), _exit(), dup2(),
 
 
47
                                   STDOUT_FILENO, setresgid(),
 
 
48
                                   setresuid(), execv(), ssize_t,
 
 
49
                                   read(), dup3(), getuid(), dup(),
 
 
50
                                   STDERR_FILENO, pause(), write(),
 
 
51
                                   rmdir(), unlink(), getpid() */
 
 
52
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
 
53
                                   malloc(), free(), realloc(),
 
 
54
                                   setenv(), calloc(), mkdtemp(),
 
 
56
#include <iso646.h>             /* not, or, and, xor */
 
 
57
#include <error.h>              /* error() */
 
 
58
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
 
 
59
#include <errno.h>              /* errno, error_t, EACCES,
 
 
60
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
 
61
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
 
62
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
 
63
                                   ECONNREFUSED, ECONNRESET,
 
 
64
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
 
 
66
#include <string.h>             /* strdup(), memcpy(),
 
 
67
                                   explicit_bzero(), memset(),
 
 
68
                                   strcmp(), strlen(), strncpy(),
 
 
69
                                   memcmp(), basename(), strerror() */
 
 
70
#include <argz.h>               /* argz_create(), argz_count(),
 
 
71
                                   argz_extract(), argz_next(),
 
 
73
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
 
 
74
                                   epoll_ctl(), EPOLL_CTL_ADD,
 
 
75
                                   struct epoll_event, EPOLLIN,
 
 
78
#include <time.h>               /* struct timespec, clock_gettime(),
 
 
80
#include <argp.h>               /* struct argp_option, OPTION_HIDDEN,
 
 
81
                                   OPTION_ALIAS, struct argp_state,
 
 
82
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
 
 
83
                                   struct argp, argp_parse(),
 
 
85
#include <stdint.h>             /* SIZE_MAX, uint32_t */
 
 
86
#include <sys/mman.h>           /* munlock(), mlock() */
 
 
87
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
 
88
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
 
89
                                   open(), O_WRONLY, O_NOCTTY,
 
 
90
                                   O_RDONLY, O_NOFOLLOW */
 
 
91
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
 
93
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
 
94
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
 
95
                                   IN_CLOEXEC, inotify_add_watch(),
 
 
96
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
 
97
                                   IN_MOVED_FROM, IN_DELETE,
 
 
98
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
 
99
                                   struct inotify_event */
 
 
100
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
 
101
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
 
102
                                   fclose(), getline(), sscanf(),
 
 
103
                                   feof(), ferror(), rename(),
 
 
104
                                   fdopen(), fprintf(), fscanf() */
 
 
105
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
 
 
106
                        GError, g_key_file_load_from_file(),
 
 
107
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
 
 
108
                        g_key_file_get_string(), guint64,
 
 
109
                        g_key_file_get_uint64(),
 
 
110
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
 
 
111
                        g_assert_true(), g_assert_nonnull(),
 
 
112
                        g_assert_null(), g_assert_false(),
 
 
113
                        g_assert_cmpint(), g_assert_cmpuint(),
 
 
114
                        g_test_skip(), g_assert_cmpstr(),
 
 
115
                        g_test_message(), g_test_init(), g_test_add(),
 
 
116
                        g_test_run(), GOptionContext,
 
 
117
                        g_option_context_new(),
 
 
118
                        g_option_context_set_help_enabled(), FALSE,
 
 
119
                        g_option_context_set_ignore_unknown_options(),
 
 
120
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
 
 
121
                        g_option_context_add_main_entries(),
 
 
122
                        g_option_context_parse(),
 
 
123
                        g_option_context_free(), g_error() */
 
 
124
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
 
 
125
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
 
 
126
                                   SOCK_DGRAM, SOCK_NONBLOCK,
 
 
127
                                   SOCK_CLOEXEC, connect(),
 
 
128
                                   struct sockaddr, socklen_t,
 
 
129
                                   shutdown(), SHUT_RD, send(),
 
 
130
                                   MSG_NOSIGNAL, bind(), recv(),
 
 
132
#include <glob.h>               /* globfree(), glob_t, glob(),
 
 
133
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
 
 
134
                                   GLOB_ABORTED, GLOB_NOMATCH,
 
 
137
/* End of includes */
 
 
139
/* Start of declarations of private types and functions */
 
 
141
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
 
 
142
typedef uintmax_t mono_microsecs;
 
 
144
/* "task_queue" - A queue of tasks to be run */
 
 
146
  struct task_struct *tasks;    /* Tasks in this queue */
 
 
147
  size_t length;                /* Number of tasks */
 
 
148
  /* Memory allocated for "tasks", in bytes */
 
 
150
  /* Time when this queue should be run, at the latest */
 
 
151
  mono_microsecs next_run;
 
 
152
} __attribute__((designated_init)) task_queue;
 
 
154
/* "task_func" - A function type for task functions
 
 
156
   I.e. functions for the code which runs when a task is run, all have
 
 
158
typedef void (task_func) (const struct task_struct,
 
 
160
  __attribute__((nonnull));
 
 
162
/* "buffer" - A data buffer for a growing array of bytes
 
 
164
   Used for the "password" variable */
 
 
169
} __attribute__((designated_init)) buffer;
 
 
171
/* "string_set" - A set type which can contain strings
 
 
173
   Used by the "cancelled_filenames" variable */
 
 
175
  char *argz;                   /* Do not access these except in */
 
 
176
  size_t argz_len;              /* the string_set_* functions */
 
 
177
} __attribute__((designated_init)) string_set;
 
 
179
/* "task_context" - local variables for tasks
 
 
181
   This data structure distinguishes between different tasks which are
 
 
182
   using the same function.  This data structure is passed to every
 
 
183
   task function when each task is run.
 
 
185
   Note that not every task uses every struct member. */
 
 
186
typedef struct task_struct {
 
 
187
  task_func *const func;         /* The function run by this task */
 
 
188
  char *const question_filename; /* The question file */
 
 
189
  const pid_t pid;               /* Mandos client process ID */
 
 
190
  const int epoll_fd;            /* The epoll set file descriptor */
 
 
191
  bool *const quit_now;          /* Set to true on fatal errors */
 
 
192
  const int fd;                  /* General purpose file descriptor */
 
 
193
  bool *const mandos_client_exited; /* Set true when client exits */
 
 
194
  buffer *const password;           /* As read from client process */
 
 
195
  bool *const password_is_read;     /* "password" is done growing */
 
 
196
  char *filename;                   /* General purpose file name */
 
 
197
  /* A set of strings of all the file names of questions which have
 
 
198
     been cancelled for any reason; tasks pertaining to these question
 
 
199
     files should not be run */
 
 
200
  string_set *const cancelled_filenames;
 
 
201
  const mono_microsecs notafter; /* "NotAfter" from question file */
 
 
202
  /* Updated before each queue run; is compared with queue.next_run */
 
 
203
  const mono_microsecs *const current_time;
 
 
204
} __attribute__((designated_init)) task_context;
 
 
206
/* Declare all our functions here so we can define them in any order
 
 
207
   below.  Note: test functions are *not* declared here, they are
 
 
208
   declared in the test section. */
 
 
209
__attribute__((warn_unused_result))
 
 
210
static bool should_only_run_tests(int *, char **[]);
 
 
211
__attribute__((warn_unused_result, cold))
 
 
212
static bool run_tests(int, char *[]);
 
 
213
static void handle_sigchld(__attribute__((unused)) int sig){}
 
 
214
__attribute__((warn_unused_result, malloc))
 
 
215
task_queue *create_queue(void);
 
 
216
__attribute__((nonnull, warn_unused_result))
 
 
217
bool add_to_queue(task_queue *const, const task_context);
 
 
218
__attribute__((nonnull))
 
 
219
void cleanup_task(const task_context *const);
 
 
220
__attribute__((nonnull))
 
 
221
void cleanup_queue(task_queue *const *const);
 
 
222
__attribute__((pure, nonnull, warn_unused_result))
 
 
223
bool queue_has_question(const task_queue *const);
 
 
224
__attribute__((nonnull))
 
 
225
void cleanup_close(const int *const);
 
 
226
__attribute__((nonnull))
 
 
227
void cleanup_string(char *const *const);
 
 
228
__attribute__((nonnull))
 
 
229
void cleanup_buffer(buffer *const);
 
 
230
__attribute__((pure, nonnull, warn_unused_result))
 
 
231
bool string_set_contains(const string_set, const char *const);
 
 
232
__attribute__((nonnull, warn_unused_result))
 
 
233
bool string_set_add(string_set *const, const char *const);
 
 
234
__attribute__((nonnull))
 
 
235
void string_set_clear(string_set *);
 
 
236
void string_set_swap(string_set *const, string_set *const);
 
 
237
__attribute__((nonnull, warn_unused_result))
 
 
238
bool start_mandos_client(task_queue *const, const int, bool *const,
 
 
239
                         bool *const, buffer *const, bool *const,
 
 
240
                         const struct sigaction *const,
 
 
241
                         const sigset_t, const char *const,
 
 
242
                         const uid_t, const gid_t,
 
 
243
                         const char *const *const);
 
 
244
__attribute__((nonnull))
 
 
245
task_func wait_for_mandos_client_exit;
 
 
246
__attribute__((nonnull))
 
 
247
task_func read_mandos_client_output;
 
 
248
__attribute__((warn_unused_result))
 
 
249
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
 
 
250
                           buffer *const, const char *const,
 
 
251
                           string_set *, const mono_microsecs *const,
 
 
252
                           bool *const, bool *const);
 
 
253
__attribute__((nonnull))
 
 
254
task_func read_inotify_event;
 
 
255
__attribute__((nonnull))
 
 
256
task_func open_and_parse_question;
 
 
257
__attribute__((nonnull))
 
 
258
task_func cancel_old_question;
 
 
259
__attribute__((nonnull))
 
 
260
task_func connect_question_socket;
 
 
261
__attribute__((nonnull))
 
 
262
task_func send_password_to_socket;
 
 
263
__attribute__((warn_unused_result))
 
 
264
bool add_existing_questions(task_queue *const, const int,
 
 
265
                            buffer *const, string_set *,
 
 
266
                            const mono_microsecs *const,
 
 
267
                            bool *const, bool *const,
 
 
269
__attribute__((nonnull, warn_unused_result))
 
 
270
bool wait_for_event(const int, const mono_microsecs,
 
 
271
                    const mono_microsecs);
 
 
272
bool run_queue(task_queue **const, string_set *const, bool *const);
 
 
273
bool clear_all_fds_from_epoll_set(const int);
 
 
274
mono_microsecs get_current_time(void);
 
 
275
__attribute__((nonnull, warn_unused_result))
 
 
276
bool setup_signal_handler(struct sigaction *const);
 
 
277
__attribute__((nonnull))
 
 
278
bool restore_signal_handler(const struct sigaction *const);
 
 
279
__attribute__((nonnull, warn_unused_result))
 
 
280
bool block_sigchld(sigset_t *const);
 
 
281
__attribute__((nonnull))
 
 
282
bool restore_sigmask(const sigset_t *const);
 
 
283
__attribute__((nonnull))
 
 
284
bool parse_arguments(int, char *[], const bool, char **, char **,
 
 
285
                     uid_t *const , gid_t *const, char **, size_t *);
 
 
287
/* End of declarations of private types and functions */
 
 
289
/* Start of "main" section; this section LACKS TESTS!
 
 
291
   Code here should be as simple as possible. */
 
 
293
/* These are required to be global by Argp */
 
 
294
const char *argp_program_version = "password-agent " VERSION;
 
 
295
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
 
297
int main(int argc, char *argv[]){
 
 
299
  /* If the --test option is passed, skip all normal operations and
 
 
300
     instead only run the run_tests() function, which also does all
 
 
301
     its own option parsing, so we don't have to do anything here. */
 
 
302
  if(should_only_run_tests(&argc, &argv)){
 
 
303
    if(run_tests(argc, argv)){
 
 
304
      return EXIT_SUCCESS;      /* All tests successful */
 
 
306
    return EXIT_FAILURE;        /* Some test(s) failed */
 
 
309
  __attribute__((cleanup(cleanup_string)))
 
 
310
    char *agent_directory = NULL;
 
 
312
  __attribute__((cleanup(cleanup_string)))
 
 
313
    char *helper_directory = NULL;
 
 
318
  __attribute__((cleanup(cleanup_string)))
 
 
319
    char *mandos_argz = NULL;
 
 
320
  size_t mandos_argz_length = 0;
 
 
322
  if(not parse_arguments(argc, argv, true, &agent_directory,
 
 
323
                         &helper_directory, &user, &group,
 
 
324
                         &mandos_argz, &mandos_argz_length)){
 
 
325
    /* This should never happen, since "true" is passed as the third
 
 
326
       argument to parse_arguments() above, which should make
 
 
327
       argp_parse() call exit() if any parsing error occurs. */
 
 
328
    error(EX_USAGE, errno, "Failed to parse arguments");
 
 
331
  const char default_agent_directory[] = "/run/systemd/ask-password";
 
 
332
  const char default_helper_directory[]
 
 
333
    = "/lib/mandos/plugin-helpers";
 
 
334
  const char *const default_argv[]
 
 
335
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
 
 
337
  /* Set variables to default values if unset */
 
 
338
  if(agent_directory == NULL){
 
 
339
    agent_directory = strdup(default_agent_directory);
 
 
340
    if(agent_directory == NULL){
 
 
341
      error(EX_OSERR, errno, "Failed strdup()");
 
 
344
  if(helper_directory == NULL){
 
 
345
    helper_directory = strdup(default_helper_directory);
 
 
346
    if(helper_directory == NULL){
 
 
347
      error(EX_OSERR, errno, "Failed strdup()");
 
 
351
    user = 65534;               /* nobody */
 
 
354
    group = 65534;              /* nogroup */
 
 
356
  /* If parse_opt did not create an argz vector, create one with
 
 
358
  if(mandos_argz == NULL){
 
 
360
#pragma GCC diagnostic push
 
 
361
    /* argz_create() takes a non-const argv for some unknown reason -
 
 
362
       argz_create() isn't modifying the strings, just copying them.
 
 
363
       Therefore, this cast to non-const should be safe. */
 
 
364
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
366
    errno = argz_create((char *const *)default_argv, &mandos_argz,
 
 
367
                        &mandos_argz_length);
 
 
369
#pragma GCC diagnostic pop
 
 
372
      error(EX_OSERR, errno, "Failed argz_create()");
 
 
375
  /* Use argz vector to create a normal argv, usable by execv() */
 
 
377
  char **mandos_argv = malloc((argz_count(mandos_argz,
 
 
379
                               + 1) * sizeof(char *));
 
 
380
  if(mandos_argv == NULL){
 
 
381
    error_t saved_errno = errno;
 
 
383
    error(EX_OSERR, saved_errno, "Failed malloc()");
 
 
385
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
 
 
387
  sigset_t orig_sigmask;
 
 
388
  if(not block_sigchld(&orig_sigmask)){
 
 
392
  struct sigaction old_sigchld_action;
 
 
393
  if(not setup_signal_handler(&old_sigchld_action)){
 
 
397
  mono_microsecs current_time = 0;
 
 
399
  bool mandos_client_exited = false;
 
 
400
  bool quit_now = false;
 
 
401
  __attribute__((cleanup(cleanup_close)))
 
 
402
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
404
    error(EX_OSERR, errno, "Failed to create epoll set fd");
 
 
406
  __attribute__((cleanup(cleanup_queue)))
 
 
407
    task_queue *queue = create_queue();
 
 
409
    error(EX_OSERR, errno, "Failed to create task queue");
 
 
412
  __attribute__((cleanup(cleanup_buffer)))
 
 
413
    buffer password = {};
 
 
414
  bool password_is_read = false;
 
 
416
  __attribute__((cleanup(string_set_clear)))
 
 
417
    string_set cancelled_filenames = {};
 
 
419
  /* Add tasks to queue */
 
 
420
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
 
 
421
                             &quit_now, &password, &password_is_read,
 
 
422
                             &old_sigchld_action, orig_sigmask,
 
 
423
                             helper_directory, user, group,
 
 
424
                             (const char *const *)mandos_argv)){
 
 
425
    return EX_OSERR;            /* Error has already been printed */
 
 
427
  /* These variables were only for start_mandos_client() and are not
 
 
432
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
 
 
433
                               agent_directory, &cancelled_filenames,
 
 
434
                               ¤t_time, &mandos_client_exited,
 
 
436
    switch(errno){              /* Error has already been printed */
 
 
446
  if(not add_existing_questions(queue, epoll_fd, &password,
 
 
447
                                &cancelled_filenames, ¤t_time,
 
 
448
                                &mandos_client_exited,
 
 
449
                                &password_is_read, agent_directory)){
 
 
450
    return EXIT_FAILURE;        /* Error has already been printed */
 
 
455
    current_time = get_current_time();
 
 
456
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
 
 
457
      const error_t saved_errno = errno;
 
 
458
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
 
 
462
    current_time = get_current_time();
 
 
463
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
 
 
464
      const error_t saved_errno = errno;
 
 
465
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
 
 
468
    /*  When no tasks about questions are left in the queue, break out
 
 
469
        of the loop (and implicitly exit the program) */
 
 
470
  } while(queue_has_question(queue));
 
 
472
  restore_signal_handler(&old_sigchld_action);
 
 
473
  restore_sigmask(&orig_sigmask);
 
 
478
__attribute__((warn_unused_result))
 
 
479
mono_microsecs get_current_time(void){
 
 
480
  struct timespec currtime;
 
 
481
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
 
 
482
    error(0, errno, "Failed to get current time");
 
 
485
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
 
 
486
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
 
 
489
/* End of "main" section */
 
 
491
/* Start of regular code section; ALL this code has tests */
 
 
493
__attribute__((nonnull))
 
 
494
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
 
 
495
                     char **agent_directory, char **helper_directory,
 
 
496
                     uid_t *const user, gid_t *const group,
 
 
497
                     char **mandos_argz, size_t *mandos_argz_length){
 
 
499
  const struct argp_option options[] = {
 
 
500
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
 
 
501
      .doc="Systemd password agent directory" },
 
 
502
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
 
 
503
      .doc="Mandos Client password helper directory" },
 
 
504
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
 
 
505
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
 
506
    { .name="user", .key='u', .arg="USERID",
 
 
507
      .doc="User ID the Mandos Client will use as its unprivileged"
 
 
509
    { .name="userid", .key=130, /* From plugin--runner */
 
 
510
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
 
511
    { .name="group", .key='g', .arg="GROUPID",
 
 
512
      .doc="Group ID the Mandos Client will use as its unprivileged"
 
 
514
    { .name="groupid", .key=131, /* From plugin--runner */
 
 
515
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
 
516
    { .name="test", .key=255, /* See should_only_run_tests() */
 
 
517
      .doc="Skip normal operation, and only run self-tests.  See"
 
 
518
      " --test --help.", .group=10, },
 
 
522
  __attribute__((nonnull(3)))
 
 
523
    error_t parse_opt(int key, char *arg, struct argp_state *state){
 
 
526
    case 'd':                   /* --agent-directory */
 
 
527
      *agent_directory = strdup(arg);
 
 
529
    case 128:                   /* --helper-directory */
 
 
530
    case 129:                   /* --plugin-helper-dir */
 
 
531
      *helper_directory = strdup(arg);
 
 
533
    case 'u':                   /* --user */
 
 
534
    case 130:                   /* --userid */
 
 
537
        uintmax_t tmp_id = 0;
 
 
539
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
 
540
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
541
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
 
 
542
          return ARGP_ERR_UNKNOWN;
 
 
544
        *user = (uid_t)tmp_id;
 
 
548
    case 'g':                   /* --group */
 
 
549
    case 131:                   /* --groupid */
 
 
552
        uintmax_t tmp_id = 0;
 
 
554
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
 
555
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
556
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
 
 
557
          return ARGP_ERR_UNKNOWN;
 
 
559
        *group = (gid_t)tmp_id;
 
 
564
      /* Copy arguments into argz vector */
 
 
565
      return argz_create(state->argv + state->next, mandos_argz,
 
 
568
      return ARGP_ERR_UNKNOWN;
 
 
573
  const struct argp argp = {
 
 
576
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
 
 
577
    .doc = "Mandos password agent -- runs Mandos client as a"
 
 
578
    " systemd password agent",
 
 
581
  errno = argp_parse(&argp, argc, argv,
 
 
582
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
 
 
587
__attribute__((nonnull, warn_unused_result))
 
 
588
bool block_sigchld(sigset_t *const orig_sigmask){
 
 
589
  sigset_t sigchld_sigmask;
 
 
590
  if(sigemptyset(&sigchld_sigmask) < 0){
 
 
591
    error(0, errno, "Failed to empty signal set");
 
 
594
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
 
 
595
    error(0, errno, "Failed to add SIGCHLD to signal set");
 
 
598
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
 
 
599
    error(0, errno, "Failed to block SIGCHLD signal");
 
 
605
__attribute__((nonnull, warn_unused_result, const))
 
 
606
bool restore_sigmask(const sigset_t *const orig_sigmask){
 
 
607
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
 
 
608
    error(0, errno, "Failed to restore blocked signals");
 
 
614
__attribute__((nonnull, warn_unused_result))
 
 
615
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
 
 
616
  struct sigaction sigchld_action = {
 
 
617
    .sa_handler=handle_sigchld,
 
 
618
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
620
  /* Set all signals in "sa_mask" struct member; this makes all
 
 
621
     signals automatically blocked during signal handler */
 
 
622
  if(sigfillset(&sigchld_action.sa_mask) != 0){
 
 
623
    error(0, errno, "Failed to do sigfillset()");
 
 
626
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
 
 
627
    error(0, errno, "Failed to set SIGCHLD signal handler");
 
 
633
__attribute__((nonnull, warn_unused_result))
 
 
634
bool restore_signal_handler(const struct sigaction *const
 
 
636
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
 
 
637
    error(0, errno, "Failed to restore signal handler");
 
 
643
__attribute__((warn_unused_result, malloc))
 
 
644
task_queue *create_queue(void){
 
 
645
  task_queue *queue = malloc(sizeof(task_queue));
 
 
649
    queue->allocated = 0;
 
 
655
__attribute__((nonnull, warn_unused_result))
 
 
656
bool add_to_queue(task_queue *const queue, const task_context task){
 
 
657
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
 
659
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
 
660
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
 
664
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
 
665
  if(needed_size > (queue->allocated)){
 
 
666
    task_context *const new_tasks = realloc(queue->tasks,
 
 
668
    if(new_tasks == NULL){
 
 
669
      error(0, errno, "Failed to allocate %" PRIuMAX
 
 
670
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
 
673
    queue->tasks = new_tasks;
 
 
674
    queue->allocated = needed_size;
 
 
676
  /* Using memcpy here is necessary because doing */
 
 
677
  /* queue->tasks[queue->length++] = task; */
 
 
678
  /* would violate const-ness of task members */
 
 
679
  memcpy(&(queue->tasks[queue->length++]), &task,
 
 
680
         sizeof(task_context));
 
 
684
__attribute__((nonnull))
 
 
685
void cleanup_task(const task_context *const task){
 
 
686
  const error_t saved_errno = errno;
 
 
687
  /* free and close all task data */
 
 
688
  free(task->question_filename);
 
 
689
  if(task->filename != task->question_filename){
 
 
690
    free(task->filename);
 
 
693
    kill(task->pid, SIGTERM);
 
 
701
__attribute__((nonnull))
 
 
702
void free_queue(task_queue *const queue){
 
 
707
__attribute__((nonnull))
 
 
708
void cleanup_queue(task_queue *const *const queue){
 
 
712
  for(size_t i = 0; i < (*queue)->length; i++){
 
 
713
    const task_context *const task = ((*queue)->tasks)+i;
 
 
719
__attribute__((pure, nonnull, warn_unused_result))
 
 
720
bool queue_has_question(const task_queue *const queue){
 
 
721
  for(size_t i=0; i < queue->length; i++){
 
 
722
    if(queue->tasks[i].question_filename != NULL){
 
 
729
__attribute__((nonnull))
 
 
730
void cleanup_close(const int *const fd){
 
 
731
  const error_t saved_errno = errno;
 
 
736
__attribute__((nonnull))
 
 
737
void cleanup_string(char *const *const ptr){
 
 
741
__attribute__((nonnull))
 
 
742
void cleanup_buffer(buffer *buf){
 
 
743
  if(buf->allocated > 0){
 
 
744
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
745
    explicit_bzero(buf->data, buf->allocated);
 
 
747
    memset(buf->data, '\0', buf->allocated);
 
 
750
  if(buf->data != NULL){
 
 
751
    if(munlock(buf->data, buf->allocated) != 0){
 
 
752
      error(0, errno, "Failed to unlock memory of old buffer");
 
 
761
__attribute__((pure, nonnull, warn_unused_result))
 
 
762
bool string_set_contains(const string_set set, const char *const str){
 
 
763
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
 
764
      s = argz_next(set.argz, set.argz_len, s)){
 
 
765
    if(strcmp(s, str) == 0){
 
 
772
__attribute__((nonnull, warn_unused_result))
 
 
773
bool string_set_add(string_set *const set, const char *const str){
 
 
774
  if(string_set_contains(*set, str)){
 
 
777
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
 
785
__attribute__((nonnull))
 
 
786
void string_set_clear(string_set *set){
 
 
792
__attribute__((nonnull))
 
 
793
void string_set_swap(string_set *const set1, string_set *const set2){
 
 
794
  /* Swap contents of two string sets */
 
 
796
    char *const tmp_argz = set1->argz;
 
 
797
    set1->argz = set2->argz;
 
 
798
    set2->argz = tmp_argz;
 
 
801
    const size_t tmp_argz_len = set1->argz_len;
 
 
802
    set1->argz_len = set2->argz_len;
 
 
803
    set2->argz_len = tmp_argz_len;
 
 
807
__attribute__((nonnull, warn_unused_result))
 
 
808
bool start_mandos_client(task_queue *const queue,
 
 
810
                         bool *const mandos_client_exited,
 
 
811
                         bool *const quit_now, buffer *const password,
 
 
812
                         bool *const password_is_read,
 
 
813
                         const struct sigaction *const
 
 
814
                         old_sigchld_action, const sigset_t sigmask,
 
 
815
                         const char *const helper_directory,
 
 
816
                         const uid_t user, const gid_t group,
 
 
817
                         const char *const *const argv){
 
 
819
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
 
820
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
 
824
  const pid_t pid = fork();
 
 
826
    if(not restore_signal_handler(old_sigchld_action)){
 
 
829
    if(not restore_sigmask(&sigmask)){
 
 
832
    if(close(pipefds[0]) != 0){
 
 
833
      error(0, errno, "Failed to close() parent pipe fd");
 
 
836
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
 
837
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
 
840
    if(close(pipefds[1]) != 0){
 
 
841
      error(0, errno, "Failed to close() old child pipe fd");
 
 
844
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
 
845
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
 
846
            " \"%s\", 1)", helper_directory);
 
 
849
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
 
850
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
 
851
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
854
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
 
855
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
 
856
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
860
#pragma GCC diagnostic push
 
 
861
    /* For historical reasons, the "argv" argument to execv() is not
 
 
862
       const, but it is safe to override this. */
 
 
863
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
865
    execv(argv[0], (char **)argv);
 
 
867
#pragma GCC diagnostic pop
 
 
869
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
 
875
    error(0, errno, "Failed to fork()");
 
 
880
  if(not add_to_queue(queue, (task_context){
 
 
881
        .func=wait_for_mandos_client_exit,
 
 
883
        .mandos_client_exited=mandos_client_exited,
 
 
886
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
 
891
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
 
892
                            &(struct epoll_event)
 
 
893
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
894
  if(ret != 0 and errno != EEXIST){
 
 
895
    error(0, errno, "Failed to add file descriptor to epoll set");
 
 
900
  return add_to_queue(queue, (task_context){
 
 
901
      .func=read_mandos_client_output,
 
 
906
      .password_is_read=password_is_read,
 
 
910
__attribute__((nonnull))
 
 
911
void wait_for_mandos_client_exit(const task_context task,
 
 
912
                                 task_queue *const queue){
 
 
913
  const pid_t pid = task.pid;
 
 
914
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
915
  bool *const quit_now = task.quit_now;
 
 
918
  switch(waitpid(pid, &status, WNOHANG)){
 
 
919
  case 0:                       /* Not exited yet */
 
 
920
    if(not add_to_queue(queue, task)){
 
 
921
      error(0, errno, "Failed to add myself to queue");
 
 
926
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
 
932
  default:                      /* Has exited */
 
 
933
    *mandos_client_exited = true;
 
 
934
    if((not WIFEXITED(status))
 
 
935
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
 
936
      error(0, 0, "Mandos client failed or was killed");
 
 
942
__attribute__((nonnull))
 
 
943
void read_mandos_client_output(const task_context task,
 
 
944
                               task_queue *const queue){
 
 
945
  buffer *const password = task.password;
 
 
946
  bool *const quit_now = task.quit_now;
 
 
947
  bool *const password_is_read = task.password_is_read;
 
 
948
  const int fd = task.fd;
 
 
949
  const int epoll_fd = task.epoll_fd;
 
 
951
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
 
952
  if(password->allocated < new_potential_size){
 
 
953
    char *const new_buffer = calloc(new_potential_size, 1);
 
 
954
    if(new_buffer == NULL){
 
 
955
      error(0, errno, "Failed to allocate %" PRIuMAX
 
 
956
            " bytes for password", (uintmax_t)new_potential_size);
 
 
961
    if(mlock(new_buffer, new_potential_size) != 0){
 
 
962
      /* Warn but do not treat as fatal error */
 
 
963
      if(errno != EPERM and errno != ENOMEM){
 
 
964
        error(0, errno, "Failed to lock memory for password");
 
 
967
    if(password->length > 0){
 
 
968
      memcpy(new_buffer, password->data, password->length);
 
 
969
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
970
      explicit_bzero(password->data, password->allocated);
 
 
972
      memset(password->data, '\0', password->allocated);
 
 
975
    if(password->data != NULL){
 
 
976
      if(munlock(password->data, password->allocated) != 0){
 
 
977
        error(0, errno, "Failed to unlock memory of old buffer");
 
 
979
      free(password->data);
 
 
981
    password->data = new_buffer;
 
 
982
    password->allocated = new_potential_size;
 
 
985
  const ssize_t read_length = read(fd, password->data
 
 
986
                                   + password->length, PIPE_BUF);
 
 
988
  if(read_length == 0){ /* EOF */
 
 
989
    *password_is_read = true;
 
 
993
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
 
994
    error(0, errno, "Failed to read password from Mandos client");
 
 
999
  if(read_length > 0){          /* Data has been read */
 
 
1000
    password->length += (size_t)read_length;
 
 
1003
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
 
1006
  /* Re-add the fd to the epoll set */
 
 
1007
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1008
                            &(struct epoll_event)
 
 
1009
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1010
  if(ret != 0 and errno != EEXIST){
 
 
1011
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
 
1017
  /* Re-add myself to the queue */
 
 
1018
  if(not add_to_queue(queue, task)){
 
 
1019
    error(0, errno, "Failed to add myself to queue");
 
 
1025
__attribute__((nonnull, warn_unused_result))
 
 
1026
bool add_inotify_dir_watch(task_queue *const queue,
 
 
1027
                           const int epoll_fd, bool *const quit_now,
 
 
1028
                           buffer *const password,
 
 
1029
                           const char *const dir,
 
 
1030
                           string_set *cancelled_filenames,
 
 
1031
                           const mono_microsecs *const current_time,
 
 
1032
                           bool *const mandos_client_exited,
 
 
1033
                           bool *const password_is_read){
 
 
1034
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
 
1036
    error(0, errno, "Failed to create inotify instance");
 
 
1040
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
 
1041
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
 
 
1044
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
 
1048
  /* Add the inotify fd to the epoll set */
 
 
1049
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1050
                            &(struct epoll_event)
 
 
1051
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1052
  if(ret != 0 and errno != EEXIST){
 
 
1053
    error(0, errno, "Failed to add file descriptor to epoll set");
 
 
1058
  const task_context read_inotify_event_task = {
 
 
1059
    .func=read_inotify_event,
 
 
1064
    .filename=strdup(dir),
 
 
1065
    .cancelled_filenames=cancelled_filenames,
 
 
1066
    .current_time=current_time,
 
 
1067
    .mandos_client_exited=mandos_client_exited,
 
 
1068
    .password_is_read=password_is_read,
 
 
1070
  if(read_inotify_event_task.filename == NULL){
 
 
1071
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
 
1076
  return add_to_queue(queue, read_inotify_event_task);
 
 
1079
__attribute__((nonnull))
 
 
1080
void read_inotify_event(const task_context task,
 
 
1081
                        task_queue *const queue){
 
 
1082
  const int fd = task.fd;
 
 
1083
  const int epoll_fd = task.epoll_fd;
 
 
1084
  char *const filename = task.filename;
 
 
1085
  bool *quit_now = task.quit_now;
 
 
1086
  buffer *const password = task.password;
 
 
1087
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1088
  const mono_microsecs *const current_time = task.current_time;
 
 
1089
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1090
  bool *const password_is_read = task.password_is_read;
 
 
1092
  /* "sufficient to read at least one event." - inotify(7) */
 
 
1093
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
1096
    struct inotify_event event;
 
 
1097
    char name_buffer[NAME_MAX + 1];
 
 
1099
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
1101
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
 
1102
  if(read_length == 0){ /* EOF */
 
 
1103
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
 
1105
    cleanup_task(&task);
 
 
1108
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
 
1109
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
 
1112
    cleanup_task(&task);
 
 
1115
  if(read_length > 0            /* Data has been read */
 
 
1116
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
 
1117
    char *question_filename = NULL;
 
 
1118
    const ssize_t question_filename_length
 
 
1119
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
 
1120
    if(question_filename_length < 0){
 
 
1121
      error(0, errno, "Failed to create file name from directory name"
 
 
1122
            " %s and file name %s", filename, ievent->name);
 
 
1124
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
 
1125
        if(not add_to_queue(queue, (task_context){
 
 
1126
              .func=open_and_parse_question,
 
 
1128
              .question_filename=question_filename,
 
 
1129
              .filename=question_filename,
 
 
1131
              .cancelled_filenames=cancelled_filenames,
 
 
1132
              .current_time=current_time,
 
 
1133
              .mandos_client_exited=mandos_client_exited,
 
 
1134
              .password_is_read=password_is_read,
 
 
1136
          error(0, errno, "Failed to add open_and_parse_question task"
 
 
1137
                " for file name %s to queue", filename);
 
 
1139
          /* Force the added task (open_and_parse_question) to run
 
 
1141
          queue->next_run = 1;
 
 
1143
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
 
1144
        if(not string_set_add(cancelled_filenames,
 
 
1145
                              question_filename)){
 
 
1146
          error(0, errno, "Could not add question %s to"
 
 
1147
                " cancelled_questions", question_filename);
 
 
1149
          free(question_filename);
 
 
1150
          cleanup_task(&task);
 
 
1153
        free(question_filename);
 
 
1158
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
 
1161
  /* Re-add myself to the queue */
 
 
1162
  if(not add_to_queue(queue, task)){
 
 
1163
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
 
1164
          " queue", filename);
 
 
1166
    cleanup_task(&task);
 
 
1170
  /* Re-add the fd to the epoll set */
 
 
1171
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1172
                            &(struct epoll_event)
 
 
1173
                            { .events=EPOLLIN | EPOLLRDHUP });
 
 
1174
  if(ret != 0 and errno != EEXIST){
 
 
1175
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
 
1176
          " directory %s to epoll set", fd, filename);
 
 
1177
    /* Force the added task (read_inotify_event) to run again, at most
 
 
1178
       one second from now */
 
 
1179
    if((queue->next_run == 0)
 
 
1180
       or (queue->next_run > (*current_time + 1000000))){
 
 
1181
      queue->next_run = *current_time + 1000000;
 
 
1186
__attribute__((nonnull))
 
 
1187
void open_and_parse_question(const task_context task,
 
 
1188
                             task_queue *const queue){
 
 
1189
  __attribute__((cleanup(cleanup_string)))
 
 
1190
    char *question_filename = task.question_filename;
 
 
1191
  const int epoll_fd = task.epoll_fd;
 
 
1192
  buffer *const password = task.password;
 
 
1193
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1194
  const mono_microsecs *const current_time = task.current_time;
 
 
1195
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1196
  bool *const password_is_read = task.password_is_read;
 
 
1198
  /* We use the GLib "Key-value file parser" functions to parse the
 
 
1199
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
 
1200
     specification of contents */
 
 
1201
  __attribute__((nonnull))
 
 
1202
    void cleanup_g_key_file(GKeyFile **key_file){
 
 
1203
    if(*key_file != NULL){
 
 
1204
      g_key_file_free(*key_file);
 
 
1208
  __attribute__((cleanup(cleanup_g_key_file)))
 
 
1209
    GKeyFile *key_file = g_key_file_new();
 
 
1210
  if(key_file == NULL){
 
 
1211
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
 
1215
  GError *glib_error = NULL;
 
 
1216
  if(g_key_file_load_from_file(key_file, question_filename,
 
 
1217
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
 
1218
    /* If a file was removed, we should ignore it, so */
 
 
1219
    /* only show error message if file actually existed */
 
 
1220
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
 
1221
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
 
1222
            question_filename, glib_error->message);
 
 
1227
  __attribute__((cleanup(cleanup_string)))
 
 
1228
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
 
1231
  if(socket_name == NULL){
 
 
1232
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
 
1233
          question_filename, glib_error->message);
 
 
1237
  if(strlen(socket_name) == 0){
 
 
1238
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
 
1243
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
 
1245
  if(glib_error != NULL){
 
 
1246
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
 
1247
          question_filename, glib_error->message);
 
 
1251
  if((pid != (guint64)((pid_t)pid))
 
 
1252
     or (kill((pid_t)pid, 0) != 0)){
 
 
1253
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
 
1254
          " does not exist", (uintmax_t)pid, question_filename);
 
 
1258
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
 
1259
                                           "NotAfter", &glib_error);
 
 
1260
  if(glib_error != NULL){
 
 
1261
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
 
1262
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
 
1263
            " %s", question_filename, glib_error->message);
 
 
1268
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
 
1269
      queue->next_run = notafter;
 
 
1271
    if(*current_time >= notafter){
 
 
1276
  const task_context connect_question_socket_task = {
 
 
1277
    .func=connect_question_socket,
 
 
1278
    .question_filename=strdup(question_filename),
 
 
1281
    .filename=strdup(socket_name),
 
 
1282
    .cancelled_filenames=task.cancelled_filenames,
 
 
1283
    .mandos_client_exited=mandos_client_exited,
 
 
1284
    .password_is_read=password_is_read,
 
 
1285
    .current_time=current_time,
 
 
1287
  if(connect_question_socket_task.question_filename == NULL
 
 
1288
     or connect_question_socket_task.filename == NULL
 
 
1289
     or not add_to_queue(queue, connect_question_socket_task)){
 
 
1290
    error(0, errno, "Failed to add connect_question_socket for socket"
 
 
1291
          " %s (from \"%s\") to queue", socket_name,
 
 
1293
    cleanup_task(&connect_question_socket_task);
 
 
1296
  /* Force the added task (connect_question_socket) to run
 
 
1298
  queue->next_run = 1;
 
 
1301
    char *const dup_filename = strdup(question_filename);
 
 
1302
    const task_context cancel_old_question_task = {
 
 
1303
      .func=cancel_old_question,
 
 
1304
      .question_filename=dup_filename,
 
 
1306
      .filename=dup_filename,
 
 
1307
      .cancelled_filenames=cancelled_filenames,
 
 
1308
      .current_time=current_time,
 
 
1310
    if(cancel_old_question_task.question_filename == NULL
 
 
1311
       or not add_to_queue(queue, cancel_old_question_task)){
 
 
1312
      error(0, errno, "Failed to add cancel_old_question for file "
 
 
1313
            "\"%s\" to queue", question_filename);
 
 
1314
      cleanup_task(&cancel_old_question_task);
 
 
1320
__attribute__((nonnull))
 
 
1321
void cancel_old_question(const task_context task,
 
 
1322
                         task_queue *const queue){
 
 
1323
  char *const question_filename = task.question_filename;
 
 
1324
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1325
  const mono_microsecs notafter = task.notafter;
 
 
1326
  const mono_microsecs *const current_time = task.current_time;
 
 
1328
  if(*current_time >= notafter){
 
 
1329
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1330
      error(0, errno, "Failed to cancel question for file %s",
 
 
1333
    cleanup_task(&task);
 
 
1337
  if(not add_to_queue(queue, task)){
 
 
1338
    error(0, errno, "Failed to add cancel_old_question for file "
 
 
1339
          "%s to queue", question_filename);
 
 
1340
    cleanup_task(&task);
 
 
1344
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
 
1345
    queue->next_run = notafter;
 
 
1349
__attribute__((nonnull))
 
 
1350
void connect_question_socket(const task_context task,
 
 
1351
                             task_queue *const queue){
 
 
1352
  char *const question_filename = task.question_filename;
 
 
1353
  char *const filename = task.filename;
 
 
1354
  const int epoll_fd = task.epoll_fd;
 
 
1355
  buffer *const password = task.password;
 
 
1356
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
 
1357
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1358
  bool *const password_is_read = task.password_is_read;
 
 
1359
  const mono_microsecs *const current_time = task.current_time;
 
 
1361
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
1363
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
 
1364
    error(0, 0, "Socket filename is larger than"
 
 
1365
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
 
1366
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
 
1367
    if(not string_set_add(cancelled_filenames, question_filename)){
 
 
1368
      error(0, errno, "Failed to cancel question for file %s",
 
 
1371
    cleanup_task(&task);
 
 
1375
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
1376
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
1379
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
 
1380
    if(not add_to_queue(queue, task)){
 
 
1381
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1382
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1384
      cleanup_task(&task);
 
 
1386
      /* Force the added task (connect_question_socket) to run
 
 
1388
      queue->next_run = 1;
 
 
1393
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
1394
  if(connect(fd, (struct sockaddr *)&sock_name,
 
 
1395
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
 
1396
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
 
1397
    if(not add_to_queue(queue, task)){
 
 
1398
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1399
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1401
      cleanup_task(&task);
 
 
1403
      /* Force the added task (connect_question_socket) to run again,
 
 
1404
         at most one second from now */
 
 
1405
      if((queue->next_run == 0)
 
 
1406
         or (queue->next_run > (*current_time + 1000000))){
 
 
1407
        queue->next_run = *current_time + 1000000;
 
 
1413
  /* Not necessary, but we can try, and merely warn on failure */
 
 
1414
  if(shutdown(fd, SHUT_RD) != 0){
 
 
1415
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
 
1419
  /* Add the fd to the epoll set */
 
 
1420
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1421
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1423
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
 
1424
          " socket %s to epoll set", fd, filename);
 
 
1425
    if(not add_to_queue(queue, task)){
 
 
1426
      error(0, errno, "Failed to add connect_question_socket for file"
 
 
1427
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
 
1429
      cleanup_task(&task);
 
 
1431
      /* Force the added task (connect_question_socket) to run again,
 
 
1432
         at most one second from now */
 
 
1433
      if((queue->next_run == 0)
 
 
1434
         or (queue->next_run > (*current_time + 1000000))){
 
 
1435
        queue->next_run = *current_time + 1000000;
 
 
1441
  /* add task send_password_to_socket to queue */
 
 
1442
  const task_context send_password_to_socket_task = {
 
 
1443
    .func=send_password_to_socket,
 
 
1444
    .question_filename=question_filename,
 
 
1449
    .cancelled_filenames=cancelled_filenames,
 
 
1450
    .mandos_client_exited=mandos_client_exited,
 
 
1451
    .password_is_read=password_is_read,
 
 
1452
    .current_time=current_time,
 
 
1455
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
 
1456
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1457
          " file \"%s\" and socket \"%s\" to queue",
 
 
1458
          question_filename, filename);
 
 
1459
    cleanup_task(&send_password_to_socket_task);
 
 
1463
__attribute__((nonnull))
 
 
1464
void send_password_to_socket(const task_context task,
 
 
1465
                             task_queue *const queue){
 
 
1466
  char *const question_filename=task.question_filename;
 
 
1467
  char *const filename=task.filename;
 
 
1468
  const int epoll_fd=task.epoll_fd;
 
 
1469
  const int fd=task.fd;
 
 
1470
  buffer *const password=task.password;
 
 
1471
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
 
1472
  bool *const mandos_client_exited = task.mandos_client_exited;
 
 
1473
  bool *const password_is_read = task.password_is_read;
 
 
1474
  const mono_microsecs *const current_time = task.current_time;
 
 
1476
  if(*mandos_client_exited and *password_is_read){
 
 
1478
    const size_t send_buffer_length = password->length + 2;
 
 
1479
    char *send_buffer = malloc(send_buffer_length);
 
 
1480
    if(send_buffer == NULL){
 
 
1481
      error(0, errno, "Failed to allocate send_buffer");
 
 
1483
      if(mlock(send_buffer, send_buffer_length) != 0){
 
 
1484
        /* Warn but do not treat as fatal error */
 
 
1485
        if(errno != EPERM and errno != ENOMEM){
 
 
1486
          error(0, errno, "Failed to lock memory for password"
 
 
1490
      /* “[…] send a single datagram to the socket consisting of the
 
 
1491
         password string either prefixed with "+" or with "-"
 
 
1492
         depending on whether the password entry was successful or
 
 
1493
         not. You may but don't have to include a final NUL byte in
 
 
1496
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
 
1499
      send_buffer[0] = '+';     /* Prefix with "+" */
 
 
1500
      /* Always add an extra NUL */
 
 
1501
      send_buffer[password->length + 1] = '\0';
 
 
1502
      if(password->length > 0){
 
 
1503
        memcpy(send_buffer + 1, password->data, password->length);
 
 
1506
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
 
1508
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
 
1509
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
1510
      explicit_bzero(send_buffer, send_buffer_length);
 
 
1512
      memset(send_buffer, '\0', send_buffer_length);
 
 
1514
      if(munlock(send_buffer, send_buffer_length) != 0){
 
 
1515
        error(0, errno, "Failed to unlock memory of send buffer");
 
 
1518
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
 
1519
        switch(saved_errno){
 
 
1532
          error(0, saved_errno, "Password of size %" PRIuMAX
 
 
1533
                " is too big", (uintmax_t)password->length);
 
 
1537
          __attribute__((fallthrough));
 
 
1540
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
 
1541
            error(0, 0, "Password only partially sent to socket %s: %"
 
 
1542
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
 
1543
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
 
1548
          __attribute__((fallthrough));
 
 
1551
          error(0, saved_errno, "Failed to send() to socket %s",
 
 
1553
          if(not string_set_add(cancelled_filenames,
 
 
1554
                                question_filename)){
 
 
1555
            error(0, errno, "Failed to cancel question for file %s",
 
 
1558
          cleanup_task(&task);
 
 
1563
        cleanup_task(&task);
 
 
1569
  /* We failed or are not ready yet; retry later */
 
 
1571
  if(not add_to_queue(queue, task)){
 
 
1572
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1573
          " file %s and socket %s to queue", question_filename,
 
 
1575
    cleanup_task(&task);
 
 
1578
  /* Add the fd to the epoll set */
 
 
1579
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1580
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1582
    error(0, errno, "Failed to add socket file descriptor %d for"
 
 
1583
          " socket %s to epoll set", fd, filename);
 
 
1584
    /* Force the added task (send_password_to_socket) to run again, at
 
 
1585
       most one second from now */
 
 
1586
    if((queue->next_run == 0)
 
 
1587
       or (queue->next_run > (*current_time + 1000000))){
 
 
1588
      queue->next_run = *current_time + 1000000;
 
 
1593
__attribute__((warn_unused_result))
 
 
1594
bool add_existing_questions(task_queue *const queue,
 
 
1596
                            buffer *const password,
 
 
1597
                            string_set *cancelled_filenames,
 
 
1598
                            const mono_microsecs *const current_time,
 
 
1599
                            bool *const mandos_client_exited,
 
 
1600
                            bool *const password_is_read,
 
 
1601
                            const char *const dirname){
 
 
1602
  __attribute__((cleanup(cleanup_string)))
 
 
1603
    char *dir_pattern = NULL;
 
 
1604
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
 
1605
  if(ret < 0 or dir_pattern == NULL){
 
 
1606
    error(0, errno, "Could not create glob pattern for directory %s",
 
 
1610
  __attribute__((cleanup(globfree)))
 
 
1611
    glob_t question_filenames = {};
 
 
1612
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
 
1613
              NULL, &question_filenames)){
 
 
1616
    error(0, errno, "Failed to open directory %s", dirname);
 
 
1619
    error(0, errno, "There are no question files in %s", dirname);
 
 
1622
    error(0, errno, "Could not allocate memory for question file"
 
 
1623
          " names in %s", dirname);
 
 
1627
    __attribute__((fallthrough));
 
 
1630
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
 
1631
      char *const question_filename = strdup(question_filenames
 
 
1633
      const task_context task = {
 
 
1634
        .func=open_and_parse_question,
 
 
1636
        .question_filename=question_filename,
 
 
1637
        .filename=question_filename,
 
 
1639
        .cancelled_filenames=cancelled_filenames,
 
 
1640
        .current_time=current_time,
 
 
1641
        .mandos_client_exited=mandos_client_exited,
 
 
1642
        .password_is_read=password_is_read,
 
 
1645
      if(question_filename == NULL
 
 
1646
         or not add_to_queue(queue, task)){
 
 
1647
        error(0, errno, "Failed to add open_and_parse_question for"
 
 
1648
              " file %s to queue",
 
 
1649
              question_filenames.gl_pathv[i]);
 
 
1650
        free(question_filename);
 
 
1652
        queue->next_run = 1;
 
 
1659
__attribute__((nonnull, warn_unused_result))
 
 
1660
bool wait_for_event(const int epoll_fd,
 
 
1661
                    const mono_microsecs queue_next_run,
 
 
1662
                    const mono_microsecs current_time){
 
 
1663
  __attribute__((const))
 
 
1664
    int milliseconds_to_wait(const mono_microsecs currtime,
 
 
1665
                             const mono_microsecs nextrun){
 
 
1666
    if(currtime >= nextrun){
 
 
1669
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
 
1670
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
 
1673
    return (int)wait_time_ms;
 
 
1676
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
 
1679
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
 
1680
  sigset_t temporary_unblocked_sigmask;
 
 
1681
  /* Get current signal mask */
 
 
1682
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
 
1685
  /* Remove SIGCHLD from the signal mask */
 
 
1686
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
 
1689
  struct epoll_event events[8]; /* Ignored */
 
 
1690
  int ret = epoll_pwait(epoll_fd, events,
 
 
1691
                        sizeof(events) / sizeof(struct epoll_event),
 
 
1692
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
 
1693
                        &temporary_unblocked_sigmask);
 
 
1694
  if(ret < 0 and errno != EINTR){
 
 
1695
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
 
1697
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
 
1700
  return clear_all_fds_from_epoll_set(epoll_fd);
 
 
1703
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
 
1704
  /* Create a new empty epoll set */
 
 
1705
  __attribute__((cleanup(cleanup_close)))
 
 
1706
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
1707
  if(new_epoll_fd < 0){
 
 
1710
  /* dup3() the new epoll set fd over the old one, replacing it */
 
 
1711
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
 
1717
__attribute__((nonnull, warn_unused_result))
 
 
1718
bool run_queue(task_queue **const queue,
 
 
1719
               string_set *const cancelled_filenames,
 
 
1720
               bool *const quit_now){
 
 
1722
  task_queue *new_queue = create_queue();
 
 
1723
  if(new_queue == NULL){
 
 
1727
  __attribute__((cleanup(string_set_clear)))
 
 
1728
    string_set old_cancelled_filenames = {};
 
 
1729
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
 
1731
  /* Declare i outside the for loop, since we might need i after the
 
 
1732
     loop in case we aborted in the middle */
 
 
1734
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
 
1735
    task_context *const task = &((*queue)->tasks[i]);
 
 
1736
    const char *const question_filename = task->question_filename;
 
 
1737
    /* Skip any task referencing a cancelled question filename */
 
 
1738
    if(question_filename != NULL
 
 
1739
       and string_set_contains(old_cancelled_filenames,
 
 
1740
                               question_filename)){
 
 
1744
    task->func(*task, new_queue);
 
 
1748
    /* we might be in the middle of the queue, so clean up any
 
 
1749
       remaining tasks in the current queue */
 
 
1750
    for(; i < (*queue)->length; i++){
 
 
1751
      cleanup_task(&((*queue)->tasks[i]));
 
 
1765
/* End of regular code section */
 
 
1767
/* Start of tests section; here are the tests for the above code */
 
 
1769
/* This "fixture" data structure is used by the test setup and
 
 
1770
   teardown functions */
 
 
1772
  struct sigaction orig_sigaction;
 
 
1773
  sigset_t orig_sigmask;
 
 
1776
static void test_setup(test_fixture *fixture,
 
 
1777
                       __attribute__((unused))
 
 
1778
                       gconstpointer user_data){
 
 
1779
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
 
1780
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
 
1783
static void test_teardown(test_fixture *fixture,
 
 
1784
                          __attribute__((unused))
 
 
1785
                          gconstpointer user_data){
 
 
1786
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
 
1787
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
 
1790
/* Utility function used by tests to search queue for matching task */
 
 
1791
__attribute__((pure, nonnull, warn_unused_result))
 
 
1792
static task_context *find_matching_task(const task_queue *const queue,
 
 
1793
                                        const task_context task){
 
 
1794
  /* The argument "task" structure is a pattern to match; 0 in any
 
 
1795
     member means any value matches, otherwise the value must match.
 
 
1796
     The filename strings are compared by strcmp(), not by pointer. */
 
 
1797
  for(size_t i = 0; i < queue->length; i++){
 
 
1798
    task_context *const current_task = queue->tasks+i;
 
 
1799
    /* Check all members of task_context, if set to a non-zero value.
 
 
1800
       If a member does not match, continue to next task in queue */
 
 
1802
    /* task_func *const func */
 
 
1803
    if(task.func != NULL and current_task->func != task.func){
 
 
1806
    /* char *const question_filename; */
 
 
1807
    if(task.question_filename != NULL
 
 
1808
       and (current_task->question_filename == NULL
 
 
1809
            or strcmp(current_task->question_filename,
 
 
1810
                      task.question_filename) != 0)){
 
 
1813
    /* const pid_t pid; */
 
 
1814
    if(task.pid != 0 and current_task->pid != task.pid){
 
 
1817
    /* const int epoll_fd; */
 
 
1818
    if(task.epoll_fd != 0
 
 
1819
       and current_task->epoll_fd != task.epoll_fd){
 
 
1822
    /* bool *const quit_now; */
 
 
1823
    if(task.quit_now != NULL
 
 
1824
       and current_task->quit_now != task.quit_now){
 
 
1828
    if(task.fd != 0 and current_task->fd != task.fd){
 
 
1831
    /* bool *const mandos_client_exited; */
 
 
1832
    if(task.mandos_client_exited != NULL
 
 
1833
       and current_task->mandos_client_exited
 
 
1834
       != task.mandos_client_exited){
 
 
1837
    /* buffer *const password; */
 
 
1838
    if(task.password != NULL
 
 
1839
       and current_task->password != task.password){
 
 
1842
    /* bool *const password_is_read; */
 
 
1843
    if(task.password_is_read != NULL
 
 
1844
       and current_task->password_is_read != task.password_is_read){
 
 
1847
    /* char *filename; */
 
 
1848
    if(task.filename != NULL
 
 
1849
       and (current_task->filename == NULL
 
 
1850
            or strcmp(current_task->filename, task.filename) != 0)){
 
 
1853
    /* string_set *const cancelled_filenames; */
 
 
1854
    if(task.cancelled_filenames != NULL
 
 
1855
       and current_task->cancelled_filenames
 
 
1856
       != task.cancelled_filenames){
 
 
1859
    /* const mono_microsecs notafter; */
 
 
1860
    if(task.notafter != 0
 
 
1861
       and current_task->notafter != task.notafter){
 
 
1864
    /* const mono_microsecs *const current_time; */
 
 
1865
    if(task.current_time != NULL
 
 
1866
       and current_task->current_time != task.current_time){
 
 
1869
    /* Current task matches all members; return it */
 
 
1870
    return current_task;
 
 
1872
  /* No task in queue matches passed pattern task */
 
 
1876
static void test_create_queue(__attribute__((unused))
 
 
1877
                              test_fixture *fixture,
 
 
1878
                              __attribute__((unused))
 
 
1879
                              gconstpointer user_data){
 
 
1880
  __attribute__((cleanup(cleanup_queue)))
 
 
1881
    task_queue *const queue = create_queue();
 
 
1882
  g_assert_nonnull(queue);
 
 
1883
  g_assert_null(queue->tasks);
 
 
1884
  g_assert_true(queue->length == 0);
 
 
1885
  g_assert_true(queue->next_run == 0);
 
 
1888
static task_func dummy_func;
 
 
1890
static void test_add_to_queue(__attribute__((unused))
 
 
1891
                              test_fixture *fixture,
 
 
1892
                              __attribute__((unused))
 
 
1893
                              gconstpointer user_data){
 
 
1894
  __attribute__((cleanup(cleanup_queue)))
 
 
1895
    task_queue *queue = create_queue();
 
 
1896
  g_assert_nonnull(queue);
 
 
1898
  g_assert_true(add_to_queue(queue,
 
 
1899
                             (task_context){ .func=dummy_func }));
 
 
1900
  g_assert_true(queue->length == 1);
 
 
1901
  g_assert_nonnull(queue->tasks);
 
 
1902
  g_assert_true(queue->tasks[0].func == dummy_func);
 
 
1905
static void test_add_to_queue_overflow(__attribute__((unused))
 
 
1906
                                       test_fixture *fixture,
 
 
1907
                                       __attribute__((unused))
 
 
1908
                                       gconstpointer user_data){
 
 
1909
  __attribute__((cleanup(cleanup_queue)))
 
 
1910
    task_queue *queue = create_queue();
 
 
1911
  g_assert_nonnull(queue);
 
 
1912
  g_assert_true(queue->length == 0);
 
 
1913
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
 
1915
  FILE *real_stderr = stderr;
 
 
1916
  FILE *devnull = fopen("/dev/null", "we");
 
 
1917
  g_assert_nonnull(devnull);
 
 
1919
  const bool ret = add_to_queue(queue,
 
 
1920
                                (task_context){ .func=dummy_func });
 
 
1921
  g_assert_true(errno == ENOMEM);
 
 
1922
  g_assert_false(ret);
 
 
1923
  stderr = real_stderr;
 
 
1924
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
1925
  queue->length = 0;            /* Restore real size */
 
 
1928
static void dummy_func(__attribute__((unused))
 
 
1929
                       const task_context task,
 
 
1930
                       __attribute__((unused))
 
 
1931
                       task_queue *const queue){
 
 
1934
static void test_queue_has_question_empty(__attribute__((unused))
 
 
1935
                                          test_fixture *fixture,
 
 
1936
                                          __attribute__((unused))
 
 
1937
                                          gconstpointer user_data){
 
 
1938
  __attribute__((cleanup(cleanup_queue)))
 
 
1939
    task_queue *queue = create_queue();
 
 
1940
  g_assert_nonnull(queue);
 
 
1941
  g_assert_false(queue_has_question(queue));
 
 
1944
static void test_queue_has_question_false(__attribute__((unused))
 
 
1945
                                          test_fixture *fixture,
 
 
1946
                                          __attribute__((unused))
 
 
1947
                                          gconstpointer user_data){
 
 
1948
  __attribute__((cleanup(cleanup_queue)))
 
 
1949
    task_queue *queue = create_queue();
 
 
1950
  g_assert_nonnull(queue);
 
 
1951
  g_assert_true(add_to_queue(queue,
 
 
1952
                             (task_context){ .func=dummy_func }));
 
 
1953
  g_assert_false(queue_has_question(queue));
 
 
1956
static void test_queue_has_question_true(__attribute__((unused))
 
 
1957
                                         test_fixture *fixture,
 
 
1958
                                         __attribute__((unused))
 
 
1959
                                         gconstpointer user_data){
 
 
1960
  __attribute__((cleanup(cleanup_queue)))
 
 
1961
    task_queue *queue = create_queue();
 
 
1962
  g_assert_nonnull(queue);
 
 
1963
  char *const question_filename
 
 
1964
    = strdup("/nonexistent/question_filename");
 
 
1965
  g_assert_nonnull(question_filename);
 
 
1966
  task_context task = {
 
 
1968
    .question_filename=question_filename,
 
 
1970
  g_assert_true(add_to_queue(queue, task));
 
 
1971
  g_assert_true(queue_has_question(queue));
 
 
1974
static void test_queue_has_question_false2(__attribute__((unused))
 
 
1975
                                           test_fixture *fixture,
 
 
1976
                                           __attribute__((unused))
 
 
1977
                                           gconstpointer user_data){
 
 
1978
  __attribute__((cleanup(cleanup_queue)))
 
 
1979
    task_queue *queue = create_queue();
 
 
1980
  g_assert_nonnull(queue);
 
 
1981
  task_context task = { .func=dummy_func };
 
 
1982
  g_assert_true(add_to_queue(queue, task));
 
 
1983
  g_assert_true(add_to_queue(queue, task));
 
 
1984
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
1985
  g_assert_false(queue_has_question(queue));
 
 
1988
static void test_queue_has_question_true2(__attribute__((unused))
 
 
1989
                                          test_fixture *fixture,
 
 
1990
                                          __attribute__((unused))
 
 
1991
                                          gconstpointer user_data){
 
 
1992
  __attribute__((cleanup(cleanup_queue)))
 
 
1993
    task_queue *queue = create_queue();
 
 
1994
  g_assert_nonnull(queue);
 
 
1995
  task_context task1 = { .func=dummy_func };
 
 
1996
  g_assert_true(add_to_queue(queue, task1));
 
 
1997
  char *const question_filename
 
 
1998
    = strdup("/nonexistent/question_filename");
 
 
1999
  g_assert_nonnull(question_filename);
 
 
2000
  task_context task2 = {
 
 
2002
    .question_filename=question_filename,
 
 
2004
  g_assert_true(add_to_queue(queue, task2));
 
 
2005
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
2006
  g_assert_true(queue_has_question(queue));
 
 
2009
static void test_cleanup_buffer(__attribute__((unused))
 
 
2010
                                test_fixture *fixture,
 
 
2011
                                __attribute__((unused))
 
 
2012
                                gconstpointer user_data){
 
 
2015
  const size_t buffersize = 10;
 
 
2017
  buf.data = malloc(buffersize);
 
 
2018
  g_assert_nonnull(buf.data);
 
 
2019
  if(mlock(buf.data, buffersize) != 0){
 
 
2020
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
2023
  cleanup_buffer(&buf);
 
 
2024
  g_assert_null(buf.data);
 
 
2028
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
 
2029
                                              test_fixture *fixture,
 
 
2030
                                              __attribute__((unused))
 
 
2033
  __attribute__((cleanup(string_set_clear)))
 
 
2034
    string_set set = {};
 
 
2035
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
 
2036
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2040
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
 
2041
                                              test_fixture *fixture,
 
 
2042
                                              __attribute__((unused))
 
 
2045
  __attribute__((cleanup(string_set_clear)))
 
 
2046
    string_set set = {};
 
 
2047
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2048
  g_assert_true(string_set_contains(set, "test_string"));
 
 
2052
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
 
2053
                                             test_fixture *fixture,
 
 
2054
                                             __attribute__((unused))
 
 
2055
                                             gconstpointer user_data){
 
 
2056
  __attribute__((cleanup(string_set_clear)))
 
 
2057
    string_set set = {};
 
 
2058
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2059
  string_set_clear(&set);
 
 
2060
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2064
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
 
2065
                                         test_fixture *fixture,
 
 
2066
                                         __attribute__((unused))
 
 
2067
                                         gconstpointer user_data){
 
 
2068
  __attribute__((cleanup(string_set_clear)))
 
 
2069
    string_set set1 = {};
 
 
2070
  __attribute__((cleanup(string_set_clear)))
 
 
2071
    string_set set2 = {};
 
 
2072
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2073
  string_set_swap(&set1, &set2);
 
 
2074
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2075
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2079
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
 
2080
                                         test_fixture *fixture,
 
 
2081
                                         __attribute__((unused))
 
 
2082
                                         gconstpointer user_data){
 
 
2083
  __attribute__((cleanup(string_set_clear)))
 
 
2084
    string_set set1 = {};
 
 
2085
  __attribute__((cleanup(string_set_clear)))
 
 
2086
    string_set set2 = {};
 
 
2087
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2088
  string_set_swap(&set1, &set2);
 
 
2089
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2090
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2093
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
 
2094
                                              test_fixture *fixture,
 
 
2095
                                              __attribute__((unused))
 
 
2098
  __attribute__((cleanup(string_set_clear)))
 
 
2099
    string_set set1 = {};
 
 
2100
  __attribute__((cleanup(string_set_clear)))
 
 
2101
    string_set set2 = {};
 
 
2102
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2103
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2104
  string_set_swap(&set1, &set2);
 
 
2105
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2106
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2107
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2108
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2111
static bool fd_has_cloexec_and_nonblock(const int);
 
 
2113
static bool epoll_set_contains(int, int, uint32_t);
 
 
2115
static void test_start_mandos_client(test_fixture *fixture,
 
 
2116
                                     __attribute__((unused))
 
 
2117
                                     gconstpointer user_data){
 
 
2119
  bool mandos_client_exited = false;
 
 
2120
  bool quit_now = false;
 
 
2121
  __attribute__((cleanup(cleanup_close)))
 
 
2122
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2123
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2124
  __attribute__((cleanup(cleanup_queue)))
 
 
2125
    task_queue *queue = create_queue();
 
 
2126
  g_assert_nonnull(queue);
 
 
2127
  buffer password = {};
 
 
2128
  bool password_is_read = false;
 
 
2129
  const char helper_directory[] = "/nonexistent";
 
 
2130
  const char *const argv[] = { "/bin/true", NULL };
 
 
2132
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2133
                                    &mandos_client_exited, &quit_now,
 
 
2134
                                    &password, &password_is_read,
 
 
2135
                                    &fixture->orig_sigaction,
 
 
2136
                                    fixture->orig_sigmask,
 
 
2137
                                    helper_directory, 0, 0, argv));
 
 
2139
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
2141
  const task_context *const added_wait_task
 
 
2142
    = find_matching_task(queue, (task_context){
 
 
2143
        .func=wait_for_mandos_client_exit,
 
 
2144
        .mandos_client_exited=&mandos_client_exited,
 
 
2145
        .quit_now=&quit_now,
 
 
2147
  g_assert_nonnull(added_wait_task);
 
 
2148
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
 
2149
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
 
2150
  waitpid(added_wait_task->pid, NULL, 0);
 
 
2152
  const task_context *const added_read_task
 
 
2153
    = find_matching_task(queue, (task_context){
 
 
2154
        .func=read_mandos_client_output,
 
 
2156
        .password=&password,
 
 
2157
        .password_is_read=&password_is_read,
 
 
2158
        .quit_now=&quit_now,
 
 
2160
  g_assert_nonnull(added_read_task);
 
 
2161
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
2162
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
2163
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
 
2164
                                   EPOLLIN | EPOLLRDHUP));
 
 
2167
static bool fd_has_cloexec_and_nonblock(const int fd){
 
 
2168
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
 
2169
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
 
2170
  return ((socket_fd_flags >= 0)
 
 
2171
          and (socket_fd_flags & FD_CLOEXEC)
 
 
2172
          and (socket_file_flags >= 0)
 
 
2173
          and (socket_file_flags & O_NONBLOCK));
 
 
2176
__attribute__((const))
 
 
2177
bool is_privileged(void){
 
 
2178
  uid_t user = getuid() + 1;
 
 
2179
  if(user == 0){                /* Overflow check */
 
 
2182
  gid_t group = getuid() + 1;
 
 
2183
  if(group == 0){               /* Overflow check */
 
 
2186
  const pid_t pid = fork();
 
 
2187
  if(pid == 0){                 /* Child */
 
 
2188
    if(setresgid((uid_t)-1, group, group) == -1){
 
 
2190
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
 
2191
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
2195
    if(setresuid((uid_t)-1, user, user) == -1){
 
 
2197
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
 
2198
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
2205
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
 
2209
  waitpid(pid, &status, 0);
 
 
2210
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
 
2216
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
 
2217
  /* Only scan for events in this eventmask */
 
 
2218
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
 
2219
  __attribute__((cleanup(cleanup_string)))
 
 
2220
    char *fdinfo_name = NULL;
 
 
2221
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
 
2222
  g_assert_cmpint(ret, >, 0);
 
 
2223
  g_assert_nonnull(fdinfo_name);
 
 
2225
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
 
2226
  g_assert_nonnull(fdinfo);
 
 
2227
  uint32_t reported_events;
 
 
2232
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
 
2235
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
 
2236
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
 
2237
              &found_fd, &reported_events) == 2){
 
 
2242
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
 
2243
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
 
2250
    /* Don't check events if none are given */
 
 
2253
  return (reported_events & eventmask) == (events & eventmask);
 
 
2256
static void test_start_mandos_client_execv(test_fixture *fixture,
 
 
2257
                                           __attribute__((unused))
 
 
2258
                                           gconstpointer user_data){
 
 
2259
  bool mandos_client_exited = false;
 
 
2260
  bool quit_now = false;
 
 
2261
  __attribute__((cleanup(cleanup_close)))
 
 
2262
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2263
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2264
  __attribute__((cleanup(cleanup_queue)))
 
 
2265
    task_queue *queue = create_queue();
 
 
2266
  g_assert_nonnull(queue);
 
 
2267
  __attribute__((cleanup(cleanup_buffer)))
 
 
2268
    buffer password = {};
 
 
2269
  const char helper_directory[] = "/nonexistent";
 
 
2270
  /* Can't execv("/", ...), so this should fail */
 
 
2271
  const char *const argv[] = { "/", NULL };
 
 
2274
    __attribute__((cleanup(cleanup_close)))
 
 
2275
      const int devnull_fd = open("/dev/null",
 
 
2276
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2277
    g_assert_cmpint(devnull_fd, >=, 0);
 
 
2278
    __attribute__((cleanup(cleanup_close)))
 
 
2279
      const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2280
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2281
    dup2(devnull_fd, STDERR_FILENO);
 
 
2283
    const bool success = start_mandos_client(queue, epoll_fd,
 
 
2284
                                             &mandos_client_exited,
 
 
2288
                                             &fixture->orig_sigaction,
 
 
2289
                                             fixture->orig_sigmask,
 
 
2290
                                             helper_directory, 0, 0,
 
 
2292
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2293
    g_assert_true(success);
 
 
2295
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
2297
  struct timespec starttime, currtime;
 
 
2298
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2300
    queue->next_run = 0;
 
 
2301
    string_set cancelled_filenames = {};
 
 
2304
      __attribute__((cleanup(cleanup_close)))
 
 
2305
        const int devnull_fd = open("/dev/null",
 
 
2306
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2307
      g_assert_cmpint(devnull_fd, >=, 0);
 
 
2308
      __attribute__((cleanup(cleanup_close)))
 
 
2309
        const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2310
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2311
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2312
      dup2(devnull_fd, STDERR_FILENO);
 
 
2313
      const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2315
      dup2(real_stderr_fd, STDERR_FILENO);
 
 
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_true(quit_now);
 
 
2326
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2327
  g_assert_true(mandos_client_exited);
 
 
2330
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
 
2331
                                               __attribute__((unused))
 
 
2334
  if(not is_privileged()){
 
 
2335
    g_test_skip("Not privileged");
 
 
2339
  bool mandos_client_exited = false;
 
 
2340
  bool quit_now = false;
 
 
2341
  __attribute__((cleanup(cleanup_close)))
 
 
2342
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2343
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2344
  __attribute__((cleanup(cleanup_queue)))
 
 
2345
    task_queue *queue = create_queue();
 
 
2346
  g_assert_nonnull(queue);
 
 
2347
  __attribute__((cleanup(cleanup_buffer)))
 
 
2348
    buffer password = {};
 
 
2349
  bool password_is_read = false;
 
 
2350
  const char helper_directory[] = "/nonexistent";
 
 
2351
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
 
2355
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2356
                                           &mandos_client_exited,
 
 
2357
                                           &quit_now, &password,
 
 
2359
                                           &fixture->orig_sigaction,
 
 
2360
                                           fixture->orig_sigmask,
 
 
2361
                                           helper_directory, user,
 
 
2363
  g_assert_true(success);
 
 
2364
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2366
  struct timespec starttime, currtime;
 
 
2367
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2369
    queue->next_run = 0;
 
 
2370
    string_set cancelled_filenames = {};
 
 
2371
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2372
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2373
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2374
  } while(((queue->length) > 0)
 
 
2376
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2378
  g_assert_false(quit_now);
 
 
2379
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2380
  g_assert_true(mandos_client_exited);
 
 
2382
  g_assert_true(password_is_read);
 
 
2383
  g_assert_nonnull(password.data);
 
 
2386
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2388
  g_assert_true((uid_t)id == id);
 
 
2390
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2393
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
 
2394
                                               __attribute__((unused))
 
 
2397
  if(not is_privileged()){
 
 
2398
    g_test_skip("Not privileged");
 
 
2402
  bool mandos_client_exited = false;
 
 
2403
  bool quit_now = false;
 
 
2404
  __attribute__((cleanup(cleanup_close)))
 
 
2405
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2406
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2407
  __attribute__((cleanup(cleanup_queue)))
 
 
2408
    task_queue *queue = create_queue();
 
 
2409
  g_assert_nonnull(queue);
 
 
2410
  __attribute__((cleanup(cleanup_buffer)))
 
 
2411
    buffer password = {};
 
 
2412
  bool password_is_read = false;
 
 
2413
  const char helper_directory[] = "/nonexistent";
 
 
2414
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
 
2418
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2419
                                           &mandos_client_exited,
 
 
2420
                                           &quit_now, &password,
 
 
2422
                                           &fixture->orig_sigaction,
 
 
2423
                                           fixture->orig_sigmask,
 
 
2424
                                           helper_directory, user,
 
 
2426
  g_assert_true(success);
 
 
2427
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2429
  struct timespec starttime, currtime;
 
 
2430
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2432
    queue->next_run = 0;
 
 
2433
    string_set cancelled_filenames = {};
 
 
2434
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2435
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2436
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2437
  } while(((queue->length) > 0)
 
 
2439
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2441
  g_assert_false(quit_now);
 
 
2442
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2443
  g_assert_true(mandos_client_exited);
 
 
2445
  g_assert_true(password_is_read);
 
 
2446
  g_assert_nonnull(password.data);
 
 
2449
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2451
  g_assert_true((gid_t)id == id);
 
 
2453
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2456
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
 
2457
                                               __attribute__((unused))
 
 
2460
  if(not is_privileged()){
 
 
2461
    g_test_skip("Not privileged");
 
 
2465
  bool mandos_client_exited = false;
 
 
2466
  bool quit_now = false;
 
 
2467
  __attribute__((cleanup(cleanup_close)))
 
 
2468
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2469
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2470
  __attribute__((cleanup(cleanup_queue)))
 
 
2471
    task_queue *queue = create_queue();
 
 
2472
  g_assert_nonnull(queue);
 
 
2473
  __attribute__((cleanup(cleanup_buffer)))
 
 
2474
    buffer password = {};
 
 
2475
  bool password_is_read = false;
 
 
2476
  const char helper_directory[] = "/nonexistent";
 
 
2477
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
 
2482
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2483
                                           &mandos_client_exited,
 
 
2484
                                           &quit_now, &password,
 
 
2486
                                           &fixture->orig_sigaction,
 
 
2487
                                           fixture->orig_sigmask,
 
 
2488
                                           helper_directory, user,
 
 
2490
  g_assert_true(success);
 
 
2491
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2493
  struct timespec starttime, currtime;
 
 
2494
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2496
    queue->next_run = 0;
 
 
2497
    string_set cancelled_filenames = {};
 
 
2498
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2499
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2500
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2501
  } while(((queue->length) > 0)
 
 
2503
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2505
  g_assert_false(quit_now);
 
 
2506
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2507
  g_assert_true(mandos_client_exited);
 
 
2509
  g_assert_true(password_is_read);
 
 
2510
  g_assert_nonnull(password.data);
 
 
2513
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2515
  g_assert_true((uid_t)id == id);
 
 
2517
  g_assert_cmpuint((unsigned int)id, ==, user);
 
 
2520
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
 
2521
                                               __attribute__((unused))
 
 
2524
  if(not is_privileged()){
 
 
2525
    g_test_skip("Not privileged");
 
 
2529
  bool mandos_client_exited = false;
 
 
2530
  bool quit_now = false;
 
 
2531
  __attribute__((cleanup(cleanup_close)))
 
 
2532
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2533
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2534
  __attribute__((cleanup(cleanup_queue)))
 
 
2535
    task_queue *queue = create_queue();
 
 
2536
  g_assert_nonnull(queue);
 
 
2537
  __attribute__((cleanup(cleanup_buffer)))
 
 
2538
    buffer password = {};
 
 
2539
  bool password_is_read = false;
 
 
2540
  const char helper_directory[] = "/nonexistent";
 
 
2541
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
 
2546
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2547
                                           &mandos_client_exited,
 
 
2548
                                           &quit_now, &password,
 
 
2550
                                           &fixture->orig_sigaction,
 
 
2551
                                           fixture->orig_sigmask,
 
 
2552
                                           helper_directory, user,
 
 
2554
  g_assert_true(success);
 
 
2555
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2557
  struct timespec starttime, currtime;
 
 
2558
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2560
    queue->next_run = 0;
 
 
2561
    string_set cancelled_filenames = {};
 
 
2562
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2563
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2564
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2565
  } while(((queue->length) > 0)
 
 
2567
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2569
  g_assert_false(quit_now);
 
 
2570
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2571
  g_assert_true(mandos_client_exited);
 
 
2573
  g_assert_true(password_is_read);
 
 
2574
  g_assert_nonnull(password.data);
 
 
2577
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2579
  g_assert_true((gid_t)id == id);
 
 
2581
  g_assert_cmpuint((unsigned int)id, ==, group);
 
 
2584
static void test_start_mandos_client_read(test_fixture *fixture,
 
 
2585
                                          __attribute__((unused))
 
 
2586
                                          gconstpointer user_data){
 
 
2587
  bool mandos_client_exited = false;
 
 
2588
  bool quit_now = false;
 
 
2589
  __attribute__((cleanup(cleanup_close)))
 
 
2590
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2591
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2592
  __attribute__((cleanup(cleanup_queue)))
 
 
2593
    task_queue *queue = create_queue();
 
 
2594
  g_assert_nonnull(queue);
 
 
2595
  __attribute__((cleanup(cleanup_buffer)))
 
 
2596
    buffer password = {};
 
 
2597
  bool password_is_read = false;
 
 
2598
  const char dummy_test_password[] = "dummy test password";
 
 
2599
  const char helper_directory[] = "/nonexistent";
 
 
2600
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
 
2603
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2604
                                           &mandos_client_exited,
 
 
2605
                                           &quit_now, &password,
 
 
2607
                                           &fixture->orig_sigaction,
 
 
2608
                                           fixture->orig_sigmask,
 
 
2609
                                           helper_directory, 0, 0,
 
 
2611
  g_assert_true(success);
 
 
2612
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2614
  struct timespec starttime, currtime;
 
 
2615
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2617
    queue->next_run = 0;
 
 
2618
    string_set cancelled_filenames = {};
 
 
2619
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2620
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2621
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2622
  } while(((queue->length) > 0)
 
 
2624
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2626
  g_assert_false(quit_now);
 
 
2627
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2628
  g_assert_true(mandos_client_exited);
 
 
2630
  g_assert_true(password_is_read);
 
 
2631
  g_assert_cmpint((int)password.length, ==,
 
 
2632
                  sizeof(dummy_test_password)-1);
 
 
2633
  g_assert_nonnull(password.data);
 
 
2634
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
 
2635
                         sizeof(dummy_test_password)-1), ==, 0);
 
 
2639
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
 
2640
                                               __attribute__((unused))
 
 
2643
  bool mandos_client_exited = false;
 
 
2644
  bool quit_now = false;
 
 
2645
  __attribute__((cleanup(cleanup_close)))
 
 
2646
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2647
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2648
  __attribute__((cleanup(cleanup_queue)))
 
 
2649
    task_queue *queue = create_queue();
 
 
2650
  g_assert_nonnull(queue);
 
 
2651
  __attribute__((cleanup(cleanup_buffer)))
 
 
2652
    buffer password = {};
 
 
2653
  bool password_is_read = false;
 
 
2654
  const char helper_directory[] = "/nonexistent";
 
 
2655
  const char *const argv[] = { "/bin/sh", "-c",
 
 
2656
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
 
2658
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2659
                                           &mandos_client_exited,
 
 
2660
                                           &quit_now, &password,
 
 
2662
                                           &fixture->orig_sigaction,
 
 
2663
                                           fixture->orig_sigmask,
 
 
2664
                                           helper_directory, 0, 0,
 
 
2666
  g_assert_true(success);
 
 
2667
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2669
  struct timespec starttime, currtime;
 
 
2670
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2672
    queue->next_run = 0;
 
 
2673
    string_set cancelled_filenames = {};
 
 
2674
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2675
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2676
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2677
  } while(((queue->length) > 0)
 
 
2679
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2681
  g_assert_false(quit_now);
 
 
2682
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2683
  g_assert_true(mandos_client_exited);
 
 
2685
  g_assert_true(password_is_read);
 
 
2686
  g_assert_cmpint((int)password.length, ==,
 
 
2687
                  sizeof(helper_directory)-1);
 
 
2688
  g_assert_nonnull(password.data);
 
 
2689
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
 
2690
                         sizeof(helper_directory)-1), ==, 0);
 
 
2693
__attribute__((nonnull, warn_unused_result))
 
 
2694
static bool proc_status_sigblk_to_sigset(const char *const,
 
 
2697
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
 
2698
                                             __attribute__((unused))
 
 
2699
                                             gconstpointer user_data){
 
 
2700
  bool mandos_client_exited = false;
 
 
2701
  bool quit_now = false;
 
 
2702
  __attribute__((cleanup(cleanup_close)))
 
 
2703
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2704
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2705
  __attribute__((cleanup(cleanup_queue)))
 
 
2706
    task_queue *queue = create_queue();
 
 
2707
  g_assert_nonnull(queue);
 
 
2708
  __attribute__((cleanup(cleanup_buffer)))
 
 
2709
    buffer password = {};
 
 
2710
  bool password_is_read = false;
 
 
2711
  const char helper_directory[] = "/nonexistent";
 
 
2712
  /* see proc(5) for format of /proc/self/status */
 
 
2713
  const char *const argv[] = { "/usr/bin/awk",
 
 
2714
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
 
2716
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2717
                                    &mandos_client_exited, &quit_now,
 
 
2718
                                    &password, &password_is_read,
 
 
2719
                                    &fixture->orig_sigaction,
 
 
2720
                                    fixture->orig_sigmask,
 
 
2721
                                    helper_directory, 0, 0, argv));
 
 
2723
  struct timespec starttime, currtime;
 
 
2724
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2726
    queue->next_run = 0;
 
 
2727
    string_set cancelled_filenames = {};
 
 
2728
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2729
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2730
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2731
  } while((not (mandos_client_exited and password_is_read))
 
 
2733
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2734
  g_assert_true(mandos_client_exited);
 
 
2735
  g_assert_true(password_is_read);
 
 
2737
  sigset_t parsed_sigmask;
 
 
2738
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
 
2741
  for(int signum = 1; signum < NSIG; signum++){
 
 
2742
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
 
2743
    if(sigismember(&fixture->orig_sigmask, signum)){
 
 
2744
      g_assert_true(has_signal);
 
 
2746
      g_assert_false(has_signal);
 
 
2751
__attribute__((nonnull, warn_unused_result))
 
 
2752
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
 
2753
                                         sigset_t *const sigmask){
 
 
2754
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
 
2755
  uintmax_t scanned_sigmask;
 
 
2756
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
 
2759
  if(sigemptyset(sigmask) != 0){
 
 
2762
  for(int signum = 1; signum < NSIG; signum++){
 
 
2763
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
 
2764
      if(sigaddset(sigmask, signum) != 0){
 
 
2772
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2773
                                             task_queue *const queue);
 
 
2776
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
 
2777
                                             test_fixture *fixture,
 
 
2778
                                             __attribute__((unused))
 
 
2779
                                             gconstpointer user_data){
 
 
2781
  bool mandos_client_exited = false;
 
 
2782
  bool quit_now = false;
 
 
2784
  __attribute__((cleanup(cleanup_queue)))
 
 
2785
    task_queue *queue = create_queue();
 
 
2786
  g_assert_nonnull(queue);
 
 
2787
  const task_context task = {
 
 
2788
    .func=wait_for_mandos_client_exit,
 
 
2790
    .mandos_client_exited=&mandos_client_exited,
 
 
2791
    .quit_now=&quit_now,
 
 
2793
  run_task_with_stderr_to_dev_null(task, queue);
 
 
2795
  g_assert_false(mandos_client_exited);
 
 
2796
  g_assert_true(quit_now);
 
 
2797
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2800
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2801
                                             task_queue *const queue){
 
 
2802
  FILE *real_stderr = stderr;
 
 
2803
  FILE *devnull = fopen("/dev/null", "we");
 
 
2804
  g_assert_nonnull(devnull);
 
 
2807
  task.func(task, queue);
 
 
2808
  stderr = real_stderr;
 
 
2810
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
2814
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
 
2815
                                             __attribute__((unused))
 
 
2816
                                             gconstpointer user_data){
 
 
2817
  bool mandos_client_exited = false;
 
 
2818
  bool quit_now = false;
 
 
2820
  pid_t create_eternal_process(void){
 
 
2821
    const pid_t pid = fork();
 
 
2822
    if(pid == 0){               /* Child */
 
 
2823
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2824
        _exit(EXIT_FAILURE);
 
 
2826
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2827
        _exit(EXIT_FAILURE);
 
 
2835
  pid_t pid = create_eternal_process();
 
 
2836
  g_assert_true(pid != -1);
 
 
2838
  __attribute__((cleanup(cleanup_queue)))
 
 
2839
    task_queue *queue = create_queue();
 
 
2840
  g_assert_nonnull(queue);
 
 
2841
  const task_context task = {
 
 
2842
    .func=wait_for_mandos_client_exit,
 
 
2844
    .mandos_client_exited=&mandos_client_exited,
 
 
2845
    .quit_now=&quit_now,
 
 
2847
  task.func(task, queue);
 
 
2849
  g_assert_false(mandos_client_exited);
 
 
2850
  g_assert_false(quit_now);
 
 
2851
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
2853
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
2854
        .func=wait_for_mandos_client_exit,
 
 
2856
        .mandos_client_exited=&mandos_client_exited,
 
 
2857
        .quit_now=&quit_now,
 
 
2862
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
 
2863
                                              __attribute__((unused))
 
 
2866
  bool mandos_client_exited = false;
 
 
2867
  bool quit_now = false;
 
 
2869
  pid_t create_successful_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_successful_process();
 
 
2883
  g_assert_true(pid != -1);
 
 
2885
  __attribute__((cleanup(cleanup_queue)))
 
 
2886
    task_queue *queue = create_queue();
 
 
2887
  g_assert_nonnull(queue);
 
 
2888
  const task_context initial_task = {
 
 
2889
    .func=wait_for_mandos_client_exit,
 
 
2891
    .mandos_client_exited=&mandos_client_exited,
 
 
2892
    .quit_now=&quit_now,
 
 
2894
  g_assert_true(add_to_queue(queue, initial_task));
 
 
2896
  struct timespec starttime, currtime;
 
 
2897
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2898
  __attribute__((cleanup(cleanup_close)))
 
 
2899
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2901
    queue->next_run = 0;
 
 
2902
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2903
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
 
2904
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2905
  } while((not mandos_client_exited)
 
 
2907
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2909
  g_assert_true(mandos_client_exited);
 
 
2910
  g_assert_false(quit_now);
 
 
2911
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2915
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
 
2916
                                              __attribute__((unused))
 
 
2919
  bool mandos_client_exited = false;
 
 
2920
  bool quit_now = false;
 
 
2922
  pid_t create_failing_process(void){
 
 
2923
    const pid_t pid = fork();
 
 
2924
    if(pid == 0){               /* Child */
 
 
2925
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2926
        _exit(EXIT_FAILURE);
 
 
2928
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2929
        _exit(EXIT_FAILURE);
 
 
2935
  const pid_t pid = create_failing_process();
 
 
2936
  g_assert_true(pid != -1);
 
 
2938
  __attribute__((cleanup(string_set_clear)))
 
 
2939
    string_set cancelled_filenames = {};
 
 
2940
  __attribute__((cleanup(cleanup_close)))
 
 
2941
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2942
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2943
  __attribute__((cleanup(cleanup_queue)))
 
 
2944
    task_queue *queue = create_queue();
 
 
2945
  g_assert_nonnull(queue);
 
 
2946
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2947
        .func=wait_for_mandos_client_exit,
 
 
2949
        .mandos_client_exited=&mandos_client_exited,
 
 
2950
        .quit_now=&quit_now,
 
 
2953
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
 
2955
  __attribute__((cleanup(cleanup_close)))
 
 
2956
    const int devnull_fd = open("/dev/null",
 
 
2957
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2958
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2959
  __attribute__((cleanup(cleanup_close)))
 
 
2960
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2961
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2963
  struct timespec starttime, currtime;
 
 
2964
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2966
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2967
    dup2(devnull_fd, STDERR_FILENO);
 
 
2968
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2970
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2975
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2976
  } while((not mandos_client_exited)
 
 
2978
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2980
  g_assert_true(quit_now);
 
 
2981
  g_assert_true(mandos_client_exited);
 
 
2982
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2986
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
 
2987
                                             __attribute__((unused))
 
 
2988
                                             gconstpointer user_data){
 
 
2989
  bool mandos_client_exited = false;
 
 
2990
  bool quit_now = false;
 
 
2992
  pid_t create_killed_process(void){
 
 
2993
    const pid_t pid = fork();
 
 
2994
    if(pid == 0){               /* Child */
 
 
2995
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2996
        _exit(EXIT_FAILURE);
 
 
2998
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2999
        _exit(EXIT_FAILURE);
 
 
3008
  const pid_t pid = create_killed_process();
 
 
3009
  g_assert_true(pid != -1);
 
 
3011
  __attribute__((cleanup(string_set_clear)))
 
 
3012
    string_set cancelled_filenames = {};
 
 
3013
  __attribute__((cleanup(cleanup_close)))
 
 
3014
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3015
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3016
  __attribute__((cleanup(cleanup_queue)))
 
 
3017
    task_queue *queue = create_queue();
 
 
3018
  g_assert_nonnull(queue);
 
 
3019
  g_assert_true(add_to_queue(queue, (task_context){
 
 
3020
        .func=wait_for_mandos_client_exit,
 
 
3022
        .mandos_client_exited=&mandos_client_exited,
 
 
3023
        .quit_now=&quit_now,
 
 
3026
  __attribute__((cleanup(cleanup_close)))
 
 
3027
    const int devnull_fd = open("/dev/null",
 
 
3028
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
 
3029
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
3030
  __attribute__((cleanup(cleanup_close)))
 
 
3031
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
3032
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
3034
  struct timespec starttime, currtime;
 
 
3035
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
3037
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
3038
    dup2(devnull_fd, STDERR_FILENO);
 
 
3039
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
3041
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
3046
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
3047
  } while((not mandos_client_exited)
 
 
3049
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
3051
  g_assert_true(mandos_client_exited);
 
 
3052
  g_assert_true(quit_now);
 
 
3053
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3056
static bool epoll_set_does_not_contain(int, int);
 
 
3059
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
 
3060
                                              test_fixture *fixture,
 
 
3061
                                              __attribute__((unused))
 
 
3064
  __attribute__((cleanup(cleanup_close)))
 
 
3065
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3066
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3068
  __attribute__((cleanup(cleanup_buffer)))
 
 
3069
    buffer password = {};
 
 
3071
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
 
3072
  const int fd = open("/proc/self/mem",
 
 
3073
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3075
  bool password_is_read = false;
 
 
3076
  bool quit_now = false;
 
 
3077
  __attribute__((cleanup(cleanup_queue)))
 
 
3078
    task_queue *queue = create_queue();
 
 
3079
  g_assert_nonnull(queue);
 
 
3081
  task_context task = {
 
 
3082
    .func=read_mandos_client_output,
 
 
3085
    .password=&password,
 
 
3086
    .password_is_read=&password_is_read,
 
 
3087
    .quit_now=&quit_now,
 
 
3089
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3090
  g_assert_false(password_is_read);
 
 
3091
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3092
  g_assert_true(quit_now);
 
 
3093
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3095
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3097
  g_assert_cmpint(close(fd), ==, -1);
 
 
3100
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
 
3101
  return not epoll_set_contains(epoll_fd, fd, 0);
 
 
3105
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
 
3106
                                           test_fixture *fixture,
 
 
3107
                                           __attribute__((unused))
 
 
3108
                                           gconstpointer user_data){
 
 
3109
  __attribute__((cleanup(cleanup_close)))
 
 
3110
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3111
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3114
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3116
  __attribute__((cleanup(cleanup_buffer)))
 
 
3117
    buffer password = {};
 
 
3119
  bool password_is_read = false;
 
 
3120
  bool quit_now = false;
 
 
3121
  __attribute__((cleanup(cleanup_queue)))
 
 
3122
    task_queue *queue = create_queue();
 
 
3123
  g_assert_nonnull(queue);
 
 
3125
  task_context task = {
 
 
3126
    .func=read_mandos_client_output,
 
 
3129
    .password=&password,
 
 
3130
    .password_is_read=&password_is_read,
 
 
3131
    .quit_now=&quit_now,
 
 
3133
  task.func(task, queue);
 
 
3134
  g_assert_false(password_is_read);
 
 
3135
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3136
  g_assert_false(quit_now);
 
 
3137
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3139
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3140
        .func=read_mandos_client_output,
 
 
3143
        .password=&password,
 
 
3144
        .password_is_read=&password_is_read,
 
 
3145
        .quit_now=&quit_now,
 
 
3148
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3149
                                   EPOLLIN | EPOLLRDHUP));
 
 
3151
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3154
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
 
3155
                                               test_fixture *fixture,
 
 
3156
                                               __attribute__((unused))
 
 
3159
  __attribute__((cleanup(cleanup_close)))
 
 
3160
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3161
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3164
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3165
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3167
  __attribute__((cleanup(cleanup_buffer)))
 
 
3168
    buffer password = {};
 
 
3170
  bool password_is_read = false;
 
 
3171
  bool quit_now = false;
 
 
3172
  __attribute__((cleanup(cleanup_queue)))
 
 
3173
    task_queue *queue = create_queue();
 
 
3174
  g_assert_nonnull(queue);
 
 
3176
  task_context task = {
 
 
3177
    .func=read_mandos_client_output,
 
 
3180
    .password=&password,
 
 
3181
    .password_is_read=&password_is_read,
 
 
3182
    .quit_now=&quit_now,
 
 
3184
  task.func(task, queue);
 
 
3185
  g_assert_true(password_is_read);
 
 
3186
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3187
  g_assert_false(quit_now);
 
 
3188
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3190
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3192
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3196
void test_read_mandos_client_output_once(__attribute__((unused))
 
 
3197
                                         test_fixture *fixture,
 
 
3198
                                         __attribute__((unused))
 
 
3199
                                         gconstpointer user_data){
 
 
3200
  __attribute__((cleanup(cleanup_close)))
 
 
3201
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3202
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3205
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3207
  const char dummy_test_password[] = "dummy test password";
 
 
3208
  /* Start with a pre-allocated buffer */
 
 
3209
  __attribute__((cleanup(cleanup_buffer)))
 
 
3211
    .data=malloc(sizeof(dummy_test_password)),
 
 
3213
    .allocated=sizeof(dummy_test_password),
 
 
3215
  g_assert_nonnull(password.data);
 
 
3216
  if(mlock(password.data, password.allocated) != 0){
 
 
3217
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3220
  bool password_is_read = false;
 
 
3221
  bool quit_now = false;
 
 
3222
  __attribute__((cleanup(cleanup_queue)))
 
 
3223
    task_queue *queue = create_queue();
 
 
3224
  g_assert_nonnull(queue);
 
 
3226
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3227
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3228
                             sizeof(dummy_test_password)),
 
 
3229
                  ==, (int)sizeof(dummy_test_password));
 
 
3231
  task_context task = {
 
 
3232
    .func=read_mandos_client_output,
 
 
3235
    .password=&password,
 
 
3236
    .password_is_read=&password_is_read,
 
 
3237
    .quit_now=&quit_now,
 
 
3239
  task.func(task, queue);
 
 
3241
  g_assert_false(password_is_read);
 
 
3242
  g_assert_cmpint((int)password.length, ==,
 
 
3243
                  (int)sizeof(dummy_test_password));
 
 
3244
  g_assert_nonnull(password.data);
 
 
3245
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3246
                         sizeof(dummy_test_password)), ==, 0);
 
 
3248
  g_assert_false(quit_now);
 
 
3249
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3251
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3252
        .func=read_mandos_client_output,
 
 
3255
        .password=&password,
 
 
3256
        .password_is_read=&password_is_read,
 
 
3257
        .quit_now=&quit_now,
 
 
3260
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3261
                                   EPOLLIN | EPOLLRDHUP));
 
 
3263
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3267
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
 
3268
                                           test_fixture *fixture,
 
 
3269
                                           __attribute__((unused))
 
 
3270
                                           gconstpointer user_data){
 
 
3271
  __attribute__((cleanup(cleanup_close)))
 
 
3272
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3273
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3276
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3278
  const char dummy_test_password[] = "dummy test password";
 
 
3279
  /* Start with an empty buffer */
 
 
3280
  __attribute__((cleanup(cleanup_buffer)))
 
 
3281
    buffer password = {};
 
 
3283
  bool password_is_read = false;
 
 
3284
  bool quit_now = false;
 
 
3285
  __attribute__((cleanup(cleanup_queue)))
 
 
3286
    task_queue *queue = create_queue();
 
 
3287
  g_assert_nonnull(queue);
 
 
3289
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3290
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3291
                             sizeof(dummy_test_password)),
 
 
3292
                  ==, (int)sizeof(dummy_test_password));
 
 
3294
  task_context task = {
 
 
3295
    .func=read_mandos_client_output,
 
 
3298
    .password=&password,
 
 
3299
    .password_is_read=&password_is_read,
 
 
3300
    .quit_now=&quit_now,
 
 
3302
  task.func(task, queue);
 
 
3304
  g_assert_false(password_is_read);
 
 
3305
  g_assert_cmpint((int)password.length, ==,
 
 
3306
                  (int)sizeof(dummy_test_password));
 
 
3307
  g_assert_nonnull(password.data);
 
 
3308
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3309
                         sizeof(dummy_test_password)), ==, 0);
 
 
3311
  g_assert_false(quit_now);
 
 
3312
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3314
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3315
        .func=read_mandos_client_output,
 
 
3318
        .password=&password,
 
 
3319
        .password_is_read=&password_is_read,
 
 
3320
        .quit_now=&quit_now,
 
 
3323
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3324
                                   EPOLLIN | EPOLLRDHUP));
 
 
3326
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3330
void test_read_mandos_client_output_append(__attribute__((unused))
 
 
3331
                                           test_fixture *fixture,
 
 
3332
                                           __attribute__((unused))
 
 
3333
                                           gconstpointer user_data){
 
 
3334
  __attribute__((cleanup(cleanup_close)))
 
 
3335
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3336
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3339
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3341
  const char dummy_test_password[] = "dummy test password";
 
 
3342
  __attribute__((cleanup(cleanup_buffer)))
 
 
3344
    .data=malloc(PIPE_BUF),
 
 
3346
    .allocated=PIPE_BUF,
 
 
3348
  g_assert_nonnull(password.data);
 
 
3349
  if(mlock(password.data, password.allocated) != 0){
 
 
3350
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3353
  memset(password.data, 'x', PIPE_BUF);
 
 
3354
  char password_expected[PIPE_BUF];
 
 
3355
  memcpy(password_expected, password.data, PIPE_BUF);
 
 
3357
  bool password_is_read = false;
 
 
3358
  bool quit_now = false;
 
 
3359
  __attribute__((cleanup(cleanup_queue)))
 
 
3360
    task_queue *queue = create_queue();
 
 
3361
  g_assert_nonnull(queue);
 
 
3363
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3364
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3365
                             sizeof(dummy_test_password)),
 
 
3366
                  ==, (int)sizeof(dummy_test_password));
 
 
3368
  task_context task = {
 
 
3369
    .func=read_mandos_client_output,
 
 
3372
    .password=&password,
 
 
3373
    .password_is_read=&password_is_read,
 
 
3374
    .quit_now=&quit_now,
 
 
3376
  task.func(task, queue);
 
 
3378
  g_assert_false(password_is_read);
 
 
3379
  g_assert_cmpint((int)password.length, ==,
 
 
3380
                  PIPE_BUF + sizeof(dummy_test_password));
 
 
3381
  g_assert_nonnull(password.data);
 
 
3382
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
 
3384
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
 
3385
                         dummy_test_password,
 
 
3386
                         sizeof(dummy_test_password)), ==, 0);
 
 
3387
  g_assert_false(quit_now);
 
 
3388
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3390
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3391
        .func=read_mandos_client_output,
 
 
3394
        .password=&password,
 
 
3395
        .password_is_read=&password_is_read,
 
 
3396
        .quit_now=&quit_now,
 
 
3399
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3400
                                   EPOLLIN | EPOLLRDHUP));
 
 
3403
static char *make_temporary_directory(void);
 
 
3405
static void test_add_inotify_dir_watch(__attribute__((unused))
 
 
3406
                                       test_fixture *fixture,
 
 
3407
                                       __attribute__((unused))
 
 
3408
                                       gconstpointer user_data){
 
 
3409
  __attribute__((cleanup(cleanup_close)))
 
 
3410
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3411
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3412
  __attribute__((cleanup(cleanup_queue)))
 
 
3413
    task_queue *queue = create_queue();
 
 
3414
  g_assert_nonnull(queue);
 
 
3415
  __attribute__((cleanup(string_set_clear)))
 
 
3416
    string_set cancelled_filenames = {};
 
 
3417
  const mono_microsecs current_time = 0;
 
 
3419
  bool quit_now = false;
 
 
3420
  buffer password = {};
 
 
3421
  bool mandos_client_exited = false;
 
 
3422
  bool password_is_read = false;
 
 
3424
  __attribute__((cleanup(cleanup_string)))
 
 
3425
    char *tempdir = make_temporary_directory();
 
 
3426
  g_assert_nonnull(tempdir);
 
 
3428
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3430
                                      &cancelled_filenames,
 
 
3432
                                      &mandos_client_exited,
 
 
3433
                                      &password_is_read));
 
 
3435
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3437
  const task_context *const added_read_task
 
 
3438
    = find_matching_task(queue, (task_context){
 
 
3439
        .func=read_inotify_event,
 
 
3441
        .quit_now=&quit_now,
 
 
3442
        .password=&password,
 
 
3444
        .cancelled_filenames=&cancelled_filenames,
 
 
3445
        .current_time=¤t_time,
 
 
3446
        .mandos_client_exited=&mandos_client_exited,
 
 
3447
        .password_is_read=&password_is_read,
 
 
3449
  g_assert_nonnull(added_read_task);
 
 
3451
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3452
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3453
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
 
3454
                                   added_read_task->fd,
 
 
3455
                                   EPOLLIN | EPOLLRDHUP));
 
 
3457
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3460
static char *make_temporary_directory(void){
 
 
3461
  char *name = strdup("/tmp/mandosXXXXXX");
 
 
3462
  g_assert_nonnull(name);
 
 
3463
  char *result = mkdtemp(name);
 
 
3470
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
 
3471
                                            test_fixture *fixture,
 
 
3472
                                            __attribute__((unused))
 
 
3473
                                            gconstpointer user_data){
 
 
3474
  __attribute__((cleanup(cleanup_close)))
 
 
3475
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3476
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3477
  __attribute__((cleanup(cleanup_queue)))
 
 
3478
    task_queue *queue = create_queue();
 
 
3479
  g_assert_nonnull(queue);
 
 
3480
  __attribute__((cleanup(string_set_clear)))
 
 
3481
    string_set cancelled_filenames = {};
 
 
3482
  const mono_microsecs current_time = 0;
 
 
3484
  bool quit_now = false;
 
 
3485
  buffer password = {};
 
 
3486
  bool mandos_client_exited = false;
 
 
3487
  bool password_is_read = false;
 
 
3489
  const char nonexistent_dir[] = "/nonexistent";
 
 
3491
  FILE *real_stderr = stderr;
 
 
3492
  FILE *devnull = fopen("/dev/null", "we");
 
 
3493
  g_assert_nonnull(devnull);
 
 
3495
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3496
                                       &password, nonexistent_dir,
 
 
3497
                                       &cancelled_filenames,
 
 
3499
                                       &mandos_client_exited,
 
 
3500
                                       &password_is_read));
 
 
3501
  stderr = real_stderr;
 
 
3502
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3504
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3507
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
 
3508
                                              test_fixture *fixture,
 
 
3509
                                            __attribute__((unused))
 
 
3512
  __attribute__((cleanup(cleanup_close)))
 
 
3513
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3514
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3515
  __attribute__((cleanup(cleanup_queue)))
 
 
3516
    task_queue *queue = create_queue();
 
 
3517
  g_assert_nonnull(queue);
 
 
3518
  __attribute__((cleanup(string_set_clear)))
 
 
3519
    string_set cancelled_filenames = {};
 
 
3520
  const mono_microsecs current_time = 0;
 
 
3522
  bool quit_now = false;
 
 
3523
  buffer password = {};
 
 
3524
  bool mandos_client_exited = false;
 
 
3525
  bool password_is_read = false;
 
 
3527
  const char not_a_directory[] = "/dev/tty";
 
 
3529
  FILE *real_stderr = stderr;
 
 
3530
  FILE *devnull = fopen("/dev/null", "we");
 
 
3531
  g_assert_nonnull(devnull);
 
 
3533
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3534
                                       &password, not_a_directory,
 
 
3535
                                       &cancelled_filenames,
 
 
3537
                                       &mandos_client_exited,
 
 
3538
                                       &password_is_read));
 
 
3539
  stderr = real_stderr;
 
 
3540
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3542
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3545
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
 
3546
                                              test_fixture *fixture,
 
 
3547
                                              __attribute__((unused))
 
 
3550
  __attribute__((cleanup(cleanup_close)))
 
 
3551
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3552
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3553
  __attribute__((cleanup(cleanup_queue)))
 
 
3554
    task_queue *queue = create_queue();
 
 
3555
  g_assert_nonnull(queue);
 
 
3556
  __attribute__((cleanup(string_set_clear)))
 
 
3557
    string_set cancelled_filenames = {};
 
 
3558
  const mono_microsecs current_time = 0;
 
 
3560
  bool quit_now = false;
 
 
3561
  buffer password = {};
 
 
3562
  bool mandos_client_exited = false;
 
 
3563
  bool password_is_read = false;
 
 
3565
  __attribute__((cleanup(cleanup_string)))
 
 
3566
    char *tempdir = make_temporary_directory();
 
 
3567
  g_assert_nonnull(tempdir);
 
 
3569
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3571
                                      &cancelled_filenames,
 
 
3573
                                      &mandos_client_exited,
 
 
3574
                                      &password_is_read));
 
 
3576
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3578
  const task_context *const added_read_task
 
 
3579
    = find_matching_task(queue,
 
 
3580
                         (task_context){ .func=read_inotify_event });
 
 
3581
  g_assert_nonnull(added_read_task);
 
 
3583
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3584
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3586
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3587
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3589
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3590
  g_assert_nonnull(ievent);
 
 
3592
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
 
3594
  g_assert_cmpint(errno, ==, EAGAIN);
 
 
3598
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3601
static char *make_temporary_file_in_directory(const char
 
 
3605
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3606
                                               test_fixture *fixture,
 
 
3607
                                               __attribute__((unused))
 
 
3610
  __attribute__((cleanup(cleanup_close)))
 
 
3611
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3612
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3613
  __attribute__((cleanup(cleanup_queue)))
 
 
3614
    task_queue *queue = create_queue();
 
 
3615
  g_assert_nonnull(queue);
 
 
3616
  __attribute__((cleanup(string_set_clear)))
 
 
3617
    string_set cancelled_filenames = {};
 
 
3618
  const mono_microsecs current_time = 0;
 
 
3620
  bool quit_now = false;
 
 
3621
  buffer password = {};
 
 
3622
  bool mandos_client_exited = false;
 
 
3623
  bool password_is_read = false;
 
 
3625
  __attribute__((cleanup(cleanup_string)))
 
 
3626
    char *tempdir = make_temporary_directory();
 
 
3627
  g_assert_nonnull(tempdir);
 
 
3629
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3631
                                      &cancelled_filenames,
 
 
3633
                                      &mandos_client_exited,
 
 
3634
                                      &password_is_read));
 
 
3636
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3638
  const task_context *const added_read_task
 
 
3639
    = find_matching_task(queue,
 
 
3640
                         (task_context){ .func=read_inotify_event });
 
 
3641
  g_assert_nonnull(added_read_task);
 
 
3643
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3644
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3646
  __attribute__((cleanup(cleanup_string)))
 
 
3647
    char *filename = make_temporary_file_in_directory(tempdir);
 
 
3648
  g_assert_nonnull(filename);
 
 
3650
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3651
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3653
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3654
  g_assert_nonnull(ievent);
 
 
3656
  ssize_t read_size = 0;
 
 
3657
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3659
  g_assert_cmpint((int)read_size, >, 0);
 
 
3660
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
 
3661
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
 
3665
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
3666
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3669
static char *make_temporary_prefixed_file_in_directory(const char
 
 
3673
  char *filename = NULL;
 
 
3674
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
 
3676
  g_assert_nonnull(filename);
 
 
3677
  const int fd = mkostemp(filename, O_CLOEXEC);
 
 
3678
  g_assert_cmpint(fd, >=, 0);
 
 
3679
  g_assert_cmpint(close(fd), ==, 0);
 
 
3683
static char *make_temporary_file_in_directory(const char
 
 
3685
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
 
3689
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
 
3690
                                            test_fixture *fixture,
 
 
3691
                                            __attribute__((unused))
 
 
3692
                                            gconstpointer user_data){
 
 
3693
  __attribute__((cleanup(cleanup_close)))
 
 
3694
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3695
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3696
  __attribute__((cleanup(cleanup_queue)))
 
 
3697
    task_queue *queue = create_queue();
 
 
3698
  g_assert_nonnull(queue);
 
 
3699
  __attribute__((cleanup(string_set_clear)))
 
 
3700
    string_set cancelled_filenames = {};
 
 
3701
  const mono_microsecs current_time = 0;
 
 
3703
  bool quit_now = false;
 
 
3704
  buffer password = {};
 
 
3705
  bool mandos_client_exited = false;
 
 
3706
  bool password_is_read = false;
 
 
3708
  __attribute__((cleanup(cleanup_string)))
 
 
3709
    char *watchdir = make_temporary_directory();
 
 
3710
  g_assert_nonnull(watchdir);
 
 
3712
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3713
                                      &password, watchdir,
 
 
3714
                                      &cancelled_filenames,
 
 
3716
                                      &mandos_client_exited,
 
 
3717
                                      &password_is_read));
 
 
3719
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3721
  const task_context *const added_read_task
 
 
3722
    = find_matching_task(queue,
 
 
3723
                         (task_context){ .func=read_inotify_event });
 
 
3724
  g_assert_nonnull(added_read_task);
 
 
3726
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3727
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3729
  char *sourcedir = make_temporary_directory();
 
 
3730
  g_assert_nonnull(sourcedir);
 
 
3732
  __attribute__((cleanup(cleanup_string)))
 
 
3733
    char *filename = make_temporary_file_in_directory(sourcedir);
 
 
3734
  g_assert_nonnull(filename);
 
 
3736
  __attribute__((cleanup(cleanup_string)))
 
 
3737
    char *targetfilename = NULL;
 
 
3738
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
 
3739
                           basename(filename)), >, 0);
 
 
3740
  g_assert_nonnull(targetfilename);
 
 
3742
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
 
3743
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
 
3746
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3747
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3749
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3750
  g_assert_nonnull(ievent);
 
 
3752
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3754
  g_assert_cmpint((int)read_size, >, 0);
 
 
3755
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
 
3756
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
 
3760
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3761
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
 
3765
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
 
3766
                                              test_fixture *fixture,
 
 
3767
                                              __attribute__((unused))
 
 
3770
  __attribute__((cleanup(cleanup_close)))
 
 
3771
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3772
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3773
  __attribute__((cleanup(cleanup_queue)))
 
 
3774
    task_queue *queue = create_queue();
 
 
3775
  g_assert_nonnull(queue);
 
 
3776
  __attribute__((cleanup(string_set_clear)))
 
 
3777
    string_set cancelled_filenames = {};
 
 
3778
  const mono_microsecs current_time = 0;
 
 
3780
  bool quit_now = false;
 
 
3781
  buffer password = {};
 
 
3782
  bool mandos_client_exited = false;
 
 
3783
  bool password_is_read = false;
 
 
3785
  __attribute__((cleanup(cleanup_string)))
 
 
3786
    char *tempdir = make_temporary_directory();
 
 
3787
  g_assert_nonnull(tempdir);
 
 
3789
  __attribute__((cleanup(cleanup_string)))
 
 
3790
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
 
3791
  g_assert_nonnull(tempfilename);
 
 
3793
  __attribute__((cleanup(cleanup_string)))
 
 
3794
    char *targetdir = make_temporary_directory();
 
 
3795
  g_assert_nonnull(targetdir);
 
 
3797
  __attribute__((cleanup(cleanup_string)))
 
 
3798
    char *targetfilename = NULL;
 
 
3799
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
 
3800
                           basename(tempfilename)), >, 0);
 
 
3801
  g_assert_nonnull(targetfilename);
 
 
3803
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3805
                                      &cancelled_filenames,
 
 
3807
                                      &mandos_client_exited,
 
 
3808
                                      &password_is_read));
 
 
3810
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
 
3812
  const task_context *const added_read_task
 
 
3813
    = find_matching_task(queue,
 
 
3814
                         (task_context){ .func=read_inotify_event });
 
 
3815
  g_assert_nonnull(added_read_task);
 
 
3817
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3818
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3820
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3821
  g_assert_nonnull(ievent);
 
 
3823
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3825
  g_assert_cmpint((int)read_size, >, 0);
 
 
3826
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
 
3827
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
 
3831
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3832
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
 
3833
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3837
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
 
3838
                                          test_fixture *fixture,
 
 
3839
                                          __attribute__((unused))
 
 
3840
                                          gconstpointer user_data){
 
 
3841
  __attribute__((cleanup(cleanup_close)))
 
 
3842
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3843
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3844
  __attribute__((cleanup(cleanup_queue)))
 
 
3845
    task_queue *queue = create_queue();
 
 
3846
  g_assert_nonnull(queue);
 
 
3847
  __attribute__((cleanup(string_set_clear)))
 
 
3848
    string_set cancelled_filenames = {};
 
 
3849
  const mono_microsecs current_time = 0;
 
 
3851
  bool quit_now = false;
 
 
3852
  buffer password = {};
 
 
3853
  bool mandos_client_exited = false;
 
 
3854
  bool password_is_read = false;
 
 
3856
  __attribute__((cleanup(cleanup_string)))
 
 
3857
    char *tempdir = make_temporary_directory();
 
 
3858
  g_assert_nonnull(tempdir);
 
 
3860
  __attribute__((cleanup(cleanup_string)))
 
 
3861
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3862
  g_assert_nonnull(tempfile);
 
 
3864
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3866
                                      &cancelled_filenames,
 
 
3868
                                      &mandos_client_exited,
 
 
3869
                                      &password_is_read));
 
 
3870
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3872
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3874
  const task_context *const added_read_task
 
 
3875
    = find_matching_task(queue,
 
 
3876
                         (task_context){ .func=read_inotify_event });
 
 
3877
  g_assert_nonnull(added_read_task);
 
 
3879
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3880
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3882
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3883
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3885
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3886
  g_assert_nonnull(ievent);
 
 
3888
  ssize_t read_size = 0;
 
 
3889
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3891
  g_assert_cmpint((int)read_size, >, 0);
 
 
3892
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3893
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3897
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3901
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
 
3902
                                               test_fixture *fixture,
 
 
3903
                                               __attribute__((unused))
 
 
3906
  __attribute__((cleanup(cleanup_close)))
 
 
3907
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3908
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3909
  __attribute__((cleanup(cleanup_queue)))
 
 
3910
    task_queue *queue = create_queue();
 
 
3911
  g_assert_nonnull(queue);
 
 
3912
  __attribute__((cleanup(string_set_clear)))
 
 
3913
    string_set cancelled_filenames = {};
 
 
3914
  const mono_microsecs current_time = 0;
 
 
3916
  bool quit_now = false;
 
 
3917
  buffer password = {};
 
 
3918
  bool mandos_client_exited = false;
 
 
3919
  bool password_is_read = false;
 
 
3921
  __attribute__((cleanup(cleanup_string)))
 
 
3922
    char *tempdir = make_temporary_directory();
 
 
3923
  g_assert_nonnull(tempdir);
 
 
3925
  __attribute__((cleanup(cleanup_string)))
 
 
3926
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3927
  g_assert_nonnull(tempfile);
 
 
3928
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
 
3930
  g_assert_cmpint(tempfile_fd, >, 2);
 
 
3932
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3934
                                      &cancelled_filenames,
 
 
3936
                                      &mandos_client_exited,
 
 
3937
                                      &password_is_read));
 
 
3938
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3940
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3942
  const task_context *const added_read_task
 
 
3943
    = find_matching_task(queue,
 
 
3944
                         (task_context){ .func=read_inotify_event });
 
 
3945
  g_assert_nonnull(added_read_task);
 
 
3947
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3948
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3950
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3951
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3953
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3954
  g_assert_nonnull(ievent);
 
 
3956
  ssize_t read_size = 0;
 
 
3957
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3959
  g_assert_cmpint((int)read_size, >, 0);
 
 
3960
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3961
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3963
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
 
3965
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
 
3966
     file not appear as an ievent, so we should not see it now. */
 
 
3967
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3968
  g_assert_cmpint((int)read_size, ==, -1);
 
 
3969
  g_assert_true(errno == EAGAIN);
 
 
3973
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3976
static void test_read_inotify_event_readerror(__attribute__((unused))
 
 
3977
                                              test_fixture *fixture,
 
 
3978
                                              __attribute__((unused))
 
 
3981
  __attribute__((cleanup(cleanup_close)))
 
 
3982
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3983
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3984
  const mono_microsecs current_time = 0;
 
 
3986
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
 
3987
  const int fd = open("/proc/self/mem",
 
 
3988
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3990
  bool quit_now = false;
 
 
3991
  __attribute__((cleanup(cleanup_queue)))
 
 
3992
    task_queue *queue = create_queue();
 
 
3993
  g_assert_nonnull(queue);
 
 
3995
  task_context task = {
 
 
3996
    .func=read_inotify_event,
 
 
3999
    .quit_now=&quit_now,
 
 
4000
    .filename=strdup("/nonexistent"),
 
 
4001
    .cancelled_filenames = &(string_set){},
 
 
4003
    .current_time=¤t_time,
 
 
4005
  g_assert_nonnull(task.filename);
 
 
4006
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4007
  g_assert_true(quit_now);
 
 
4008
  g_assert_true(queue->next_run == 0);
 
 
4009
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4011
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
4013
  g_assert_cmpint(close(fd), ==, -1);
 
 
4016
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
 
4017
                                              test_fixture *fixture,
 
 
4018
                                              __attribute__((unused))
 
 
4021
  const mono_microsecs current_time = 17;
 
 
4024
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4025
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
 
4027
  bool quit_now = false;
 
 
4028
  buffer password = {};
 
 
4029
  bool mandos_client_exited = false;
 
 
4030
  bool password_is_read = false;
 
 
4031
  __attribute__((cleanup(cleanup_queue)))
 
 
4032
    task_queue *queue = create_queue();
 
 
4033
  g_assert_nonnull(queue);
 
 
4035
  task_context task = {
 
 
4036
    .func=read_inotify_event,
 
 
4039
    .quit_now=&quit_now,
 
 
4040
    .password=&password,
 
 
4041
    .filename=strdup("/nonexistent"),
 
 
4042
    .cancelled_filenames = &(string_set){},
 
 
4044
    .current_time=¤t_time,
 
 
4045
    .mandos_client_exited=&mandos_client_exited,
 
 
4046
    .password_is_read=&password_is_read,
 
 
4048
  g_assert_nonnull(task.filename);
 
 
4049
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4051
  g_assert_nonnull(find_matching_task(queue, task));
 
 
4052
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
4054
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
 
4055
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4058
static void test_read_inotify_event_nodata(__attribute__((unused))
 
 
4059
                                           test_fixture *fixture,
 
 
4060
                                           __attribute__((unused))
 
 
4061
                                           gconstpointer user_data){
 
 
4062
  __attribute__((cleanup(cleanup_close)))
 
 
4063
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4064
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4065
  const mono_microsecs current_time = 0;
 
 
4068
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4070
  bool quit_now = false;
 
 
4071
  buffer password = {};
 
 
4072
  bool mandos_client_exited = false;
 
 
4073
  bool password_is_read = false;
 
 
4074
  __attribute__((cleanup(cleanup_queue)))
 
 
4075
    task_queue *queue = create_queue();
 
 
4076
  g_assert_nonnull(queue);
 
 
4078
  task_context task = {
 
 
4079
    .func=read_inotify_event,
 
 
4082
    .quit_now=&quit_now,
 
 
4083
    .password=&password,
 
 
4084
    .filename=strdup("/nonexistent"),
 
 
4085
    .cancelled_filenames = &(string_set){},
 
 
4087
    .current_time=¤t_time,
 
 
4088
    .mandos_client_exited=&mandos_client_exited,
 
 
4089
    .password_is_read=&password_is_read,
 
 
4091
  g_assert_nonnull(task.filename);
 
 
4092
  task.func(task, queue);
 
 
4093
  g_assert_false(quit_now);
 
 
4094
  g_assert_true(queue->next_run == 0);
 
 
4095
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4097
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4098
        .func=read_inotify_event,
 
 
4101
        .quit_now=&quit_now,
 
 
4102
        .password=&password,
 
 
4103
        .filename=task.filename,
 
 
4104
        .cancelled_filenames=task.cancelled_filenames,
 
 
4105
        .current_time=¤t_time,
 
 
4106
        .mandos_client_exited=&mandos_client_exited,
 
 
4107
        .password_is_read=&password_is_read,
 
 
4110
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4111
                                   EPOLLIN | EPOLLRDHUP));
 
 
4113
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4116
static void test_read_inotify_event_eof(__attribute__((unused))
 
 
4117
                                        test_fixture *fixture,
 
 
4118
                                        __attribute__((unused))
 
 
4119
                                        gconstpointer user_data){
 
 
4120
  __attribute__((cleanup(cleanup_close)))
 
 
4121
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4122
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4123
  const mono_microsecs current_time = 0;
 
 
4126
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4127
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4129
  bool quit_now = false;
 
 
4130
  buffer password = {};
 
 
4131
  __attribute__((cleanup(cleanup_queue)))
 
 
4132
    task_queue *queue = create_queue();
 
 
4133
  g_assert_nonnull(queue);
 
 
4135
  task_context task = {
 
 
4136
    .func=read_inotify_event,
 
 
4139
    .quit_now=&quit_now,
 
 
4140
    .password=&password,
 
 
4141
    .filename=strdup("/nonexistent"),
 
 
4142
    .cancelled_filenames = &(string_set){},
 
 
4144
    .current_time=¤t_time,
 
 
4146
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4147
  g_assert_true(quit_now);
 
 
4148
  g_assert_true(queue->next_run == 0);
 
 
4149
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4151
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
4153
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
4157
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
 
4158
                                            test_fixture *fixture,
 
 
4159
                                            __attribute__((unused))
 
 
4160
                                            gconstpointer user_data){
 
 
4161
  __attribute__((cleanup(cleanup_close)))
 
 
4162
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4163
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4164
  const mono_microsecs current_time = 0;
 
 
4167
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4169
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4170
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4172
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4174
    struct inotify_event event;
 
 
4175
    char name_buffer[NAME_MAX + 1];
 
 
4177
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4179
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4180
  ievent->mask = IN_CLOSE_WRITE;
 
 
4181
  ievent->len = sizeof(dummy_file_name);
 
 
4182
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4183
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4184
                              + sizeof(dummy_file_name));
 
 
4185
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4187
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4189
  bool quit_now = false;
 
 
4190
  buffer password = {};
 
 
4191
  bool mandos_client_exited = false;
 
 
4192
  bool password_is_read = false;
 
 
4193
  __attribute__((cleanup(cleanup_queue)))
 
 
4194
    task_queue *queue = create_queue();
 
 
4195
  g_assert_nonnull(queue);
 
 
4197
  task_context task = {
 
 
4198
    .func=read_inotify_event,
 
 
4201
    .quit_now=&quit_now,
 
 
4202
    .password=&password,
 
 
4203
    .filename=strdup("/nonexistent"),
 
 
4204
    .cancelled_filenames = &(string_set){},
 
 
4206
    .current_time=¤t_time,
 
 
4207
    .mandos_client_exited=&mandos_client_exited,
 
 
4208
    .password_is_read=&password_is_read,
 
 
4210
  task.func(task, queue);
 
 
4211
  g_assert_false(quit_now);
 
 
4212
  g_assert_true(queue->next_run != 0);
 
 
4213
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4215
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4216
        .func=read_inotify_event,
 
 
4219
        .quit_now=&quit_now,
 
 
4220
        .password=&password,
 
 
4221
        .filename=task.filename,
 
 
4222
        .cancelled_filenames=task.cancelled_filenames,
 
 
4223
        .current_time=¤t_time,
 
 
4224
        .mandos_client_exited=&mandos_client_exited,
 
 
4225
        .password_is_read=&password_is_read,
 
 
4228
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4229
                                   EPOLLIN | EPOLLRDHUP));
 
 
4231
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4233
  __attribute__((cleanup(cleanup_string)))
 
 
4234
    char *filename = NULL;
 
 
4235
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4236
                           dummy_file_name), >, 0);
 
 
4237
  g_assert_nonnull(filename);
 
 
4238
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4239
        .func=open_and_parse_question,
 
 
4242
        .question_filename=filename,
 
 
4243
        .password=&password,
 
 
4244
        .cancelled_filenames=task.cancelled_filenames,
 
 
4245
        .current_time=¤t_time,
 
 
4246
        .mandos_client_exited=&mandos_client_exited,
 
 
4247
        .password_is_read=&password_is_read,
 
 
4252
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
 
4253
                                         test_fixture *fixture,
 
 
4254
                                         __attribute__((unused))
 
 
4255
                                         gconstpointer user_data){
 
 
4256
  __attribute__((cleanup(cleanup_close)))
 
 
4257
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4258
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4259
  const mono_microsecs current_time = 0;
 
 
4262
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4264
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4265
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4267
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4269
    struct inotify_event event;
 
 
4270
    char name_buffer[NAME_MAX + 1];
 
 
4272
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4274
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4275
  ievent->mask = IN_MOVED_TO;
 
 
4276
  ievent->len = sizeof(dummy_file_name);
 
 
4277
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4278
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4279
                              + sizeof(dummy_file_name));
 
 
4280
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4282
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4284
  bool quit_now = false;
 
 
4285
  buffer password = {};
 
 
4286
  bool mandos_client_exited = false;
 
 
4287
  bool password_is_read = false;
 
 
4288
  __attribute__((cleanup(cleanup_queue)))
 
 
4289
    task_queue *queue = create_queue();
 
 
4290
  g_assert_nonnull(queue);
 
 
4292
  task_context task = {
 
 
4293
    .func=read_inotify_event,
 
 
4296
    .quit_now=&quit_now,
 
 
4297
    .password=&password,
 
 
4298
    .filename=strdup("/nonexistent"),
 
 
4299
    .cancelled_filenames = &(string_set){},
 
 
4301
    .current_time=¤t_time,
 
 
4302
    .mandos_client_exited=&mandos_client_exited,
 
 
4303
    .password_is_read=&password_is_read,
 
 
4305
  task.func(task, queue);
 
 
4306
  g_assert_false(quit_now);
 
 
4307
  g_assert_true(queue->next_run != 0);
 
 
4308
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4310
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4311
        .func=read_inotify_event,
 
 
4314
        .quit_now=&quit_now,
 
 
4315
        .password=&password,
 
 
4316
        .filename=task.filename,
 
 
4317
        .cancelled_filenames=task.cancelled_filenames,
 
 
4318
        .current_time=¤t_time,
 
 
4319
        .mandos_client_exited=&mandos_client_exited,
 
 
4320
        .password_is_read=&password_is_read,
 
 
4323
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4324
                                   EPOLLIN | EPOLLRDHUP));
 
 
4326
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4328
  __attribute__((cleanup(cleanup_string)))
 
 
4329
    char *filename = NULL;
 
 
4330
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4331
                           dummy_file_name), >, 0);
 
 
4332
  g_assert_nonnull(filename);
 
 
4333
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4334
        .func=open_and_parse_question,
 
 
4337
        .question_filename=filename,
 
 
4338
        .password=&password,
 
 
4339
        .cancelled_filenames=task.cancelled_filenames,
 
 
4340
        .current_time=¤t_time,
 
 
4341
        .mandos_client_exited=&mandos_client_exited,
 
 
4342
        .password_is_read=&password_is_read,
 
 
4347
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
 
4348
                                           test_fixture *fixture,
 
 
4349
                                           __attribute__((unused))
 
 
4350
                                           gconstpointer user_data){
 
 
4351
  __attribute__((cleanup(cleanup_close)))
 
 
4352
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4353
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4354
  __attribute__((cleanup(string_set_clear)))
 
 
4355
    string_set cancelled_filenames = {};
 
 
4356
  const mono_microsecs current_time = 0;
 
 
4359
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4361
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4362
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4364
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4366
    struct inotify_event event;
 
 
4367
    char name_buffer[NAME_MAX + 1];
 
 
4369
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4371
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4372
  ievent->mask = IN_MOVED_FROM;
 
 
4373
  ievent->len = sizeof(dummy_file_name);
 
 
4374
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4375
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4376
                              + sizeof(dummy_file_name));
 
 
4377
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4379
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4381
  bool quit_now = false;
 
 
4382
  buffer password = {};
 
 
4383
  bool mandos_client_exited = false;
 
 
4384
  bool password_is_read = false;
 
 
4385
  __attribute__((cleanup(cleanup_queue)))
 
 
4386
    task_queue *queue = create_queue();
 
 
4387
  g_assert_nonnull(queue);
 
 
4389
  task_context task = {
 
 
4390
    .func=read_inotify_event,
 
 
4393
    .quit_now=&quit_now,
 
 
4394
    .password=&password,
 
 
4395
    .filename=strdup("/nonexistent"),
 
 
4396
    .cancelled_filenames=&cancelled_filenames,
 
 
4397
    .current_time=¤t_time,
 
 
4398
    .mandos_client_exited=&mandos_client_exited,
 
 
4399
    .password_is_read=&password_is_read,
 
 
4401
  task.func(task, queue);
 
 
4402
  g_assert_false(quit_now);
 
 
4403
  g_assert_true(queue->next_run == 0);
 
 
4404
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4406
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4407
        .func=read_inotify_event,
 
 
4410
        .quit_now=&quit_now,
 
 
4411
        .password=&password,
 
 
4412
        .filename=task.filename,
 
 
4413
        .cancelled_filenames=&cancelled_filenames,
 
 
4414
        .current_time=¤t_time,
 
 
4415
        .mandos_client_exited=&mandos_client_exited,
 
 
4416
        .password_is_read=&password_is_read,
 
 
4419
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4420
                                   EPOLLIN | EPOLLRDHUP));
 
 
4422
  __attribute__((cleanup(cleanup_string)))
 
 
4423
    char *filename = NULL;
 
 
4424
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4425
                           dummy_file_name), >, 0);
 
 
4426
  g_assert_nonnull(filename);
 
 
4427
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4431
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
 
4432
                                              test_fixture *fixture,
 
 
4433
                                              __attribute__((unused))
 
 
4436
  __attribute__((cleanup(cleanup_close)))
 
 
4437
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4438
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4439
  __attribute__((cleanup(string_set_clear)))
 
 
4440
    string_set cancelled_filenames = {};
 
 
4441
  const mono_microsecs current_time = 0;
 
 
4444
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4446
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4447
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4449
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4451
    struct inotify_event event;
 
 
4452
    char name_buffer[NAME_MAX + 1];
 
 
4454
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4456
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4457
  ievent->mask = IN_DELETE;
 
 
4458
  ievent->len = sizeof(dummy_file_name);
 
 
4459
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4460
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4461
                              + sizeof(dummy_file_name));
 
 
4462
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4464
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4466
  bool quit_now = false;
 
 
4467
  buffer password = {};
 
 
4468
  bool mandos_client_exited = false;
 
 
4469
  bool password_is_read = false;
 
 
4470
  __attribute__((cleanup(cleanup_queue)))
 
 
4471
    task_queue *queue = create_queue();
 
 
4472
  g_assert_nonnull(queue);
 
 
4474
  task_context task = {
 
 
4475
    .func=read_inotify_event,
 
 
4478
    .quit_now=&quit_now,
 
 
4479
    .password=&password,
 
 
4480
    .filename=strdup("/nonexistent"),
 
 
4481
    .cancelled_filenames=&cancelled_filenames,
 
 
4482
    .current_time=¤t_time,
 
 
4483
    .mandos_client_exited=&mandos_client_exited,
 
 
4484
    .password_is_read=&password_is_read,
 
 
4486
  task.func(task, queue);
 
 
4487
  g_assert_false(quit_now);
 
 
4488
  g_assert_true(queue->next_run == 0);
 
 
4489
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4491
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4492
        .func=read_inotify_event,
 
 
4495
        .quit_now=&quit_now,
 
 
4496
        .password=&password,
 
 
4497
        .filename=task.filename,
 
 
4498
        .cancelled_filenames=&cancelled_filenames,
 
 
4499
        .current_time=¤t_time,
 
 
4500
        .mandos_client_exited=&mandos_client_exited,
 
 
4501
        .password_is_read=&password_is_read,
 
 
4504
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4505
                                   EPOLLIN | EPOLLRDHUP));
 
 
4507
  __attribute__((cleanup(cleanup_string)))
 
 
4508
    char *filename = NULL;
 
 
4509
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4510
                           dummy_file_name), >, 0);
 
 
4511
  g_assert_nonnull(filename);
 
 
4512
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4517
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
 
4518
                                               test_fixture *fixture,
 
 
4519
                                               __attribute__((unused))
 
 
4522
  __attribute__((cleanup(cleanup_close)))
 
 
4523
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4524
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4525
  const mono_microsecs current_time = 0;
 
 
4528
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4530
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4531
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4533
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4535
    struct inotify_event event;
 
 
4536
    char name_buffer[NAME_MAX + 1];
 
 
4538
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4540
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4541
  ievent->mask = IN_CLOSE_WRITE;
 
 
4542
  ievent->len = sizeof(dummy_file_name);
 
 
4543
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4544
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4545
                              + sizeof(dummy_file_name));
 
 
4546
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4548
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4550
  bool quit_now = false;
 
 
4551
  buffer password = {};
 
 
4552
  bool mandos_client_exited = false;
 
 
4553
  bool password_is_read = false;
 
 
4554
  __attribute__((cleanup(cleanup_queue)))
 
 
4555
    task_queue *queue = create_queue();
 
 
4556
  g_assert_nonnull(queue);
 
 
4558
  task_context task = {
 
 
4559
    .func=read_inotify_event,
 
 
4562
    .quit_now=&quit_now,
 
 
4563
    .password=&password,
 
 
4564
    .filename=strdup("/nonexistent"),
 
 
4565
    .cancelled_filenames = &(string_set){},
 
 
4567
    .current_time=¤t_time,
 
 
4568
    .mandos_client_exited=&mandos_client_exited,
 
 
4569
    .password_is_read=&password_is_read,
 
 
4571
  task.func(task, queue);
 
 
4572
  g_assert_false(quit_now);
 
 
4573
  g_assert_true(queue->next_run == 0);
 
 
4574
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4576
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4577
        .func=read_inotify_event,
 
 
4580
        .quit_now=&quit_now,
 
 
4581
        .password=&password,
 
 
4582
        .filename=task.filename,
 
 
4583
        .cancelled_filenames=task.cancelled_filenames,
 
 
4584
        .current_time=¤t_time,
 
 
4585
        .mandos_client_exited=&mandos_client_exited,
 
 
4586
        .password_is_read=&password_is_read,
 
 
4589
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4590
                                   EPOLLIN | EPOLLRDHUP));
 
 
4594
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
 
4595
                                            test_fixture *fixture,
 
 
4596
                                            __attribute__((unused))
 
 
4597
                                            gconstpointer user_data){
 
 
4598
  __attribute__((cleanup(cleanup_close)))
 
 
4599
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4600
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4601
  const mono_microsecs current_time = 0;
 
 
4604
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4606
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4607
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4609
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4611
    struct inotify_event event;
 
 
4612
    char name_buffer[NAME_MAX + 1];
 
 
4614
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4616
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4617
  ievent->mask = IN_MOVED_TO;
 
 
4618
  ievent->len = sizeof(dummy_file_name);
 
 
4619
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4620
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4621
                              + sizeof(dummy_file_name));
 
 
4622
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4624
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4626
  bool quit_now = false;
 
 
4627
  buffer password = {};
 
 
4628
  bool mandos_client_exited = false;
 
 
4629
  bool password_is_read = false;
 
 
4630
  __attribute__((cleanup(cleanup_queue)))
 
 
4631
    task_queue *queue = create_queue();
 
 
4632
  g_assert_nonnull(queue);
 
 
4634
  task_context task = {
 
 
4635
    .func=read_inotify_event,
 
 
4638
    .quit_now=&quit_now,
 
 
4639
    .password=&password,
 
 
4640
    .filename=strdup("/nonexistent"),
 
 
4641
    .cancelled_filenames = &(string_set){},
 
 
4643
    .current_time=¤t_time,
 
 
4644
    .mandos_client_exited=&mandos_client_exited,
 
 
4645
    .password_is_read=&password_is_read,
 
 
4647
  task.func(task, queue);
 
 
4648
  g_assert_false(quit_now);
 
 
4649
  g_assert_true(queue->next_run == 0);
 
 
4650
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4652
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4653
        .func=read_inotify_event,
 
 
4656
        .quit_now=&quit_now,
 
 
4657
        .password=&password,
 
 
4658
        .filename=task.filename,
 
 
4659
        .cancelled_filenames=task.cancelled_filenames,
 
 
4660
        .current_time=¤t_time,
 
 
4661
        .mandos_client_exited=&mandos_client_exited,
 
 
4662
        .password_is_read=&password_is_read,
 
 
4665
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4666
                                   EPOLLIN | EPOLLRDHUP));
 
 
4670
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
 
4671
                                              test_fixture *fixture,
 
 
4672
                                              __attribute__((unused))
 
 
4675
  __attribute__((cleanup(cleanup_close)))
 
 
4676
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4677
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4678
  __attribute__((cleanup(string_set_clear)))
 
 
4679
    string_set cancelled_filenames = {};
 
 
4680
  const mono_microsecs current_time = 0;
 
 
4683
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4685
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4686
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4688
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4690
    struct inotify_event event;
 
 
4691
    char name_buffer[NAME_MAX + 1];
 
 
4693
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4695
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4696
  ievent->mask = IN_MOVED_FROM;
 
 
4697
  ievent->len = sizeof(dummy_file_name);
 
 
4698
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4699
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4700
                              + sizeof(dummy_file_name));
 
 
4701
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4703
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4705
  bool quit_now = false;
 
 
4706
  buffer password = {};
 
 
4707
  bool mandos_client_exited = false;
 
 
4708
  bool password_is_read = false;
 
 
4709
  __attribute__((cleanup(cleanup_queue)))
 
 
4710
    task_queue *queue = create_queue();
 
 
4711
  g_assert_nonnull(queue);
 
 
4713
  task_context task = {
 
 
4714
    .func=read_inotify_event,
 
 
4717
    .quit_now=&quit_now,
 
 
4718
    .password=&password,
 
 
4719
    .filename=strdup("/nonexistent"),
 
 
4720
    .cancelled_filenames=&cancelled_filenames,
 
 
4721
    .current_time=¤t_time,
 
 
4722
    .mandos_client_exited=&mandos_client_exited,
 
 
4723
    .password_is_read=&password_is_read,
 
 
4725
  task.func(task, queue);
 
 
4726
  g_assert_false(quit_now);
 
 
4727
  g_assert_true(queue->next_run == 0);
 
 
4728
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4730
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4731
        .func=read_inotify_event,
 
 
4734
        .quit_now=&quit_now,
 
 
4735
        .password=&password,
 
 
4736
        .filename=task.filename,
 
 
4737
        .cancelled_filenames=&cancelled_filenames,
 
 
4738
        .current_time=¤t_time,
 
 
4739
        .mandos_client_exited=&mandos_client_exited,
 
 
4740
        .password_is_read=&password_is_read,
 
 
4743
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4744
                                   EPOLLIN | EPOLLRDHUP));
 
 
4746
  __attribute__((cleanup(cleanup_string)))
 
 
4747
    char *filename = NULL;
 
 
4748
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4749
                           dummy_file_name), >, 0);
 
 
4750
  g_assert_nonnull(filename);
 
 
4751
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4755
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
 
4756
                                               test_fixture *fixture,
 
 
4757
                                               __attribute__((unused))
 
 
4760
  __attribute__((cleanup(cleanup_close)))
 
 
4761
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4762
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4763
  __attribute__((cleanup(string_set_clear)))
 
 
4764
    string_set cancelled_filenames = {};
 
 
4765
  const mono_microsecs current_time = 0;
 
 
4768
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4770
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4771
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4773
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4775
    struct inotify_event event;
 
 
4776
    char name_buffer[NAME_MAX + 1];
 
 
4778
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4780
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4781
  ievent->mask = IN_DELETE;
 
 
4782
  ievent->len = sizeof(dummy_file_name);
 
 
4783
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4784
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4785
                              + sizeof(dummy_file_name));
 
 
4786
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4788
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4790
  bool quit_now = false;
 
 
4791
  buffer password = {};
 
 
4792
  bool mandos_client_exited = false;
 
 
4793
  bool password_is_read = false;
 
 
4794
  __attribute__((cleanup(cleanup_queue)))
 
 
4795
    task_queue *queue = create_queue();
 
 
4796
  g_assert_nonnull(queue);
 
 
4798
  task_context task = {
 
 
4799
    .func=read_inotify_event,
 
 
4802
    .quit_now=&quit_now,
 
 
4803
    .password=&password,
 
 
4804
    .filename=strdup("/nonexistent"),
 
 
4805
    .cancelled_filenames=&cancelled_filenames,
 
 
4806
    .current_time=¤t_time,
 
 
4807
    .mandos_client_exited=&mandos_client_exited,
 
 
4808
    .password_is_read=&password_is_read,
 
 
4810
  task.func(task, queue);
 
 
4811
  g_assert_false(quit_now);
 
 
4812
  g_assert_true(queue->next_run == 0);
 
 
4813
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4815
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4816
        .func=read_inotify_event,
 
 
4819
        .quit_now=&quit_now,
 
 
4820
        .password=&password,
 
 
4821
        .filename=task.filename,
 
 
4822
        .cancelled_filenames=&cancelled_filenames,
 
 
4823
        .current_time=¤t_time,
 
 
4824
        .mandos_client_exited=&mandos_client_exited,
 
 
4825
        .password_is_read=&password_is_read,
 
 
4828
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4829
                                   EPOLLIN | EPOLLRDHUP));
 
 
4831
  __attribute__((cleanup(cleanup_string)))
 
 
4832
    char *filename = NULL;
 
 
4833
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4834
                           dummy_file_name), >, 0);
 
 
4835
  g_assert_nonnull(filename);
 
 
4836
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4840
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
 
4841
                                         test_fixture *fixture,
 
 
4842
                                         __attribute__((unused))
 
 
4843
                                         gconstpointer user_data){
 
 
4844
  __attribute__((cleanup(cleanup_close)))
 
 
4845
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4846
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4847
  __attribute__((cleanup(string_set_clear)))
 
 
4848
    string_set cancelled_filenames = {};
 
 
4849
  bool mandos_client_exited = false;
 
 
4850
  bool password_is_read = false;
 
 
4851
  __attribute__((cleanup(cleanup_queue)))
 
 
4852
    task_queue *queue = create_queue();
 
 
4853
  g_assert_nonnull(queue);
 
 
4855
  char *const filename = strdup("/nonexistent");
 
 
4856
  g_assert_nonnull(filename);
 
 
4857
  task_context task = {
 
 
4858
    .func=open_and_parse_question,
 
 
4859
    .question_filename=filename,
 
 
4861
    .password=(buffer[]){{}},
 
 
4863
    .cancelled_filenames=&cancelled_filenames,
 
 
4864
    .current_time=(mono_microsecs[]){0},
 
 
4865
    .mandos_client_exited=&mandos_client_exited,
 
 
4866
    .password_is_read=&password_is_read,
 
 
4868
  task.func(task, queue);
 
 
4869
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4872
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
 
4873
                                             test_fixture *fixture,
 
 
4874
                                             __attribute__((unused))
 
 
4875
                                             gconstpointer user_data){
 
 
4876
  __attribute__((cleanup(cleanup_close)))
 
 
4877
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4878
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4879
  __attribute__((cleanup(string_set_clear)))
 
 
4880
    string_set cancelled_filenames = {};
 
 
4881
  buffer password = {};
 
 
4882
  bool mandos_client_exited = false;
 
 
4883
  bool password_is_read = false;
 
 
4884
  __attribute__((cleanup(cleanup_queue)))
 
 
4885
    task_queue *queue = create_queue();
 
 
4886
  g_assert_nonnull(queue);
 
 
4887
  const mono_microsecs current_time = 0;
 
 
4889
  char *filename = strdup("/proc/self/mem");
 
 
4890
  task_context task = {
 
 
4891
    .func=open_and_parse_question,
 
 
4892
    .question_filename=filename,
 
 
4894
    .password=&password,
 
 
4896
    .cancelled_filenames=&cancelled_filenames,
 
 
4897
    .current_time=¤t_time,
 
 
4898
    .mandos_client_exited=&mandos_client_exited,
 
 
4899
    .password_is_read=&password_is_read,
 
 
4901
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4902
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4906
test_open_and_parse_question_parse_error(__attribute__((unused))
 
 
4907
                                         test_fixture *fixture,
 
 
4908
                                         __attribute__((unused))
 
 
4909
                                         gconstpointer user_data){
 
 
4910
  __attribute__((cleanup(cleanup_close)))
 
 
4911
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4912
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4913
  __attribute__((cleanup(string_set_clear)))
 
 
4914
    string_set cancelled_filenames = {};
 
 
4915
  __attribute__((cleanup(cleanup_queue)))
 
 
4916
    task_queue *queue = create_queue();
 
 
4917
  g_assert_nonnull(queue);
 
 
4919
  __attribute__((cleanup(cleanup_string)))
 
 
4920
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4921
  g_assert_nonnull(tempfilename);
 
 
4922
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4923
  g_assert_cmpint(tempfile, >, 0);
 
 
4924
  const char bad_data[] = "this is bad syntax\n";
 
 
4925
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
 
4926
                  ==, sizeof(bad_data));
 
 
4927
  g_assert_cmpint(close(tempfile), ==, 0);
 
 
4929
  char *const filename = strdup(tempfilename);
 
 
4930
  g_assert_nonnull(filename);
 
 
4931
  task_context task = {
 
 
4932
    .func=open_and_parse_question,
 
 
4933
    .question_filename=filename,
 
 
4935
    .password=(buffer[]){{}},
 
 
4937
    .cancelled_filenames=&cancelled_filenames,
 
 
4938
    .current_time=(mono_microsecs[]){0},
 
 
4939
    .mandos_client_exited=(bool[]){false},
 
 
4940
    .password_is_read=(bool[]){false},
 
 
4942
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4944
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4946
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4950
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
 
4951
                                           test_fixture *fixture,
 
 
4952
                                           __attribute__((unused))
 
 
4953
                                           gconstpointer user_data){
 
 
4954
  __attribute__((cleanup(cleanup_close)))
 
 
4955
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4956
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4957
  __attribute__((cleanup(string_set_clear)))
 
 
4958
    string_set cancelled_filenames = {};
 
 
4959
  __attribute__((cleanup(cleanup_queue)))
 
 
4960
    task_queue *queue = create_queue();
 
 
4961
  g_assert_nonnull(queue);
 
 
4963
  __attribute__((cleanup(cleanup_string)))
 
 
4964
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
4965
  g_assert_nonnull(tempfilename);
 
 
4966
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
4967
  g_assert_cmpint(questionfile, >, 0);
 
 
4968
  FILE *qf = fdopen(questionfile, "w");
 
 
4969
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
 
4970
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
4972
  char *const filename = strdup(tempfilename);
 
 
4973
  g_assert_nonnull(filename);
 
 
4974
  task_context task = {
 
 
4975
    .func=open_and_parse_question,
 
 
4976
    .question_filename=filename,
 
 
4978
    .password=(buffer[]){{}},
 
 
4980
    .cancelled_filenames=&cancelled_filenames,
 
 
4981
    .current_time=(mono_microsecs[]){0},
 
 
4982
    .mandos_client_exited=(bool[]){false},
 
 
4983
    .password_is_read=(bool[]){false},
 
 
4985
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4986
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4988
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
4992
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
 
4993
                                            test_fixture *fixture,
 
 
4994
                                            __attribute__((unused))
 
 
4995
                                            gconstpointer user_data){
 
 
4996
  __attribute__((cleanup(cleanup_close)))
 
 
4997
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4998
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4999
  __attribute__((cleanup(string_set_clear)))
 
 
5000
    string_set cancelled_filenames = {};
 
 
5001
  __attribute__((cleanup(cleanup_queue)))
 
 
5002
    task_queue *queue = create_queue();
 
 
5003
  g_assert_nonnull(queue);
 
 
5005
  __attribute__((cleanup(cleanup_string)))
 
 
5006
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5007
  g_assert_nonnull(tempfilename);
 
 
5008
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5009
  g_assert_cmpint(questionfile, >, 0);
 
 
5010
  FILE *qf = fdopen(questionfile, "w");
 
 
5011
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
 
5012
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5014
  char *const filename = strdup(tempfilename);
 
 
5015
  g_assert_nonnull(filename);
 
 
5016
  task_context task = {
 
 
5017
    .func=open_and_parse_question,
 
 
5018
    .question_filename=filename,
 
 
5020
    .password=(buffer[]){{}},
 
 
5022
    .cancelled_filenames=&cancelled_filenames,
 
 
5023
    .current_time=(mono_microsecs[]){0},
 
 
5024
    .mandos_client_exited=(bool[]){false},
 
 
5025
    .password_is_read=(bool[]){false},
 
 
5027
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5028
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5030
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5034
void test_open_and_parse_question_nopid(__attribute__((unused))
 
 
5035
                                        test_fixture *fixture,
 
 
5036
                                        __attribute__((unused))
 
 
5037
                                        gconstpointer user_data){
 
 
5038
  __attribute__((cleanup(cleanup_close)))
 
 
5039
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5040
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5041
  __attribute__((cleanup(string_set_clear)))
 
 
5042
    string_set cancelled_filenames = {};
 
 
5043
  __attribute__((cleanup(cleanup_queue)))
 
 
5044
    task_queue *queue = create_queue();
 
 
5045
  g_assert_nonnull(queue);
 
 
5047
  __attribute__((cleanup(cleanup_string)))
 
 
5048
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5049
  g_assert_nonnull(tempfilename);
 
 
5050
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5051
  g_assert_cmpint(questionfile, >, 0);
 
 
5052
  FILE *qf = fdopen(questionfile, "w");
 
 
5053
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
 
5054
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5056
  char *const filename = strdup(tempfilename);
 
 
5057
  g_assert_nonnull(filename);
 
 
5058
  task_context task = {
 
 
5059
    .func=open_and_parse_question,
 
 
5060
    .question_filename=filename,
 
 
5062
    .password=(buffer[]){{}},
 
 
5064
    .cancelled_filenames=&cancelled_filenames,
 
 
5065
    .current_time=(mono_microsecs[]){0},
 
 
5066
    .mandos_client_exited=(bool[]){false},
 
 
5067
    .password_is_read=(bool[]){false},
 
 
5069
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5070
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5072
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5076
void test_open_and_parse_question_badpid(__attribute__((unused))
 
 
5077
                                         test_fixture *fixture,
 
 
5078
                                         __attribute__((unused))
 
 
5079
                                         gconstpointer user_data){
 
 
5080
  __attribute__((cleanup(cleanup_close)))
 
 
5081
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5082
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5083
  __attribute__((cleanup(string_set_clear)))
 
 
5084
    string_set cancelled_filenames = {};
 
 
5085
  __attribute__((cleanup(cleanup_queue)))
 
 
5086
    task_queue *queue = create_queue();
 
 
5087
  g_assert_nonnull(queue);
 
 
5089
  __attribute__((cleanup(cleanup_string)))
 
 
5090
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5091
  g_assert_nonnull(tempfilename);
 
 
5092
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5093
  g_assert_cmpint(questionfile, >, 0);
 
 
5094
  FILE *qf = fdopen(questionfile, "w");
 
 
5095
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
 
5097
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5099
  char *const filename = strdup(tempfilename);
 
 
5100
  g_assert_nonnull(filename);
 
 
5101
  task_context task = {
 
 
5102
    .func=open_and_parse_question,
 
 
5103
    .question_filename=filename,
 
 
5105
    .password=(buffer[]){{}},
 
 
5107
    .cancelled_filenames=&cancelled_filenames,
 
 
5108
    .current_time=(mono_microsecs[]){0},
 
 
5109
    .mandos_client_exited=(bool[]){false},
 
 
5110
    .password_is_read=(bool[]){false},
 
 
5112
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5113
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5115
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5119
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
 
5120
                                         test_fixture *fixture,
 
 
5121
                                         __attribute__((unused))
 
 
5122
                                         gconstpointer user_data){
 
 
5123
  __attribute__((cleanup(cleanup_close)))
 
 
5124
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5125
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5126
  __attribute__((cleanup(string_set_clear)))
 
 
5127
    string_set cancelled_filenames = {};
 
 
5128
  buffer password = {};
 
 
5129
  bool mandos_client_exited = false;
 
 
5130
  bool password_is_read = false;
 
 
5131
  __attribute__((cleanup(cleanup_queue)))
 
 
5132
    task_queue *queue = create_queue();
 
 
5133
  g_assert_nonnull(queue);
 
 
5134
  const mono_microsecs current_time = 0;
 
 
5136
  /* Find value of sysctl kernel.pid_max */
 
 
5137
  uintmax_t pid_max = 0;
 
 
5138
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
 
5139
  g_assert_nonnull(sysctl_pid_max);
 
 
5140
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
 
5142
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
 
5144
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
 
5145
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
 
5147
  __attribute__((cleanup(cleanup_string)))
 
 
5148
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5149
  g_assert_nonnull(tempfilename);
 
 
5150
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5151
  g_assert_cmpint(questionfile, >, 0);
 
 
5152
  FILE *qf = fdopen(questionfile, "w");
 
 
5153
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5154
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
 
5156
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5158
  char *const question_filename = strdup(tempfilename);
 
 
5159
  g_assert_nonnull(question_filename);
 
 
5160
  task_context task = {
 
 
5161
    .func=open_and_parse_question,
 
 
5162
    .question_filename=question_filename,
 
 
5164
    .password=&password,
 
 
5165
    .filename=question_filename,
 
 
5166
    .cancelled_filenames=&cancelled_filenames,
 
 
5167
    .current_time=¤t_time,
 
 
5168
    .mandos_client_exited=&mandos_client_exited,
 
 
5169
    .password_is_read=&password_is_read,
 
 
5171
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5172
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5174
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5178
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
 
5179
                                         test_fixture *fixture,
 
 
5180
                                         __attribute__((unused))
 
 
5181
                                         gconstpointer user_data){
 
 
5182
  __attribute__((cleanup(cleanup_close)))
 
 
5183
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5184
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5185
  __attribute__((cleanup(string_set_clear)))
 
 
5186
    string_set cancelled_filenames = {};
 
 
5187
  buffer password = {};
 
 
5188
  bool mandos_client_exited = false;
 
 
5189
  bool password_is_read = false;
 
 
5190
  __attribute__((cleanup(cleanup_queue)))
 
 
5191
    task_queue *queue = create_queue();
 
 
5192
  g_assert_nonnull(queue);
 
 
5193
  const mono_microsecs current_time = 0;
 
 
5195
  __attribute__((cleanup(cleanup_string)))
 
 
5196
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5197
  g_assert_nonnull(tempfilename);
 
 
5198
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5199
  g_assert_cmpint(questionfile, >, 0);
 
 
5200
  FILE *qf = fdopen(questionfile, "w");
 
 
5201
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5202
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
 
5203
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5205
  char *const filename = strdup(tempfilename);
 
 
5206
  g_assert_nonnull(filename);
 
 
5207
  task_context task = {
 
 
5208
    .func=open_and_parse_question,
 
 
5209
    .question_filename=filename,
 
 
5211
    .password=&password,
 
 
5213
    .cancelled_filenames=&cancelled_filenames,
 
 
5214
    .current_time=¤t_time,
 
 
5215
    .mandos_client_exited=&mandos_client_exited,
 
 
5216
    .password_is_read=&password_is_read,
 
 
5218
  task.func(task, queue);
 
 
5219
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5221
  __attribute__((cleanup(cleanup_string)))
 
 
5222
    char *socket_filename = strdup("/nonexistent");
 
 
5223
  g_assert_nonnull(socket_filename);
 
 
5224
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5225
        .func=connect_question_socket,
 
 
5226
        .question_filename=tempfilename,
 
 
5227
        .filename=socket_filename,
 
 
5229
        .password=&password,
 
 
5230
        .current_time=¤t_time,
 
 
5231
        .mandos_client_exited=&mandos_client_exited,
 
 
5232
        .password_is_read=&password_is_read,
 
 
5235
  g_assert_true(queue->next_run != 0);
 
 
5237
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5241
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
 
5242
                                          test_fixture *fixture,
 
 
5243
                                          __attribute__((unused))
 
 
5244
                                          gconstpointer user_data){
 
 
5245
  __attribute__((cleanup(cleanup_close)))
 
 
5246
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5247
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5248
  __attribute__((cleanup(string_set_clear)))
 
 
5249
    string_set cancelled_filenames = {};
 
 
5250
  buffer password = {};
 
 
5251
  bool mandos_client_exited = false;
 
 
5252
  bool password_is_read = false;
 
 
5253
  __attribute__((cleanup(cleanup_queue)))
 
 
5254
    task_queue *queue = create_queue();
 
 
5255
  g_assert_nonnull(queue);
 
 
5256
  const mono_microsecs current_time = 0;
 
 
5258
  __attribute__((cleanup(cleanup_string)))
 
 
5259
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5260
  g_assert_nonnull(tempfilename);
 
 
5261
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5262
  g_assert_cmpint(questionfile, >, 0);
 
 
5263
  FILE *qf = fdopen(questionfile, "w");
 
 
5264
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5265
                          PRIuMAX "\nNotAfter=\n",
 
 
5266
                          (uintmax_t)getpid()), >, 0);
 
 
5267
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5269
  char *const filename = strdup(tempfilename);
 
 
5270
  g_assert_nonnull(filename);
 
 
5271
  task_context task = {
 
 
5272
    .func=open_and_parse_question,
 
 
5273
    .question_filename=filename,
 
 
5275
    .password=&password,
 
 
5277
    .cancelled_filenames=&cancelled_filenames,
 
 
5278
    .current_time=¤t_time,
 
 
5279
    .mandos_client_exited=&mandos_client_exited,
 
 
5280
    .password_is_read=&password_is_read,
 
 
5282
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5283
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5285
  __attribute__((cleanup(cleanup_string)))
 
 
5286
    char *socket_filename = strdup("/nonexistent");
 
 
5287
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5288
        .func=connect_question_socket,
 
 
5289
        .question_filename=tempfilename,
 
 
5290
        .filename=socket_filename,
 
 
5292
        .password=&password,
 
 
5293
        .current_time=¤t_time,
 
 
5294
        .mandos_client_exited=&mandos_client_exited,
 
 
5295
        .password_is_read=&password_is_read,
 
 
5297
  g_assert_true(queue->next_run != 0);
 
 
5299
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5303
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
 
5305
                                                  const mono_microsecs
 
 
5307
                                                  const mono_microsecs
 
 
5309
  __attribute__((cleanup(cleanup_close)))
 
 
5310
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5311
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5312
  __attribute__((cleanup(string_set_clear)))
 
 
5313
    string_set cancelled_filenames = {};
 
 
5314
  buffer password = {};
 
 
5315
  bool mandos_client_exited = false;
 
 
5316
  bool password_is_read = false;
 
 
5317
  __attribute__((cleanup(cleanup_queue)))
 
 
5318
    task_queue *queue = create_queue();
 
 
5319
  g_assert_nonnull(queue);
 
 
5320
  queue->next_run = next_queue_run;
 
 
5322
  __attribute__((cleanup(cleanup_string)))
 
 
5323
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5324
  g_assert_nonnull(tempfilename);
 
 
5325
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5326
  g_assert_cmpint(questionfile, >, 0);
 
 
5327
  FILE *qf = fdopen(questionfile, "w");
 
 
5328
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5329
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
 
5330
                          (uintmax_t)getpid(), notafter), >, 0);
 
 
5331
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5333
  char *const filename = strdup(tempfilename);
 
 
5334
  g_assert_nonnull(filename);
 
 
5335
  task_context task = {
 
 
5336
    .func=open_and_parse_question,
 
 
5337
    .question_filename=filename,
 
 
5339
    .password=&password,
 
 
5341
    .cancelled_filenames=&cancelled_filenames,
 
 
5342
    .current_time=¤t_time,
 
 
5343
    .mandos_client_exited=&mandos_client_exited,
 
 
5344
    .password_is_read=&password_is_read,
 
 
5346
  task.func(task, queue);
 
 
5348
  if(queue->length >= 1){
 
 
5349
    __attribute__((cleanup(cleanup_string)))
 
 
5350
      char *socket_filename = strdup("/nonexistent");
 
 
5351
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5352
          .func=connect_question_socket,
 
 
5353
          .filename=socket_filename,
 
 
5355
          .password=&password,
 
 
5356
          .current_time=¤t_time,
 
 
5357
          .cancelled_filenames=&cancelled_filenames,
 
 
5358
          .mandos_client_exited=&mandos_client_exited,
 
 
5359
          .password_is_read=&password_is_read,
 
 
5361
    g_assert_true(queue->next_run != 0);
 
 
5365
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5366
  } else if(current_time >= notafter) {
 
 
5367
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5369
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5370
          .func=cancel_old_question,
 
 
5371
          .question_filename=tempfilename,
 
 
5372
          .filename=tempfilename,
 
 
5374
          .cancelled_filenames=&cancelled_filenames,
 
 
5375
          .current_time=¤t_time,
 
 
5378
  g_assert_true(queue->next_run == 1);
 
 
5380
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5384
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
 
5385
                                        test_fixture *fixture,
 
 
5386
                                        __attribute__((unused))
 
 
5387
                                        gconstpointer user_data){
 
 
5388
  /* current_time, notafter, next_queue_run */
 
 
5389
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
 
5393
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
 
5394
                                        test_fixture *fixture,
 
 
5395
                                        __attribute__((unused))
 
 
5396
                                        gconstpointer user_data){
 
 
5397
  /* current_time, notafter, next_queue_run */
 
 
5398
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
 
5402
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
 
5403
                                          test_fixture *fixture,
 
 
5404
                                          __attribute__((unused))
 
 
5405
                                          gconstpointer user_data){
 
 
5406
  /* current_time, notafter, next_queue_run */
 
 
5407
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
 
5411
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
 
5412
                                          test_fixture *fixture,
 
 
5413
                                          __attribute__((unused))
 
 
5414
                                          gconstpointer user_data){
 
 
5415
  /* current_time, notafter, next_queue_run */
 
 
5416
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
 
5420
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
 
5421
                                            test_fixture *fixture,
 
 
5422
                                            __attribute__((unused))
 
 
5423
                                            gconstpointer user_data){
 
 
5424
  /* current_time, notafter, next_queue_run */
 
 
5425
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
 
5429
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
 
5430
                                           test_fixture *fixture,
 
 
5431
                                           __attribute__((unused))
 
 
5432
                                           gconstpointer user_data){
 
 
5433
  /* current_time, notafter, next_queue_run */
 
 
5434
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
 
5437
static void assert_cancel_old_question_param(const mono_microsecs
 
 
5439
                                             const mono_microsecs
 
 
5441
                                             const mono_microsecs
 
 
5443
                                             const mono_microsecs
 
 
5445
  __attribute__((cleanup(string_set_clear)))
 
 
5446
    string_set cancelled_filenames = {};
 
 
5447
  __attribute__((cleanup(cleanup_queue)))
 
 
5448
    task_queue *queue = create_queue();
 
 
5449
  g_assert_nonnull(queue);
 
 
5450
  queue->next_run = next_queue_run;
 
 
5452
  char *const question_filename = strdup("/nonexistent");
 
 
5453
  g_assert_nonnull(question_filename);
 
 
5454
  task_context task = {
 
 
5455
    .func=cancel_old_question,
 
 
5456
    .question_filename=question_filename,
 
 
5457
    .filename=question_filename,
 
 
5459
    .cancelled_filenames=&cancelled_filenames,
 
 
5460
    .current_time=¤t_time,
 
 
5462
  task.func(task, queue);
 
 
5464
  if(current_time >= notafter){
 
 
5465
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5466
    g_assert_true(string_set_contains(cancelled_filenames,
 
 
5469
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5470
          .func=cancel_old_question,
 
 
5471
          .question_filename=question_filename,
 
 
5472
          .filename=question_filename,
 
 
5474
          .cancelled_filenames=&cancelled_filenames,
 
 
5475
          .current_time=¤t_time,
 
 
5478
    g_assert_false(string_set_contains(cancelled_filenames,
 
 
5479
                                       question_filename));
 
 
5481
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
 
5482
                   (unsigned int)next_set_to);
 
 
5485
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
 
5486
                                           test_fixture *fixture,
 
 
5487
                                           __attribute__((unused))
 
 
5488
                                           gconstpointer user_data){
 
 
5489
  /* next_queue_run unset,
 
 
5490
     cancellation should happen because time has come,
 
 
5491
     next_queue_run should be unchanged */
 
 
5492
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5493
  assert_cancel_old_question_param(0, 1, 2, 0);
 
 
5496
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
 
5497
                                           test_fixture *fixture,
 
 
5498
                                           __attribute__((unused))
 
 
5499
                                           gconstpointer user_data){
 
 
5500
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
 
5501
     and current_time is not yet notafter or greater,
 
 
5502
     update value of next_queue_run to value of notafter */
 
 
5503
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5504
  assert_cancel_old_question_param(0, 2, 1, 2);
 
 
5507
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
 
5508
                                           test_fixture *fixture,
 
 
5509
                                           __attribute__((unused))
 
 
5510
                                           gconstpointer user_data){
 
 
5511
  /* next_queue_run 1,
 
 
5512
     cancellation should happen because time has come,
 
 
5513
     next_queue_run should be unchanged */
 
 
5514
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5515
  assert_cancel_old_question_param(1, 2, 3, 1);
 
 
5518
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
 
5519
                                           test_fixture *fixture,
 
 
5520
                                           __attribute__((unused))
 
 
5521
                                           gconstpointer user_data){
 
 
5522
  /* If next_queue_run is set,
 
 
5523
     and current_time is not yet notafter or greater,
 
 
5524
     and notafter is larger than next_queue_run
 
 
5525
     next_queue_run should be unchanged */
 
 
5526
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5527
  assert_cancel_old_question_param(1, 3, 2, 1);
 
 
5530
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
 
5531
                                           test_fixture *fixture,
 
 
5532
                                           __attribute__((unused))
 
 
5533
                                           gconstpointer user_data){
 
 
5534
  /* next_queue_run 2,
 
 
5535
     cancellation should happen because time has come,
 
 
5536
     next_queue_run should be unchanged */
 
 
5537
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5538
  assert_cancel_old_question_param(2, 1, 3, 2);
 
 
5541
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
 
5542
                                           test_fixture *fixture,
 
 
5543
                                           __attribute__((unused))
 
 
5544
                                           gconstpointer user_data){
 
 
5545
  /* If next_queue_run is set,
 
 
5546
     and current_time is not yet notafter or greater,
 
 
5547
     and notafter is larger than next_queue_run
 
 
5548
     next_queue_run should be unchanged */
 
 
5549
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5550
  assert_cancel_old_question_param(2, 3, 1, 2);
 
 
5553
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
 
5554
                                           test_fixture *fixture,
 
 
5555
                                           __attribute__((unused))
 
 
5556
                                           gconstpointer user_data){
 
 
5557
  /* next_queue_run 3,
 
 
5558
     cancellation should happen because time has come,
 
 
5559
     next_queue_run should be unchanged */
 
 
5560
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5561
  assert_cancel_old_question_param(3, 1, 2, 3);
 
 
5564
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
 
5565
                                           test_fixture *fixture,
 
 
5566
                                           __attribute__((unused))
 
 
5567
                                           gconstpointer user_data){
 
 
5568
  /* If next_queue_run is set,
 
 
5569
     and current_time is not yet notafter or greater,
 
 
5570
     and notafter is smaller than next_queue_run
 
 
5571
     update value of next_queue_run to value of notafter */
 
 
5572
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5573
  assert_cancel_old_question_param(3, 2, 1, 2);
 
 
5577
test_connect_question_socket_name_too_long(__attribute__((unused))
 
 
5578
                                           test_fixture *fixture,
 
 
5579
                                           __attribute__((unused))
 
 
5580
                                           gconstpointer user_data){
 
 
5581
  __attribute__((cleanup(cleanup_close)))
 
 
5582
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5583
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5584
  const char question_filename[] = "/nonexistent/question";
 
 
5585
  __attribute__((cleanup(string_set_clear)))
 
 
5586
    string_set cancelled_filenames = {};
 
 
5587
  __attribute__((cleanup(cleanup_queue)))
 
 
5588
    task_queue *queue = create_queue();
 
 
5589
  g_assert_nonnull(queue);
 
 
5590
  __attribute__((cleanup(cleanup_string)))
 
 
5591
    char *tempdir = make_temporary_directory();
 
 
5592
  g_assert_nonnull(tempdir);
 
 
5593
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
 
5594
  char socket_name[sizeof(unix_socket.sun_path)];
 
 
5595
  memset(socket_name, 'x', sizeof(socket_name));
 
 
5596
  socket_name[sizeof(socket_name)-1] = '\0';
 
 
5597
  char *filename = NULL;
 
 
5598
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5600
  g_assert_nonnull(filename);
 
 
5602
  task_context task = {
 
 
5603
    .func=connect_question_socket,
 
 
5604
    .question_filename=strdup(question_filename),
 
 
5606
    .password=(buffer[]){{}},
 
 
5608
    .cancelled_filenames=&cancelled_filenames,
 
 
5609
    .mandos_client_exited=(bool[]){false},
 
 
5610
    .password_is_read=(bool[]){false},
 
 
5611
    .current_time=(mono_microsecs[]){0},
 
 
5613
  g_assert_nonnull(task.question_filename);
 
 
5614
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5616
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5617
                                    question_filename));
 
 
5618
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5619
  g_assert_true(queue->next_run == 0);
 
 
5621
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5625
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
 
5626
                                               test_fixture *fixture,
 
 
5627
                                               __attribute__((unused))
 
 
5630
  __attribute__((cleanup(cleanup_close)))
 
 
5631
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5632
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5633
  const char question_filename[] = "/nonexistent/question";
 
 
5634
  __attribute__((cleanup(string_set_clear)))
 
 
5635
    string_set cancelled_filenames = {};
 
 
5636
  const mono_microsecs current_time = 3;
 
 
5637
  __attribute__((cleanup(cleanup_queue)))
 
 
5638
    task_queue *queue = create_queue();
 
 
5639
  g_assert_nonnull(queue);
 
 
5640
  __attribute__((cleanup(cleanup_string)))
 
 
5641
    char *tempdir = make_temporary_directory();
 
 
5642
  g_assert_nonnull(tempdir);
 
 
5643
  char socket_name[] = "nonexistent";
 
 
5644
  char *filename = NULL;
 
 
5645
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5647
  g_assert_nonnull(filename);
 
 
5649
  task_context task = {
 
 
5650
    .func=connect_question_socket,
 
 
5651
    .question_filename=strdup(question_filename),
 
 
5653
    .password=(buffer[]){{}},
 
 
5655
    .cancelled_filenames=&cancelled_filenames,
 
 
5656
    .mandos_client_exited=(bool[]){false},
 
 
5657
    .password_is_read=(bool[]){false},
 
 
5658
    .current_time=¤t_time,
 
 
5660
  g_assert_nonnull(task.question_filename);
 
 
5661
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5663
  g_assert_nonnull(find_matching_task(queue, task));
 
 
5665
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
5666
                                     question_filename));
 
 
5667
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5668
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5670
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5674
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
 
5675
                                            test_fixture *fixture,
 
 
5676
                                            __attribute__((unused))
 
 
5677
                                            gconstpointer user_data){
 
 
5678
  __attribute__((cleanup(cleanup_close)))
 
 
5679
    const int epoll_fd = open("/dev/null",
 
 
5680
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
5681
  __attribute__((cleanup(cleanup_string)))
 
 
5682
    char *const question_filename = strdup("/nonexistent/question");
 
 
5683
  g_assert_nonnull(question_filename);
 
 
5684
  __attribute__((cleanup(string_set_clear)))
 
 
5685
    string_set cancelled_filenames = {};
 
 
5686
  const mono_microsecs current_time = 5;
 
 
5687
  __attribute__((cleanup(cleanup_queue)))
 
 
5688
    task_queue *queue = create_queue();
 
 
5689
  g_assert_nonnull(queue);
 
 
5690
  __attribute__((cleanup(cleanup_string)))
 
 
5691
    char *tempdir = make_temporary_directory();
 
 
5692
  g_assert_nonnull(tempdir);
 
 
5693
  __attribute__((cleanup(cleanup_close)))
 
 
5694
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5695
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5696
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5697
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5698
  const char socket_name[] = "socket_name";
 
 
5699
  __attribute__((cleanup(cleanup_string)))
 
 
5700
    char *filename = NULL;
 
 
5701
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5703
  g_assert_nonnull(filename);
 
 
5704
  g_assert_cmpint((int)strlen(filename), <,
 
 
5705
                  (int)sizeof(sock_name.sun_path));
 
 
5706
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5707
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5708
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5709
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5710
  task_context task = {
 
 
5711
    .func=connect_question_socket,
 
 
5712
    .question_filename=strdup(question_filename),
 
 
5714
    .password=(buffer[]){{}},
 
 
5715
    .filename=strdup(filename),
 
 
5716
    .cancelled_filenames=&cancelled_filenames,
 
 
5717
    .mandos_client_exited=(bool[]){false},
 
 
5718
    .password_is_read=(bool[]){false},
 
 
5719
    .current_time=¤t_time,
 
 
5721
  g_assert_nonnull(task.question_filename);
 
 
5722
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5724
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5725
  const task_context *const added_task
 
 
5726
    = find_matching_task(queue, task);
 
 
5727
  g_assert_nonnull(added_task);
 
 
5728
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5730
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5731
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5735
void test_connect_question_socket_usable(__attribute__((unused))
 
 
5736
                                         test_fixture *fixture,
 
 
5737
                                         __attribute__((unused))
 
 
5738
                                         gconstpointer user_data){
 
 
5739
  __attribute__((cleanup(cleanup_close)))
 
 
5740
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5741
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5742
  __attribute__((cleanup(cleanup_string)))
 
 
5743
    char *const question_filename = strdup("/nonexistent/question");
 
 
5744
  g_assert_nonnull(question_filename);
 
 
5745
  __attribute__((cleanup(string_set_clear)))
 
 
5746
    string_set cancelled_filenames = {};
 
 
5747
  buffer password = {};
 
 
5748
  bool mandos_client_exited = false;
 
 
5749
  bool password_is_read = false;
 
 
5750
  const mono_microsecs current_time = 0;
 
 
5751
  __attribute__((cleanup(cleanup_queue)))
 
 
5752
    task_queue *queue = create_queue();
 
 
5753
  g_assert_nonnull(queue);
 
 
5754
  __attribute__((cleanup(cleanup_string)))
 
 
5755
    char *tempdir = make_temporary_directory();
 
 
5756
  g_assert_nonnull(tempdir);
 
 
5757
  __attribute__((cleanup(cleanup_close)))
 
 
5758
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5759
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5760
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5761
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5762
  const char socket_name[] = "socket_name";
 
 
5763
  __attribute__((cleanup(cleanup_string)))
 
 
5764
    char *filename = NULL;
 
 
5765
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5767
  g_assert_nonnull(filename);
 
 
5768
  g_assert_cmpint((int)strlen(filename), <,
 
 
5769
                  (int)sizeof(sock_name.sun_path));
 
 
5770
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5771
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5772
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5773
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5774
  task_context task = {
 
 
5775
    .func=connect_question_socket,
 
 
5776
    .question_filename=strdup(question_filename),
 
 
5778
    .password=&password,
 
 
5779
    .filename=strdup(filename),
 
 
5780
    .cancelled_filenames=&cancelled_filenames,
 
 
5781
    .mandos_client_exited=&mandos_client_exited,
 
 
5782
    .password_is_read=&password_is_read,
 
 
5783
    .current_time=¤t_time,
 
 
5785
  g_assert_nonnull(task.question_filename);
 
 
5786
  task.func(task, queue);
 
 
5788
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5789
  const task_context *const added_task
 
 
5790
    = find_matching_task(queue, (task_context){
 
 
5791
        .func=send_password_to_socket,
 
 
5792
        .question_filename=question_filename,
 
 
5795
        .password=&password,
 
 
5796
        .cancelled_filenames=&cancelled_filenames,
 
 
5797
        .mandos_client_exited=&mandos_client_exited,
 
 
5798
        .password_is_read=&password_is_read,
 
 
5799
        .current_time=¤t_time,
 
 
5801
  g_assert_nonnull(added_task);
 
 
5802
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5804
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5807
  const int fd = added_task->fd;
 
 
5808
  g_assert_cmpint(fd, >, 0);
 
 
5809
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
 
5812
  char write_data[PIPE_BUF];
 
 
5814
    /* Construct test password buffer */
 
 
5815
    /* Start with + since that is what the real protocol uses */
 
 
5816
    write_data[0] = '+';
 
 
5817
    /* Set a special character at string end just to mark the end */
 
 
5818
    write_data[sizeof(write_data)-2] = 'y';
 
 
5819
    /* Set NUL at buffer end, as suggested by the protocol */
 
 
5820
    write_data[sizeof(write_data)-1] = '\0';
 
 
5821
    /* Fill rest of password with 'x' */
 
 
5822
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
 
5823
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
 
5824
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
 
5827
  /* read from sock_fd */
 
 
5828
  char read_data[sizeof(write_data)];
 
 
5829
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
 
5830
                  ==, sizeof(read_data));
 
 
5832
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
 
5835
  /* writing to sock_fd should fail */
 
 
5836
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
 
5837
                       MSG_NOSIGNAL), <, 0);
 
 
5839
  /* reading from fd should fail */
 
 
5840
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
 
5841
                            MSG_NOSIGNAL), <, 0);
 
 
5843
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5844
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5848
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
 
5849
                                               test_fixture *fixture,
 
 
5850
                                               __attribute__((unused))
 
 
5853
  __attribute__((cleanup(cleanup_close)))
 
 
5854
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5855
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5856
  __attribute__((cleanup(cleanup_string)))
 
 
5857
    char *const question_filename = strdup("/nonexistent/question");
 
 
5858
  g_assert_nonnull(question_filename);
 
 
5859
  __attribute__((cleanup(cleanup_string)))
 
 
5860
    char *const filename = strdup("/nonexistent/socket");
 
 
5861
  g_assert_nonnull(filename);
 
 
5862
  __attribute__((cleanup(string_set_clear)))
 
 
5863
    string_set cancelled_filenames = {};
 
 
5864
  buffer password = {};
 
 
5865
  bool password_is_read = true;
 
 
5866
  __attribute__((cleanup(cleanup_queue)))
 
 
5867
    task_queue *queue = create_queue();
 
 
5868
  g_assert_nonnull(queue);
 
 
5870
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5871
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5873
  __attribute__((cleanup(cleanup_close)))
 
 
5874
    const int read_socket = socketfds[0];
 
 
5875
  const int write_socket = socketfds[1];
 
 
5876
  task_context task = {
 
 
5877
    .func=send_password_to_socket,
 
 
5878
    .question_filename=strdup(question_filename),
 
 
5879
    .filename=strdup(filename),
 
 
5882
    .password=&password,
 
 
5883
    .cancelled_filenames=&cancelled_filenames,
 
 
5884
    .mandos_client_exited=(bool[]){false},
 
 
5885
    .password_is_read=&password_is_read,
 
 
5886
    .current_time=(mono_microsecs[]){0},
 
 
5888
  g_assert_nonnull(task.question_filename);
 
 
5890
  task.func(task, queue);
 
 
5892
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5894
  const task_context *const added_task
 
 
5895
    = find_matching_task(queue, task);
 
 
5896
  g_assert_nonnull(added_task);
 
 
5897
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5898
  g_assert_true(password_is_read);
 
 
5900
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5901
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5906
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
 
5907
                                               test_fixture *fixture,
 
 
5908
                                               __attribute__((unused))
 
 
5911
  __attribute__((cleanup(cleanup_close)))
 
 
5912
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5913
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5914
  __attribute__((cleanup(cleanup_string)))
 
 
5915
    char *const question_filename = strdup("/nonexistent/question");
 
 
5916
  g_assert_nonnull(question_filename);
 
 
5917
  __attribute__((cleanup(cleanup_string)))
 
 
5918
    char *const filename = strdup("/nonexistent/socket");
 
 
5919
  __attribute__((cleanup(string_set_clear)))
 
 
5920
    string_set cancelled_filenames = {};
 
 
5921
  buffer password = {};
 
 
5922
  __attribute__((cleanup(cleanup_queue)))
 
 
5923
    task_queue *queue = create_queue();
 
 
5924
  g_assert_nonnull(queue);
 
 
5926
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5927
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5929
  __attribute__((cleanup(cleanup_close)))
 
 
5930
    const int read_socket = socketfds[0];
 
 
5931
  const int write_socket = socketfds[1];
 
 
5932
  task_context task = {
 
 
5933
    .func=send_password_to_socket,
 
 
5934
    .question_filename=strdup(question_filename),
 
 
5935
    .filename=strdup(filename),
 
 
5938
    .password=&password,
 
 
5939
    .cancelled_filenames=&cancelled_filenames,
 
 
5940
    .mandos_client_exited=(bool[]){false},
 
 
5941
    .password_is_read=(bool[]){false},
 
 
5942
    .current_time=(mono_microsecs[]){0},
 
 
5944
  g_assert_nonnull(task.question_filename);
 
 
5946
  task.func(task, queue);
 
 
5948
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5950
  const task_context *const added_task = find_matching_task(queue,
 
 
5952
  g_assert_nonnull(added_task);
 
 
5953
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5954
  g_assert_true(queue->next_run == 0);
 
 
5956
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5957
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5962
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
 
5963
                                           test_fixture *fixture,
 
 
5964
                                           __attribute__((unused))
 
 
5965
                                           gconstpointer user_data){
 
 
5966
  __attribute__((cleanup(cleanup_close)))
 
 
5967
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5968
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5969
  const char question_filename[] = "/nonexistent/question";
 
 
5970
  char *const filename = strdup("/nonexistent/socket");
 
 
5971
  __attribute__((cleanup(string_set_clear)))
 
 
5972
    string_set cancelled_filenames = {};
 
 
5975
  /* Find a message size which triggers EMSGSIZE */
 
 
5976
  __attribute__((cleanup(cleanup_string)))
 
 
5977
    char *message_buffer = NULL;
 
 
5978
  size_t message_size = PIPE_BUF + 1;
 
 
5979
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
 
5980
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
 
5981
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
 
5984
    message_buffer = realloc(message_buffer, message_size);
 
 
5985
    if(message_buffer == NULL){
 
 
5986
      g_test_skip("Skipping EMSGSIZE test");
 
 
5987
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
 
5988
                     (uintmax_t)message_size);
 
 
5991
    /* Fill buffer with 'x' */
 
 
5992
    memset(message_buffer, 'x', message_size);
 
 
5993
    /* Create a new socketpair for each message size to avoid having
 
 
5994
       to empty the pipe by reading the message to a separate buffer
 
 
5996
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5997
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5999
    ssret = send(socketfds[1], message_buffer, message_size,
 
 
6001
    error_t saved_errno = errno;
 
 
6002
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
 
6003
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
 
6006
      if(saved_errno != EMSGSIZE) {
 
 
6007
        g_test_skip("Skipping EMSGSIZE test");
 
 
6008
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
 
 
6009
                       (uintmax_t)message_size,
 
 
6010
                       strerror(saved_errno));
 
 
6014
    } else if(ssret != (ssize_t)message_size){
 
 
6015
      g_test_skip("Skipping EMSGSIZE test");
 
 
6016
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
 
6017
                     " bytes", (uintmax_t)ssret,
 
 
6018
                     (intmax_t)message_size);
 
 
6022
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
 
6023
                 (intmax_t)message_size);
 
 
6026
    .data=message_buffer,
 
 
6027
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
 
6028
    .allocated=message_size,
 
 
6030
  if(mlock(password.data, password.allocated) != 0){
 
 
6031
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6034
  __attribute__((cleanup(cleanup_queue)))
 
 
6035
    task_queue *queue = create_queue();
 
 
6036
  g_assert_nonnull(queue);
 
 
6037
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6038
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6040
  __attribute__((cleanup(cleanup_close)))
 
 
6041
    const int read_socket = socketfds[0];
 
 
6042
  __attribute__((cleanup(cleanup_close)))
 
 
6043
    const int write_socket = socketfds[1];
 
 
6044
  task_context task = {
 
 
6045
    .func=send_password_to_socket,
 
 
6046
    .question_filename=strdup(question_filename),
 
 
6050
    .password=&password,
 
 
6051
    .cancelled_filenames=&cancelled_filenames,
 
 
6052
    .mandos_client_exited=(bool[]){true},
 
 
6053
    .password_is_read=(bool[]){true},
 
 
6054
    .current_time=(mono_microsecs[]){0},
 
 
6056
  g_assert_nonnull(task.question_filename);
 
 
6058
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6060
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6061
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
6062
                                    question_filename));
 
 
6065
static void test_send_password_to_socket_retry(__attribute__((unused))
 
 
6066
                                               test_fixture *fixture,
 
 
6067
                                               __attribute__((unused))
 
 
6070
  __attribute__((cleanup(cleanup_close)))
 
 
6071
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6072
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6073
  __attribute__((cleanup(cleanup_string)))
 
 
6074
    char *const question_filename = strdup("/nonexistent/question");
 
 
6075
  g_assert_nonnull(question_filename);
 
 
6076
  __attribute__((cleanup(cleanup_string)))
 
 
6077
    char *const filename = strdup("/nonexistent/socket");
 
 
6078
  g_assert_nonnull(filename);
 
 
6079
  __attribute__((cleanup(string_set_clear)))
 
 
6080
    string_set cancelled_filenames = {};
 
 
6081
  __attribute__((cleanup(cleanup_buffer)))
 
 
6082
    buffer password = {};
 
 
6084
  __attribute__((cleanup(cleanup_queue)))
 
 
6085
    task_queue *queue = create_queue();
 
 
6086
  g_assert_nonnull(queue);
 
 
6088
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6089
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6091
  __attribute__((cleanup(cleanup_close)))
 
 
6092
    const int read_socket = socketfds[0];
 
 
6093
  const int write_socket = socketfds[1];
 
 
6094
  /* Close the server side socket to force ECONNRESET on client */
 
 
6095
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6096
  task_context task = {
 
 
6097
    .func=send_password_to_socket,
 
 
6098
    .question_filename=strdup(question_filename),
 
 
6099
    .filename=strdup(filename),
 
 
6102
    .password=&password,
 
 
6103
    .cancelled_filenames=&cancelled_filenames,
 
 
6104
    .mandos_client_exited=(bool[]){true},
 
 
6105
    .password_is_read=(bool[]){true},
 
 
6106
    .current_time=(mono_microsecs[]){0},
 
 
6108
  g_assert_nonnull(task.question_filename);
 
 
6110
  task.func(task, queue);
 
 
6112
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6114
  const task_context *const added_task = find_matching_task(queue,
 
 
6116
  g_assert_nonnull(added_task);
 
 
6117
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6119
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
6124
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
 
6125
                                            test_fixture *fixture,
 
 
6126
                                            __attribute__((unused))
 
 
6127
                                            gconstpointer user_data){
 
 
6128
  __attribute__((cleanup(cleanup_close)))
 
 
6129
    const int epoll_fd = open("/dev/null",
 
 
6130
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6131
  __attribute__((cleanup(cleanup_string)))
 
 
6132
    char *const question_filename = strdup("/nonexistent/question");
 
 
6133
  g_assert_nonnull(question_filename);
 
 
6134
  __attribute__((cleanup(cleanup_string)))
 
 
6135
    char *const filename = strdup("/nonexistent/socket");
 
 
6136
  g_assert_nonnull(filename);
 
 
6137
  __attribute__((cleanup(string_set_clear)))
 
 
6138
    string_set cancelled_filenames = {};
 
 
6139
  __attribute__((cleanup(cleanup_buffer)))
 
 
6140
    buffer password = {};
 
 
6142
  const mono_microsecs current_time = 11;
 
 
6143
  __attribute__((cleanup(cleanup_queue)))
 
 
6144
    task_queue *queue = create_queue();
 
 
6145
  g_assert_nonnull(queue);
 
 
6147
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6148
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6150
  __attribute__((cleanup(cleanup_close)))
 
 
6151
    const int read_socket = socketfds[0];
 
 
6152
  const int write_socket = socketfds[1];
 
 
6153
  /* Close the server side socket to force ECONNRESET on client */
 
 
6154
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6155
  task_context task = {
 
 
6156
    .func=send_password_to_socket,
 
 
6157
    .question_filename=strdup(question_filename),
 
 
6158
    .filename=strdup(filename),
 
 
6161
    .password=&password,
 
 
6162
    .cancelled_filenames=&cancelled_filenames,
 
 
6163
    .mandos_client_exited=(bool[]){true},
 
 
6164
    .password_is_read=(bool[]){true},
 
 
6165
    .current_time=¤t_time,
 
 
6167
  g_assert_nonnull(task.question_filename);
 
 
6169
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6171
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6173
  const task_context *const added_task = find_matching_task(queue,
 
 
6175
  g_assert_nonnull(added_task);
 
 
6176
  g_assert_true(queue->next_run == current_time + 1000000);
 
 
6177
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6180
static void assert_send_password_to_socket_password(buffer password){
 
 
6181
  __attribute__((cleanup(cleanup_close)))
 
 
6182
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6183
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6184
  char *const question_filename = strdup("/nonexistent/question");
 
 
6185
  g_assert_nonnull(question_filename);
 
 
6186
  char *const filename = strdup("/nonexistent/socket");
 
 
6187
  g_assert_nonnull(filename);
 
 
6188
  __attribute__((cleanup(string_set_clear)))
 
 
6189
    string_set cancelled_filenames = {};
 
 
6191
  __attribute__((cleanup(cleanup_queue)))
 
 
6192
    task_queue *queue = create_queue();
 
 
6193
  g_assert_nonnull(queue);
 
 
6195
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6196
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6198
  __attribute__((cleanup(cleanup_close)))
 
 
6199
    const int read_socket = socketfds[0];
 
 
6200
  const int write_socket = socketfds[1];
 
 
6201
  task_context task = {
 
 
6202
    .func=send_password_to_socket,
 
 
6203
    .question_filename=question_filename,
 
 
6207
    .password=&password,
 
 
6208
    .cancelled_filenames=&cancelled_filenames,
 
 
6209
    .mandos_client_exited=(bool[]){true},
 
 
6210
    .password_is_read=(bool[]){true},
 
 
6211
    .current_time=(mono_microsecs[]){0},
 
 
6214
  char *expected_written_data = malloc(password.length + 2);
 
 
6215
  g_assert_nonnull(expected_written_data);
 
 
6216
  expected_written_data[0] = '+';
 
 
6217
  expected_written_data[password.length + 1] = '\0';
 
 
6218
  if(password.length > 0){
 
 
6219
    g_assert_nonnull(password.data);
 
 
6220
    memcpy(expected_written_data + 1, password.data, password.length);
 
 
6223
  task.func(task, queue);
 
 
6226
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
 
6227
                  (int)(password.length + 2));
 
 
6228
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6230
  g_assert_true(memcmp(expected_written_data, buf,
 
 
6231
                       password.length + 2) == 0);
 
 
6233
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
 
6235
  free(expected_written_data);
 
 
6239
test_send_password_to_socket_null_password(__attribute__((unused))
 
 
6240
                                           test_fixture *fixture,
 
 
6241
                                           __attribute__((unused))
 
 
6242
                                           gconstpointer user_data){
 
 
6243
  __attribute__((cleanup(cleanup_buffer)))
 
 
6244
    buffer password = {};
 
 
6245
  assert_send_password_to_socket_password(password);
 
 
6249
test_send_password_to_socket_empty_password(__attribute__((unused))
 
 
6250
                                            test_fixture *fixture,
 
 
6251
                                            __attribute__((unused))
 
 
6252
                                            gconstpointer user_data){
 
 
6253
  __attribute__((cleanup(cleanup_buffer)))
 
 
6255
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
 
6257
    .allocated=0,               /* deliberate lie */
 
 
6259
  g_assert_nonnull(password.data);
 
 
6260
  assert_send_password_to_socket_password(password);
 
 
6264
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
 
6265
                                            test_fixture *fixture,
 
 
6266
                                            __attribute__((unused))
 
 
6267
                                            gconstpointer user_data){
 
 
6268
  __attribute__((cleanup(cleanup_buffer)))
 
 
6274
  if(mlock(password.data, password.allocated) != 0){
 
 
6275
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6277
  assert_send_password_to_socket_password(password);
 
 
6281
test_send_password_to_socket_text_password(__attribute__((unused))
 
 
6282
                                           test_fixture *fixture,
 
 
6283
                                           __attribute__((unused))
 
 
6284
                                           gconstpointer user_data){
 
 
6285
  const char dummy_test_password[] = "dummy test password";
 
 
6286
  __attribute__((cleanup(cleanup_buffer)))
 
 
6288
    .data = strdup(dummy_test_password),
 
 
6289
    .length = strlen(dummy_test_password),
 
 
6290
    .allocated = sizeof(dummy_test_password),
 
 
6292
  if(mlock(password.data, password.allocated) != 0){
 
 
6293
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6295
  assert_send_password_to_socket_password(password);
 
 
6299
test_send_password_to_socket_binary_password(__attribute__((unused))
 
 
6300
                                             test_fixture *fixture,
 
 
6301
                                             __attribute__((unused))
 
 
6302
                                             gconstpointer user_data){
 
 
6303
  __attribute__((cleanup(cleanup_buffer)))
 
 
6309
  g_assert_nonnull(password.data);
 
 
6310
  if(mlock(password.data, password.allocated) != 0){
 
 
6311
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6313
  char c = 1;                   /* Start at 1, avoiding NUL */
 
 
6314
  for(int i=0; i < 255; i++){
 
 
6315
    password.data[i] = c++;
 
 
6317
  assert_send_password_to_socket_password(password);
 
 
6321
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
 
6322
                                              test_fixture *fixture,
 
 
6323
                                              __attribute__((unused))
 
 
6326
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
 
6327
  __attribute__((cleanup(cleanup_buffer)))
 
 
6329
    .data=malloc(sizeof(test_password)),
 
 
6330
    .length=sizeof(test_password),
 
 
6331
    .allocated=sizeof(test_password),
 
 
6333
  g_assert_nonnull(password.data);
 
 
6334
  if(mlock(password.data, password.allocated) !=0){
 
 
6335
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6337
  memcpy(password.data, test_password, password.allocated);
 
 
6338
  assert_send_password_to_socket_password(password);
 
 
6341
static bool assert_add_existing_questions_to_devnull(task_queue
 
 
6354
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
 
6355
                                               test_fixture *fixture,
 
 
6356
                                               __attribute__((unused))
 
 
6359
  __attribute__((cleanup(cleanup_queue)))
 
 
6360
    task_queue *queue = create_queue();
 
 
6361
  g_assert_nonnull(queue);
 
 
6362
  __attribute__((cleanup(cleanup_close)))
 
 
6363
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6364
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6365
  __attribute__((cleanup(string_set_clear)))
 
 
6366
    string_set cancelled_filenames = {};
 
 
6368
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6371
                  (buffer[]){{}}, /* password */
 
 
6372
                  &cancelled_filenames,
 
 
6373
                  (mono_microsecs[]){0}, /* current_time */
 
 
6374
                  (bool[]){false},       /* mandos_client_exited */
 
 
6375
                  (bool[]){false},       /* password_is_read */
 
 
6376
                  "/nonexistent"));      /* dirname */
 
 
6378
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6382
bool assert_add_existing_questions_to_devnull(task_queue
 
 
6389
                                              *cancelled_filenames,
 
 
6390
                                              const mono_microsecs
 
 
6391
                                              *const current_time,
 
 
6393
                                              mandos_client_exited,
 
 
6398
  __attribute__((cleanup(cleanup_close)))
 
 
6399
    const int devnull_fd = open("/dev/null",
 
 
6400
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6401
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
6402
  __attribute__((cleanup(cleanup_close)))
 
 
6403
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
6404
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
6405
  dup2(devnull_fd, STDERR_FILENO);
 
 
6406
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
 
6407
                                          cancelled_filenames,
 
 
6409
                                          mandos_client_exited,
 
 
6410
                                          password_is_read, dirname);
 
 
6411
  dup2(real_stderr_fd, STDERR_FILENO);
 
 
6416
void test_add_existing_questions_no_questions(__attribute__((unused))
 
 
6417
                                              test_fixture *fixture,
 
 
6418
                                              __attribute__((unused))
 
 
6421
  __attribute__((cleanup(cleanup_queue)))
 
 
6422
    task_queue *queue = create_queue();
 
 
6423
  g_assert_nonnull(queue);
 
 
6424
  __attribute__((cleanup(cleanup_close)))
 
 
6425
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6426
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6427
  __attribute__((cleanup(string_set_clear)))
 
 
6428
    string_set cancelled_filenames = {};
 
 
6429
  __attribute__((cleanup(cleanup_string)))
 
 
6430
    char *tempdir = make_temporary_directory();
 
 
6431
  g_assert_nonnull(tempdir);
 
 
6433
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6436
                  (buffer[]){{}}, /* password */
 
 
6437
                  &cancelled_filenames,
 
 
6438
                  (mono_microsecs[]){0}, /* current_time */
 
 
6439
                  (bool[]){false},       /* mandos_client_exited */
 
 
6440
                  (bool[]){false},       /* password_is_read */
 
 
6443
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6445
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6448
static char *make_question_file_in_directory(const char *const);
 
 
6451
void test_add_existing_questions_one_question(__attribute__((unused))
 
 
6452
                                              test_fixture *fixture,
 
 
6453
                                              __attribute__((unused))
 
 
6456
  __attribute__((cleanup(cleanup_queue)))
 
 
6457
    task_queue *queue = create_queue();
 
 
6458
  g_assert_nonnull(queue);
 
 
6459
  __attribute__((cleanup(cleanup_close)))
 
 
6460
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6461
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6462
  __attribute__((cleanup(cleanup_buffer)))
 
 
6463
    buffer password = {};
 
 
6464
  __attribute__((cleanup(string_set_clear)))
 
 
6465
    string_set cancelled_filenames = {};
 
 
6466
  const mono_microsecs current_time = 0;
 
 
6467
  bool mandos_client_exited = false;
 
 
6468
  bool password_is_read = false;
 
 
6469
  __attribute__((cleanup(cleanup_string)))
 
 
6470
    char *tempdir = make_temporary_directory();
 
 
6471
  g_assert_nonnull(tempdir);
 
 
6472
  __attribute__((cleanup(cleanup_string)))
 
 
6473
    char *question_filename
 
 
6474
    = make_question_file_in_directory(tempdir);
 
 
6475
  g_assert_nonnull(question_filename);
 
 
6477
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6481
                 &cancelled_filenames,
 
 
6483
                 &mandos_client_exited,
 
 
6487
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6489
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6490
        .func=open_and_parse_question,
 
 
6492
        .filename=question_filename,
 
 
6493
        .question_filename=question_filename,
 
 
6494
        .password=&password,
 
 
6495
        .cancelled_filenames=&cancelled_filenames,
 
 
6496
        .current_time=¤t_time,
 
 
6497
        .mandos_client_exited=&mandos_client_exited,
 
 
6498
        .password_is_read=&password_is_read,
 
 
6501
  g_assert_true(queue->next_run == 1);
 
 
6503
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6504
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6507
static char *make_question_file_in_directory(const char
 
 
6509
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
 
6513
void test_add_existing_questions_two_questions(__attribute__((unused))
 
 
6514
                                               test_fixture *fixture,
 
 
6515
                                               __attribute__((unused))
 
 
6518
  __attribute__((cleanup(cleanup_queue)))
 
 
6519
    task_queue *queue = create_queue();
 
 
6520
  g_assert_nonnull(queue);
 
 
6521
  __attribute__((cleanup(cleanup_close)))
 
 
6522
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6523
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6524
  __attribute__((cleanup(cleanup_buffer)))
 
 
6525
    buffer password = {};
 
 
6526
  __attribute__((cleanup(string_set_clear)))
 
 
6527
    string_set cancelled_filenames = {};
 
 
6528
  const mono_microsecs current_time = 0;
 
 
6529
  bool mandos_client_exited = false;
 
 
6530
  bool password_is_read = false;
 
 
6531
  __attribute__((cleanup(cleanup_string)))
 
 
6532
    char *tempdir = make_temporary_directory();
 
 
6533
  g_assert_nonnull(tempdir);
 
 
6534
  __attribute__((cleanup(cleanup_string)))
 
 
6535
    char *question_filename1
 
 
6536
    = make_question_file_in_directory(tempdir);
 
 
6537
  g_assert_nonnull(question_filename1);
 
 
6538
  __attribute__((cleanup(cleanup_string)))
 
 
6539
    char *question_filename2
 
 
6540
    = make_question_file_in_directory(tempdir);
 
 
6541
  g_assert_nonnull(question_filename2);
 
 
6543
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6547
                 &cancelled_filenames,
 
 
6549
                 &mandos_client_exited,
 
 
6553
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
6555
  g_assert_true(queue->next_run == 1);
 
 
6557
  __attribute__((cleanup(string_set_clear)))
 
 
6558
    string_set seen_questions = {};
 
 
6560
  bool queue_contains_question_opener(char *const question_filename){
 
 
6561
    return(find_matching_task(queue, (task_context){
 
 
6562
          .func=open_and_parse_question,
 
 
6564
          .question_filename=question_filename,
 
 
6565
          .password=&password,
 
 
6566
          .cancelled_filenames=&cancelled_filenames,
 
 
6567
          .current_time=¤t_time,
 
 
6568
          .mandos_client_exited=&mandos_client_exited,
 
 
6569
          .password_is_read=&password_is_read,
 
 
6573
  g_assert_true(queue_contains_question_opener(question_filename1));
 
 
6574
  g_assert_true(queue_contains_question_opener(question_filename2));
 
 
6576
  g_assert_true(queue->next_run == 1);
 
 
6578
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6579
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6580
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6584
test_add_existing_questions_non_questions(__attribute__((unused))
 
 
6585
                                          test_fixture *fixture,
 
 
6586
                                          __attribute__((unused))
 
 
6587
                                          gconstpointer user_data){
 
 
6588
  __attribute__((cleanup(cleanup_queue)))
 
 
6589
    task_queue *queue = create_queue();
 
 
6590
  g_assert_nonnull(queue);
 
 
6591
  __attribute__((cleanup(cleanup_close)))
 
 
6592
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6593
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6594
  __attribute__((cleanup(string_set_clear)))
 
 
6595
    string_set cancelled_filenames = {};
 
 
6596
  __attribute__((cleanup(cleanup_string)))
 
 
6597
    char *tempdir = make_temporary_directory();
 
 
6598
  g_assert_nonnull(tempdir);
 
 
6599
  __attribute__((cleanup(cleanup_string)))
 
 
6600
    char *question_filename1
 
 
6601
    = make_temporary_file_in_directory(tempdir);
 
 
6602
  g_assert_nonnull(question_filename1);
 
 
6603
  __attribute__((cleanup(cleanup_string)))
 
 
6604
    char *question_filename2
 
 
6605
    = make_temporary_file_in_directory(tempdir);
 
 
6606
  g_assert_nonnull(question_filename2);
 
 
6608
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6611
                  (buffer[]){{}}, /* password */
 
 
6612
                  &cancelled_filenames,
 
 
6613
                  (mono_microsecs[]){0}, /* current_time */
 
 
6614
                  (bool[]){false},       /* mandos_client_exited */
 
 
6615
                  (bool[]){false},       /* password_is_read */
 
 
6618
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6620
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6621
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6622
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6626
test_add_existing_questions_both_types(__attribute__((unused))
 
 
6627
                                       test_fixture *fixture,
 
 
6628
                                       __attribute__((unused))
 
 
6629
                                       gconstpointer user_data){
 
 
6630
  __attribute__((cleanup(cleanup_queue)))
 
 
6631
    task_queue *queue = create_queue();
 
 
6632
  g_assert_nonnull(queue);
 
 
6633
  __attribute__((cleanup(cleanup_close)))
 
 
6634
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6635
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6636
  __attribute__((cleanup(cleanup_buffer)))
 
 
6637
    buffer password = {};
 
 
6638
  __attribute__((cleanup(string_set_clear)))
 
 
6639
    string_set cancelled_filenames = {};
 
 
6640
  const mono_microsecs current_time = 0;
 
 
6641
  bool mandos_client_exited = false;
 
 
6642
  bool password_is_read = false;
 
 
6643
  __attribute__((cleanup(cleanup_string)))
 
 
6644
    char *tempdir = make_temporary_directory();
 
 
6645
  g_assert_nonnull(tempdir);
 
 
6646
  __attribute__((cleanup(cleanup_string)))
 
 
6647
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
 
6648
  g_assert_nonnull(tempfilename1);
 
 
6649
  __attribute__((cleanup(cleanup_string)))
 
 
6650
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
 
6651
  g_assert_nonnull(tempfilename2);
 
 
6652
  __attribute__((cleanup(cleanup_string)))
 
 
6653
    char *question_filename
 
 
6654
    = make_question_file_in_directory(tempdir);
 
 
6655
  g_assert_nonnull(question_filename);
 
 
6657
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6661
                 &cancelled_filenames,
 
 
6663
                 &mandos_client_exited,
 
 
6667
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6669
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6670
        .func=open_and_parse_question,
 
 
6672
        .filename=question_filename,
 
 
6673
        .question_filename=question_filename,
 
 
6674
        .password=&password,
 
 
6675
        .cancelled_filenames=&cancelled_filenames,
 
 
6676
        .current_time=¤t_time,
 
 
6677
        .mandos_client_exited=&mandos_client_exited,
 
 
6678
        .password_is_read=&password_is_read,
 
 
6681
  g_assert_true(queue->next_run == 1);
 
 
6683
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
 
6684
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
 
6685
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6686
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6689
static void test_wait_for_event_timeout(__attribute__((unused))
 
 
6690
                                        test_fixture *fixture,
 
 
6691
                                        __attribute__((unused))
 
 
6692
                                        gconstpointer user_data){
 
 
6693
  __attribute__((cleanup(cleanup_close)))
 
 
6694
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6695
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6697
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
 
6700
static void test_wait_for_event_event(__attribute__((unused))
 
 
6701
                                      test_fixture *fixture,
 
 
6702
                                      __attribute__((unused))
 
 
6703
                                      gconstpointer user_data){
 
 
6704
  __attribute__((cleanup(cleanup_close)))
 
 
6705
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6706
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6708
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6709
  __attribute__((cleanup(cleanup_close)))
 
 
6710
    const int read_pipe = pipefds[0];
 
 
6711
  __attribute__((cleanup(cleanup_close)))
 
 
6712
    const int write_pipe = pipefds[1];
 
 
6713
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
 
6714
                            &(struct epoll_event)
 
 
6715
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
 
6716
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
 
6718
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6721
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
 
6722
                                        __attribute__((unused))
 
 
6723
                                        gconstpointer user_data){
 
 
6724
  const pid_t pid = fork();
 
 
6725
  if(pid == 0){         /* Child */
 
 
6726
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
6727
      _exit(EXIT_FAILURE);
 
 
6729
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
6730
      _exit(EXIT_FAILURE);
 
 
6734
  g_assert_true(pid != -1);
 
 
6735
  __attribute__((cleanup(cleanup_close)))
 
 
6736
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6737
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6739
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6742
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
 
6743
  g_assert_true(WIFEXITED(status));
 
 
6744
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
 
6747
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
 
6748
                                           test_fixture *fixture,
 
 
6749
                                           __attribute__((unused))
 
 
6750
                                           gconstpointer user_data){
 
 
6751
  __attribute__((cleanup(cleanup_queue)))
 
 
6752
    task_queue *queue = create_queue();
 
 
6753
  g_assert_nonnull(queue);
 
 
6754
  queue->next_run = 1;
 
 
6755
  __attribute__((cleanup(cleanup_close)))
 
 
6756
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6757
  __attribute__((cleanup(string_set_clear)))
 
 
6758
    string_set cancelled_filenames = {};
 
 
6759
  bool quit_now = false;
 
 
6761
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6762
  g_assert_false(quit_now);
 
 
6763
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
 
6767
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
 
6768
                                               test_fixture *fixture,
 
 
6769
                                               __attribute__((unused))
 
 
6772
  __attribute__((cleanup(cleanup_queue)))
 
 
6773
    task_queue *queue = create_queue();
 
 
6774
  g_assert_nonnull(queue);
 
 
6775
  __attribute__((cleanup(string_set_clear)))
 
 
6776
    string_set cancelled_filenames = {};
 
 
6777
  bool quit_now = false;
 
 
6778
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6779
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6780
                               question_filename));
 
 
6782
  g_assert_true(add_to_queue(queue,
 
 
6783
                             (task_context){ .func=dummy_func }));
 
 
6785
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6786
  g_assert_false(quit_now);
 
 
6787
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6788
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
6789
                                     question_filename));
 
 
6793
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
 
6794
                                              test_fixture *fixture,
 
 
6795
                                              __attribute__((unused))
 
 
6798
  __attribute__((cleanup(cleanup_queue)))
 
 
6799
    task_queue *queue = create_queue();
 
 
6800
  g_assert_nonnull(queue);
 
 
6801
  __attribute__((cleanup(string_set_clear)))
 
 
6802
    string_set cancelled_filenames = {};
 
 
6803
  bool quit_now = false;
 
 
6805
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6806
  __attribute__((cleanup(cleanup_close)))
 
 
6807
    const int read_pipe = pipefds[0];
 
 
6808
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
6809
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6810
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6811
                               question_filename));
 
 
6812
  __attribute__((nonnull))
 
 
6813
    void quit_func(const task_context task,
 
 
6814
                   __attribute__((unused)) task_queue *const q){
 
 
6815
    g_assert_nonnull(task.quit_now);
 
 
6816
    *task.quit_now = true;
 
 
6818
  task_context task = {
 
 
6820
    .question_filename=strdup(question_filename),
 
 
6821
    .quit_now=&quit_now,
 
 
6824
  g_assert_nonnull(task.question_filename);
 
 
6826
  g_assert_true(add_to_queue(queue, task));
 
 
6828
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6829
  g_assert_false(quit_now);
 
 
6831
  /* read_pipe should be closed already */
 
 
6833
  bool read_pipe_closed = (close(read_pipe) == -1);
 
 
6834
  read_pipe_closed &= (errno == EBADF);
 
 
6835
  g_assert_true(read_pipe_closed);
 
 
6838
static void test_run_queue_one_task(__attribute__((unused))
 
 
6839
                                    test_fixture *fixture,
 
 
6840
                                    __attribute__((unused))
 
 
6841
                                    gconstpointer user_data){
 
 
6842
  __attribute__((cleanup(cleanup_queue)))
 
 
6843
    task_queue *queue = create_queue();
 
 
6844
  g_assert_nonnull(queue);
 
 
6845
  __attribute__((cleanup(string_set_clear)))
 
 
6846
    string_set cancelled_filenames = {};
 
 
6847
  bool quit_now = false;
 
 
6849
  __attribute__((nonnull))
 
 
6850
    void next_run_func(__attribute__((unused))
 
 
6851
                       const task_context task,
 
 
6852
                       task_queue *const q){
 
 
6856
  task_context task = {
 
 
6857
    .func=next_run_func,
 
 
6859
  g_assert_true(add_to_queue(queue, task));
 
 
6861
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6862
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6863
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6866
static void test_run_queue_two_tasks(__attribute__((unused))
 
 
6867
                                     test_fixture *fixture,
 
 
6868
                                     __attribute__((unused))
 
 
6869
                                     gconstpointer user_data){
 
 
6870
  __attribute__((cleanup(cleanup_queue)))
 
 
6871
    task_queue *queue = create_queue();
 
 
6872
  g_assert_nonnull(queue);
 
 
6873
  queue->next_run = 1;
 
 
6874
  __attribute__((cleanup(string_set_clear)))
 
 
6875
    string_set cancelled_filenames = {};
 
 
6876
  bool quit_now = false;
 
 
6877
  bool mandos_client_exited = false;
 
 
6879
  __attribute__((nonnull))
 
 
6880
    void next_run_func(__attribute__((unused))
 
 
6881
                       const task_context task,
 
 
6882
                       task_queue *const q){
 
 
6886
  __attribute__((nonnull))
 
 
6887
    void exited_func(const task_context task,
 
 
6888
                     __attribute__((unused)) task_queue *const q){
 
 
6889
    *task.mandos_client_exited = true;
 
 
6892
  task_context task1 = {
 
 
6893
    .func=next_run_func,
 
 
6895
  g_assert_true(add_to_queue(queue, task1));
 
 
6897
  task_context task2 = {
 
 
6899
    .mandos_client_exited=&mandos_client_exited,
 
 
6901
  g_assert_true(add_to_queue(queue, task2));
 
 
6903
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6904
  g_assert_false(quit_now);
 
 
6905
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6906
  g_assert_true(mandos_client_exited);
 
 
6907
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6910
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
 
6911
                                          test_fixture *fixture,
 
 
6912
                                          __attribute__((unused))
 
 
6913
                                          gconstpointer user_data){
 
 
6914
  __attribute__((cleanup(cleanup_queue)))
 
 
6915
    task_queue *queue = create_queue();
 
 
6916
  g_assert_nonnull(queue);
 
 
6917
  __attribute__((cleanup(string_set_clear)))
 
 
6918
    string_set cancelled_filenames = {};
 
 
6919
  bool quit_now = false;
 
 
6920
  bool mandos_client_exited = false;
 
 
6921
  bool password_is_read = false;
 
 
6923
  __attribute__((nonnull))
 
 
6924
    void set_exited_func(const task_context task,
 
 
6925
                         __attribute__((unused)) task_queue *const q){
 
 
6926
    *task.mandos_client_exited = true;
 
 
6927
    *task.quit_now = true;
 
 
6929
  task_context task1 = {
 
 
6930
    .func=set_exited_func,
 
 
6931
    .quit_now=&quit_now,
 
 
6932
    .mandos_client_exited=&mandos_client_exited,
 
 
6934
  g_assert_true(add_to_queue(queue, task1));
 
 
6936
  __attribute__((nonnull))
 
 
6937
    void set_read_func(const task_context task,
 
 
6938
                       __attribute__((unused)) task_queue *const q){
 
 
6939
    *task.quit_now = true;
 
 
6940
    *task.password_is_read = true;
 
 
6942
  task_context task2 = {
 
 
6943
    .func=set_read_func,
 
 
6944
    .quit_now=&quit_now,
 
 
6945
    .password_is_read=&password_is_read,
 
 
6947
  g_assert_true(add_to_queue(queue, task2));
 
 
6949
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6950
  g_assert_true(quit_now);
 
 
6951
  g_assert_true(mandos_client_exited xor password_is_read);
 
 
6952
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6955
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
 
6956
                                             test_fixture *fixture,
 
 
6957
                                             __attribute__((unused))
 
 
6958
                                             gconstpointer user_data){
 
 
6959
  __attribute__((cleanup(cleanup_queue)))
 
 
6960
    task_queue *queue = create_queue();
 
 
6961
  g_assert_nonnull(queue);
 
 
6962
  __attribute__((cleanup(string_set_clear)))
 
 
6963
    string_set cancelled_filenames = {};
 
 
6965
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6966
  __attribute__((cleanup(cleanup_close)))
 
 
6967
    const int read_pipe = pipefds[0];
 
 
6968
  __attribute__((cleanup(cleanup_close)))
 
 
6969
    const int write_pipe = pipefds[1];
 
 
6970
  bool quit_now = false;
 
 
6972
  __attribute__((nonnull))
 
 
6973
    void read_func(const task_context task,
 
 
6974
                   __attribute__((unused)) task_queue *const q){
 
 
6975
    *task.quit_now = true;
 
 
6977
  task_context task1 = {
 
 
6979
    .quit_now=&quit_now,
 
 
6982
  g_assert_true(add_to_queue(queue, task1));
 
 
6984
  __attribute__((nonnull))
 
 
6985
    void write_func(const task_context task,
 
 
6986
                    __attribute__((unused)) task_queue *const q){
 
 
6987
    *task.quit_now = true;
 
 
6989
  task_context task2 = {
 
 
6991
    .quit_now=&quit_now,
 
 
6994
  g_assert_true(add_to_queue(queue, task2));
 
 
6996
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6997
  g_assert_true(quit_now);
 
 
6999
  /* Either read_pipe or write_pipe should be closed already */
 
 
7001
  bool close_read_pipe = (close(read_pipe) == -1);
 
 
7002
  close_read_pipe &= (errno == EBADF);
 
 
7004
  bool close_write_pipe = (close(write_pipe) == -1);
 
 
7005
  close_write_pipe &= (errno == EBADF);
 
 
7006
  g_assert_true(close_read_pipe xor close_write_pipe);
 
 
7007
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
7010
static void test_setup_signal_handler(__attribute__((unused))
 
 
7011
                                      test_fixture *fixture,
 
 
7012
                                      __attribute__((unused))
 
 
7013
                                      gconstpointer user_data){
 
 
7014
  /* Save current SIGCHLD action, whatever it is */
 
 
7015
  struct sigaction expected_sigchld_action;
 
 
7016
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7019
  /* Act; i.e. run the setup_signal_handler() function */
 
 
7020
  struct sigaction actual_old_sigchld_action;
 
 
7021
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
 
7023
  /* Check that the function correctly set "actual_old_sigchld_action"
 
 
7024
     to the same values as the previously saved
 
 
7025
     "expected_sigchld_action" */
 
 
7026
  /* Check member sa_handler */
 
 
7027
  g_assert_true(actual_old_sigchld_action.sa_handler
 
 
7028
                == expected_sigchld_action.sa_handler);
 
 
7029
  /* Check member sa_mask */
 
 
7030
  for(int signum = 1; signum < NSIG; signum++){
 
 
7031
    const int expected_old_block_state
 
 
7032
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7033
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7034
    const int actual_old_block_state
 
 
7035
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
 
7036
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7037
    g_assert_cmpint(actual_old_block_state,
 
 
7038
                    ==, expected_old_block_state);
 
 
7040
  /* Check member sa_flags */
 
 
7041
  g_assert_true((actual_old_sigchld_action.sa_flags
 
 
7042
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7043
                == (expected_sigchld_action.sa_flags
 
 
7044
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7046
  /* Retrieve the current signal handler for SIGCHLD as set by
 
 
7047
     setup_signal_handler() */
 
 
7048
  struct sigaction actual_new_sigchld_action;
 
 
7049
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7050
                            &actual_new_sigchld_action), ==, 0);
 
 
7051
  /* Check that the signal handler (member sa_handler) is correctly
 
 
7052
     set to the "handle_sigchld" function */
 
 
7053
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
 
7054
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
 
7055
  g_assert_true(actual_new_sigchld_action.sa_handler
 
 
7057
  /* Check (in member sa_mask) that at least a handful of signals are
 
 
7058
     actually blocked during the signal handler */
 
 
7059
  for(int signum = 1; signum < NSIG; signum++){
 
 
7060
    int actual_new_block_state;
 
 
7066
      actual_new_block_state
 
 
7067
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
 
7068
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
 
7070
    case SIGKILL:               /* non-blockable */
 
 
7071
    case SIGSTOP:               /* non-blockable */
 
 
7072
    case SIGCHLD:               /* always blocked */
 
 
7077
  /* Check member sa_flags */
 
 
7078
  g_assert_true((actual_new_sigchld_action.sa_flags
 
 
7079
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7080
                == (SA_NOCLDSTOP | SA_RESTART));
 
 
7082
  /* Restore signal handler */
 
 
7083
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
 
7087
static void test_restore_signal_handler(__attribute__((unused))
 
 
7088
                                        test_fixture *fixture,
 
 
7089
                                        __attribute__((unused))
 
 
7090
                                        gconstpointer user_data){
 
 
7091
  /* Save current SIGCHLD action, whatever it is */
 
 
7092
  struct sigaction expected_sigchld_action;
 
 
7093
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7095
  /* Since we haven't established a signal handler yet, there should
 
 
7096
     not be one established.  But another test may have relied on
 
 
7097
     restore_signal_handler() to restore the signal handler, and if
 
 
7098
     restore_signal_handler() is buggy (which we should be prepared
 
 
7099
     for in this test) the signal handler may not have been restored
 
 
7100
     properly; check for this: */
 
 
7101
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
 
7103
  /* Establish a signal handler */
 
 
7104
  struct sigaction sigchld_action = {
 
 
7105
    .sa_handler=handle_sigchld,
 
 
7106
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
7108
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
 
7109
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
 
7111
  /* Act; i.e. run the restore_signal_handler() function */
 
 
7112
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
 
7114
  /* Retrieve the restored signal handler data */
 
 
7115
  struct sigaction actual_restored_sigchld_action;
 
 
7116
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7117
                            &actual_restored_sigchld_action), ==, 0);
 
 
7119
  /* Check that the function correctly restored the signal action, as
 
 
7120
     saved in "actual_restored_sigchld_action", to the same values as
 
 
7121
     the previously saved "expected_sigchld_action" */
 
 
7122
  /* Check member sa_handler */
 
 
7123
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
 
7124
                == expected_sigchld_action.sa_handler);
 
 
7125
  /* Check member sa_mask */
 
 
7126
  for(int signum = 1; signum < NSIG; signum++){
 
 
7127
    const int expected_old_block_state
 
 
7128
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7129
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7130
    const int actual_restored_block_state
 
 
7131
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
 
7132
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
 
7133
    g_assert_cmpint(actual_restored_block_state,
 
 
7134
                    ==, expected_old_block_state);
 
 
7136
  /* Check member sa_flags */
 
 
7137
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
 
7138
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7139
                == (expected_sigchld_action.sa_flags
 
 
7140
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7143
static void test_block_sigchld(__attribute__((unused))
 
 
7144
                               test_fixture *fixture,
 
 
7145
                               __attribute__((unused))
 
 
7146
                               gconstpointer user_data){
 
 
7147
  /* Save original signal mask */
 
 
7148
  sigset_t expected_sigmask;
 
 
7149
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
 
7152
  /* Make sure SIGCHLD is unblocked for this test */
 
 
7153
  sigset_t sigchld_sigmask;
 
 
7154
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7155
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7156
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
 
7159
  /* Act; i.e. run the block_sigchld() function */
 
 
7160
  sigset_t actual_old_sigmask;
 
 
7161
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
 
7163
  /* Check the actual_old_sigmask; it should be the same as the
 
 
7164
     previously saved signal mask "expected_sigmask". */
 
 
7165
  for(int signum = 1; signum < NSIG; signum++){
 
 
7166
    const int expected_old_block_state
 
 
7167
      = sigismember(&expected_sigmask, signum);
 
 
7168
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7169
    const int actual_old_block_state
 
 
7170
      = sigismember(&actual_old_sigmask, signum);
 
 
7171
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7172
    g_assert_cmpint(actual_old_block_state,
 
 
7173
                    ==, expected_old_block_state);
 
 
7176
  /* Retrieve the newly set signal mask */
 
 
7177
  sigset_t actual_sigmask;
 
 
7178
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
 
7180
  /* SIGCHLD should be blocked */
 
 
7181
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
 
7183
  /* Restore signal mask */
 
 
7184
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
 
7188
static void test_restore_sigmask(__attribute__((unused))
 
 
7189
                                 test_fixture *fixture,
 
 
7190
                                 __attribute__((unused))
 
 
7191
                                 gconstpointer user_data){
 
 
7192
  /* Save original signal mask */
 
 
7193
  sigset_t orig_sigmask;
 
 
7194
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
 
7196
  /* Make sure SIGCHLD is blocked for this test */
 
 
7197
  sigset_t sigchld_sigmask;
 
 
7198
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7199
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7200
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
 
7203
  /* Act; i.e. run the restore_sigmask() function */
 
 
7204
  g_assert_true(restore_sigmask(&orig_sigmask));
 
 
7206
  /* Retrieve the newly restored signal mask */
 
 
7207
  sigset_t restored_sigmask;
 
 
7208
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
 
7211
  /* Check the restored_sigmask; it should be the same as the
 
 
7212
     previously saved signal mask "orig_sigmask". */
 
 
7213
  for(int signum = 1; signum < NSIG; signum++){
 
 
7214
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
 
7215
    g_assert_cmpint(orig_block_state, >=, 0);
 
 
7216
    const int restored_block_state = sigismember(&restored_sigmask,
 
 
7218
    g_assert_cmpint(restored_block_state, >=, 0);
 
 
7219
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
 
7222
  /* Restore signal mask */
 
 
7223
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
 
7227
static void test_parse_arguments_noargs(__attribute__((unused))
 
 
7228
                                        test_fixture *fixture,
 
 
7229
                                        __attribute__((unused))
 
 
7230
                                        gconstpointer user_data){
 
 
7234
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7236
  char *agent_directory = NULL;
 
 
7237
  char *helper_directory = NULL;
 
 
7240
  char *mandos_argz = NULL;
 
 
7241
  size_t mandos_argz_length = 0;
 
 
7243
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7244
                                &helper_directory, &user, &group,
 
 
7245
                                &mandos_argz, &mandos_argz_length));
 
 
7246
  g_assert_null(agent_directory);
 
 
7247
  g_assert_null(helper_directory);
 
 
7248
  g_assert_true(user == 0);
 
 
7249
  g_assert_true(group == 0);
 
 
7250
  g_assert_null(mandos_argz);
 
 
7251
  g_assert_true(mandos_argz_length == 0);
 
 
7253
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7258
__attribute__((nonnull))
 
 
7259
static bool parse_arguments_devnull(int argc, char *argv[],
 
 
7260
                                    const bool exit_failure,
 
 
7261
                                    char **agent_directory,
 
 
7262
                                    char **helper_directory,
 
 
7266
                                    size_t *mandos_argz_length){
 
 
7268
  FILE *real_stderr = stderr;
 
 
7269
  FILE *devnull = fopen("/dev/null", "we");
 
 
7270
  g_assert_nonnull(devnull);
 
 
7273
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
 
7275
                                   helper_directory, user, group,
 
 
7276
                                   mandos_argz, mandos_argz_length);
 
 
7277
  const error_t saved_errno = errno;
 
 
7279
  stderr = real_stderr;
 
 
7280
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
7282
  errno = saved_errno;
 
 
7287
static void test_parse_arguments_invalid(__attribute__((unused))
 
 
7288
                                         test_fixture *fixture,
 
 
7289
                                         __attribute__((unused))
 
 
7290
                                         gconstpointer user_data){
 
 
7293
    strdup("--invalid"),
 
 
7295
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7297
  char *agent_directory = NULL;
 
 
7298
  char *helper_directory = NULL;
 
 
7301
  char *mandos_argz = NULL;
 
 
7302
  size_t mandos_argz_length = 0;
 
 
7304
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7306
                                         &helper_directory, &user,
 
 
7307
                                         &group, &mandos_argz,
 
 
7308
                                         &mandos_argz_length));
 
 
7310
  g_assert_true(errno == EINVAL);
 
 
7311
  g_assert_null(agent_directory);
 
 
7312
  g_assert_null(helper_directory);
 
 
7313
  g_assert_null(mandos_argz);
 
 
7314
  g_assert_true(mandos_argz_length == 0);
 
 
7316
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7321
static void test_parse_arguments_long_dir(__attribute__((unused))
 
 
7322
                                          test_fixture *fixture,
 
 
7323
                                          __attribute__((unused))
 
 
7324
                                          gconstpointer user_data){
 
 
7327
    strdup("--agent-directory"),
 
 
7330
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7332
  __attribute__((cleanup(cleanup_string)))
 
 
7333
    char *agent_directory = NULL;
 
 
7334
  char *helper_directory = NULL;
 
 
7337
  __attribute__((cleanup(cleanup_string)))
 
 
7338
    char *mandos_argz = NULL;
 
 
7339
  size_t mandos_argz_length = 0;
 
 
7341
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7342
                                &helper_directory, &user, &group,
 
 
7343
                                &mandos_argz, &mandos_argz_length));
 
 
7345
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7346
  g_assert_null(helper_directory);
 
 
7347
  g_assert_true(user == 0);
 
 
7348
  g_assert_true(group == 0);
 
 
7349
  g_assert_null(mandos_argz);
 
 
7350
  g_assert_true(mandos_argz_length == 0);
 
 
7352
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7357
static void test_parse_arguments_short_dir(__attribute__((unused))
 
 
7358
                                           test_fixture *fixture,
 
 
7359
                                           __attribute__((unused))
 
 
7360
                                           gconstpointer user_data){
 
 
7366
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7368
  __attribute__((cleanup(cleanup_string)))
 
 
7369
    char *agent_directory = NULL;
 
 
7370
  char *helper_directory = NULL;
 
 
7373
  __attribute__((cleanup(cleanup_string)))
 
 
7374
    char *mandos_argz = NULL;
 
 
7375
  size_t mandos_argz_length = 0;
 
 
7377
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7378
                                &helper_directory, &user, &group,
 
 
7379
                                &mandos_argz, &mandos_argz_length));
 
 
7381
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7382
  g_assert_null(helper_directory);
 
 
7383
  g_assert_true(user == 0);
 
 
7384
  g_assert_true(group == 0);
 
 
7385
  g_assert_null(mandos_argz);
 
 
7386
  g_assert_true(mandos_argz_length == 0);
 
 
7388
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7394
void test_parse_arguments_helper_directory(__attribute__((unused))
 
 
7395
                                           test_fixture *fixture,
 
 
7396
                                           __attribute__((unused))
 
 
7397
                                           gconstpointer user_data){
 
 
7400
    strdup("--helper-directory"),
 
 
7403
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7405
  char *agent_directory = NULL;
 
 
7406
  __attribute__((cleanup(cleanup_string)))
 
 
7407
    char *helper_directory = NULL;
 
 
7410
  __attribute__((cleanup(cleanup_string)))
 
 
7411
    char *mandos_argz = NULL;
 
 
7412
  size_t mandos_argz_length = 0;
 
 
7414
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7415
                                &helper_directory, &user, &group,
 
 
7416
                                &mandos_argz, &mandos_argz_length));
 
 
7418
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7419
  g_assert_null(agent_directory);
 
 
7420
  g_assert_true(user == 0);
 
 
7421
  g_assert_true(group == 0);
 
 
7422
  g_assert_null(mandos_argz);
 
 
7423
  g_assert_true(mandos_argz_length == 0);
 
 
7425
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7431
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
 
7432
                                            test_fixture *fixture,
 
 
7433
                                            __attribute__((unused))
 
 
7434
                                            gconstpointer user_data){
 
 
7437
    strdup("--plugin-helper-dir"),
 
 
7440
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7442
  char *agent_directory = NULL;
 
 
7443
  __attribute__((cleanup(cleanup_string)))
 
 
7444
    char *helper_directory = NULL;
 
 
7447
  __attribute__((cleanup(cleanup_string)))
 
 
7448
    char *mandos_argz = NULL;
 
 
7449
  size_t mandos_argz_length = 0;
 
 
7451
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7452
                                &helper_directory, &user, &group,
 
 
7453
                                &mandos_argz, &mandos_argz_length));
 
 
7455
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7456
  g_assert_null(agent_directory);
 
 
7457
  g_assert_true(user == 0);
 
 
7458
  g_assert_true(group == 0);
 
 
7459
  g_assert_null(mandos_argz);
 
 
7460
  g_assert_true(mandos_argz_length == 0);
 
 
7462
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7467
static void test_parse_arguments_user(__attribute__((unused))
 
 
7468
                                      test_fixture *fixture,
 
 
7469
                                      __attribute__((unused))
 
 
7470
                                      gconstpointer user_data){
 
 
7476
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7478
  char *agent_directory = NULL;
 
 
7479
  __attribute__((cleanup(cleanup_string)))
 
 
7480
    char *helper_directory = NULL;
 
 
7483
  __attribute__((cleanup(cleanup_string)))
 
 
7484
    char *mandos_argz = NULL;
 
 
7485
  size_t mandos_argz_length = 0;
 
 
7487
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7488
                                &helper_directory, &user, &group,
 
 
7489
                                &mandos_argz, &mandos_argz_length));
 
 
7491
  g_assert_null(helper_directory);
 
 
7492
  g_assert_null(agent_directory);
 
 
7493
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
 
7494
  g_assert_true(group == 0);
 
 
7495
  g_assert_null(mandos_argz);
 
 
7496
  g_assert_true(mandos_argz_length == 0);
 
 
7498
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7503
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
 
7504
                                              test_fixture *fixture,
 
 
7505
                                              __attribute__((unused))
 
 
7513
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7515
  char *agent_directory = NULL;
 
 
7516
  __attribute__((cleanup(cleanup_string)))
 
 
7517
    char *helper_directory = NULL;
 
 
7520
  __attribute__((cleanup(cleanup_string)))
 
 
7521
    char *mandos_argz = NULL;
 
 
7522
  size_t mandos_argz_length = 0;
 
 
7524
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7526
                                         &helper_directory, &user,
 
 
7527
                                         &group, &mandos_argz,
 
 
7528
                                         &mandos_argz_length));
 
 
7530
  g_assert_null(helper_directory);
 
 
7531
  g_assert_null(agent_directory);
 
 
7532
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7533
  g_assert_true(group == 0);
 
 
7534
  g_assert_null(mandos_argz);
 
 
7535
  g_assert_true(mandos_argz_length == 0);
 
 
7537
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7543
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
 
7544
                                            test_fixture *fixture,
 
 
7545
                                            __attribute__((unused))
 
 
7546
                                            gconstpointer user_data){
 
 
7552
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7554
  char *agent_directory = NULL;
 
 
7555
  __attribute__((cleanup(cleanup_string)))
 
 
7556
    char *helper_directory = NULL;
 
 
7559
  __attribute__((cleanup(cleanup_string)))
 
 
7560
    char *mandos_argz = NULL;
 
 
7561
  size_t mandos_argz_length = 0;
 
 
7563
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7565
                                         &helper_directory, &user,
 
 
7566
                                         &group, &mandos_argz,
 
 
7567
                                         &mandos_argz_length));
 
 
7569
  g_assert_null(helper_directory);
 
 
7570
  g_assert_null(agent_directory);
 
 
7571
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7572
  g_assert_true(group == 0);
 
 
7573
  g_assert_null(mandos_argz);
 
 
7574
  g_assert_true(mandos_argz_length == 0);
 
 
7576
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7581
static void test_parse_arguments_group(__attribute__((unused))
 
 
7582
                                       test_fixture *fixture,
 
 
7583
                                       __attribute__((unused))
 
 
7584
                                       gconstpointer user_data){
 
 
7590
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7592
  char *agent_directory = NULL;
 
 
7593
  __attribute__((cleanup(cleanup_string)))
 
 
7594
    char *helper_directory = NULL;
 
 
7597
  __attribute__((cleanup(cleanup_string)))
 
 
7598
    char *mandos_argz = NULL;
 
 
7599
  size_t mandos_argz_length = 0;
 
 
7601
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7602
                                &helper_directory, &user, &group,
 
 
7603
                                &mandos_argz, &mandos_argz_length));
 
 
7605
  g_assert_null(helper_directory);
 
 
7606
  g_assert_null(agent_directory);
 
 
7607
  g_assert_true(user == 0);
 
 
7608
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
 
7609
  g_assert_null(mandos_argz);
 
 
7610
  g_assert_true(mandos_argz_length == 0);
 
 
7612
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7617
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
 
7618
                                               test_fixture *fixture,
 
 
7619
                                               __attribute__((unused))
 
 
7627
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7629
  char *agent_directory = NULL;
 
 
7630
  __attribute__((cleanup(cleanup_string)))
 
 
7631
    char *helper_directory = NULL;
 
 
7634
  __attribute__((cleanup(cleanup_string)))
 
 
7635
    char *mandos_argz = NULL;
 
 
7636
  size_t mandos_argz_length = 0;
 
 
7638
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7640
                                         &helper_directory, &user,
 
 
7641
                                         &group, &mandos_argz,
 
 
7642
                                         &mandos_argz_length));
 
 
7644
  g_assert_null(helper_directory);
 
 
7645
  g_assert_null(agent_directory);
 
 
7646
  g_assert_true(user == 0);
 
 
7647
  g_assert_true(group == 0);
 
 
7648
  g_assert_null(mandos_argz);
 
 
7649
  g_assert_true(mandos_argz_length == 0);
 
 
7651
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7657
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
 
7658
                                             test_fixture *fixture,
 
 
7659
                                             __attribute__((unused))
 
 
7660
                                             gconstpointer user_data){
 
 
7666
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7668
  char *agent_directory = NULL;
 
 
7669
  __attribute__((cleanup(cleanup_string)))
 
 
7670
    char *helper_directory = NULL;
 
 
7673
  __attribute__((cleanup(cleanup_string)))
 
 
7674
    char *mandos_argz = NULL;
 
 
7675
  size_t mandos_argz_length = 0;
 
 
7677
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7679
                                         &helper_directory, &user,
 
 
7680
                                         &group, &mandos_argz,
 
 
7681
                                         &mandos_argz_length));
 
 
7683
  g_assert_null(helper_directory);
 
 
7684
  g_assert_null(agent_directory);
 
 
7685
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
 
7686
  g_assert_true(group == 0);
 
 
7687
  g_assert_null(mandos_argz);
 
 
7688
  g_assert_true(mandos_argz_length == 0);
 
 
7690
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7695
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
 
7696
                                               test_fixture *fixture,
 
 
7697
                                               __attribute__((unused))
 
 
7702
    strdup("mandos-client"),
 
 
7704
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7706
  __attribute__((cleanup(cleanup_string)))
 
 
7707
    char *agent_directory = NULL;
 
 
7708
  __attribute__((cleanup(cleanup_string)))
 
 
7709
    char *helper_directory = NULL;
 
 
7712
  __attribute__((cleanup(cleanup_string)))
 
 
7713
    char *mandos_argz = NULL;
 
 
7714
  size_t mandos_argz_length = 0;
 
 
7716
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7717
                                &helper_directory, &user, &group,
 
 
7718
                                &mandos_argz, &mandos_argz_length));
 
 
7720
  g_assert_null(agent_directory);
 
 
7721
  g_assert_null(helper_directory);
 
 
7722
  g_assert_true(user == 0);
 
 
7723
  g_assert_true(group == 0);
 
 
7724
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
 
7725
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7726
                                            mandos_argz_length),
 
 
7729
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7734
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
 
7735
                                             test_fixture *fixture,
 
 
7736
                                             __attribute__((unused))
 
 
7737
                                             gconstpointer user_data){
 
 
7740
    strdup("mandos-client"),
 
 
7745
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7747
  __attribute__((cleanup(cleanup_string)))
 
 
7748
    char *agent_directory = NULL;
 
 
7749
  __attribute__((cleanup(cleanup_string)))
 
 
7750
    char *helper_directory = NULL;
 
 
7753
  __attribute__((cleanup(cleanup_string)))
 
 
7754
    char *mandos_argz = NULL;
 
 
7755
  size_t mandos_argz_length = 0;
 
 
7757
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7758
                                &helper_directory, &user, &group,
 
 
7759
                                &mandos_argz, &mandos_argz_length));
 
 
7761
  g_assert_null(agent_directory);
 
 
7762
  g_assert_null(helper_directory);
 
 
7763
  g_assert_true(user == 0);
 
 
7764
  g_assert_true(group == 0);
 
 
7765
  char *marg = mandos_argz;
 
 
7766
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7767
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7768
  g_assert_cmpstr(marg, ==, "one");
 
 
7769
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7770
  g_assert_cmpstr(marg, ==, "two");
 
 
7771
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7772
  g_assert_cmpstr(marg, ==, "three");
 
 
7773
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7774
                                            mandos_argz_length),
 
 
7777
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7782
static void test_parse_arguments_all_args(__attribute__((unused))
 
 
7783
                                          test_fixture *fixture,
 
 
7784
                                          __attribute__((unused))
 
 
7785
                                          gconstpointer user_data){
 
 
7788
    strdup("--agent-directory"),
 
 
7790
    strdup("--helper-directory"),
 
 
7796
    strdup("mandos-client"),
 
 
7801
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7803
  __attribute__((cleanup(cleanup_string)))
 
 
7804
    char *agent_directory = NULL;
 
 
7805
  __attribute__((cleanup(cleanup_string)))
 
 
7806
    char *helper_directory = NULL;
 
 
7809
  __attribute__((cleanup(cleanup_string)))
 
 
7810
    char *mandos_argz = NULL;
 
 
7811
  size_t mandos_argz_length = 0;
 
 
7813
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7814
                                &helper_directory, &user, &group,
 
 
7815
                                &mandos_argz, &mandos_argz_length));
 
 
7817
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7818
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7819
  g_assert_true(user == 1);
 
 
7820
  g_assert_true(group == 2);
 
 
7821
  char *marg = mandos_argz;
 
 
7822
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7823
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7824
  g_assert_cmpstr(marg, ==, "one");
 
 
7825
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7826
  g_assert_cmpstr(marg, ==, "two");
 
 
7827
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7828
  g_assert_cmpstr(marg, ==, "three");
 
 
7829
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7830
                                            mandos_argz_length),
 
 
7833
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7838
static void test_parse_arguments_mixed(__attribute__((unused))
 
 
7839
                                       test_fixture *fixture,
 
 
7840
                                       __attribute__((unused))
 
 
7841
                                       gconstpointer user_data){
 
 
7844
    strdup("mandos-client"),
 
 
7848
    strdup("--agent-directory"),
 
 
7852
    strdup("--helper-directory=/var/tmp"),
 
 
7854
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7856
  __attribute__((cleanup(cleanup_string)))
 
 
7857
    char *agent_directory = NULL;
 
 
7858
  __attribute__((cleanup(cleanup_string)))
 
 
7859
    char *helper_directory = NULL;
 
 
7862
  __attribute__((cleanup(cleanup_string)))
 
 
7863
    char *mandos_argz = NULL;
 
 
7864
  size_t mandos_argz_length = 0;
 
 
7866
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7867
                                &helper_directory, &user, &group,
 
 
7868
                                &mandos_argz, &mandos_argz_length));
 
 
7870
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7871
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7872
  g_assert_true(user == 1);
 
 
7873
  g_assert_true(group == 0);
 
 
7874
  char *marg = mandos_argz;
 
 
7875
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7876
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7877
  g_assert_cmpstr(marg, ==, "one");
 
 
7878
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7879
  g_assert_cmpstr(marg, ==, "two");
 
 
7880
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7881
  g_assert_cmpstr(marg, ==, "three");
 
 
7882
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7883
                                            mandos_argz_length),
 
 
7886
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7891
/* End of tests section */
 
 
7893
/* Test boilerplate section; New tests should be added to the test
 
 
7894
   suite definition here, in the "run_tests" function.
 
 
7896
   Finally, this section also contains the should_only_run_tests()
 
 
7897
   function used by main() for deciding if tests should be run or to
 
 
7900
__attribute__((cold))
 
 
7901
static bool run_tests(int argc, char *argv[]){
 
 
7902
  g_test_init(&argc, &argv, NULL);
 
 
7904
  /* A macro to add a test with no setup or teardown functions */
 
 
7905
#define test_add(testpath, testfunc)                    \
 
 
7907
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
 
7908
               (testfunc), NULL);                       \
 
 
7911
  /* Test the signal-related functions first, since some other tests
 
 
7912
     depend on these functions in their setups and teardowns */
 
 
7913
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
 
7914
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
 
7915
  test_add("/signal-handling/block", test_block_sigchld);
 
 
7916
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
 
7918
  /* Regular non-signal-related tests; these use no setups or
 
 
7920
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
 
7921
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
 
7922
  test_add("/parse_arguments/long-dir",
 
 
7923
           test_parse_arguments_long_dir);
 
 
7924
  test_add("/parse_arguments/short-dir",
 
 
7925
           test_parse_arguments_short_dir);
 
 
7926
  test_add("/parse_arguments/helper-directory",
 
 
7927
           test_parse_arguments_helper_directory);
 
 
7928
  test_add("/parse_arguments/plugin-helper-dir",
 
 
7929
           test_parse_arguments_plugin_helper_dir);
 
 
7930
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
 
7931
  test_add("/parse_arguments/user-invalid",
 
 
7932
           test_parse_arguments_user_invalid);
 
 
7933
  test_add("/parse_arguments/user-zero-invalid",
 
 
7934
           test_parse_arguments_user_zero_invalid);
 
 
7935
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
 
7936
  test_add("/parse_arguments/group-invalid",
 
 
7937
           test_parse_arguments_group_invalid);
 
 
7938
  test_add("/parse_arguments/group-zero-invalid",
 
 
7939
           test_parse_arguments_group_zero_invalid);
 
 
7940
  test_add("/parse_arguments/mandos-noargs",
 
 
7941
           test_parse_arguments_mandos_noargs);
 
 
7942
  test_add("/parse_arguments/mandos-args",
 
 
7943
           test_parse_arguments_mandos_args);
 
 
7944
  test_add("/parse_arguments/all-args",
 
 
7945
           test_parse_arguments_all_args);
 
 
7946
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
 
7947
  test_add("/queue/create", test_create_queue);
 
 
7948
  test_add("/queue/add", test_add_to_queue);
 
 
7949
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
 
7950
  test_add("/queue/has_question/empty",
 
 
7951
           test_queue_has_question_empty);
 
 
7952
  test_add("/queue/has_question/false",
 
 
7953
           test_queue_has_question_false);
 
 
7954
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
 
7955
  test_add("/queue/has_question/false2",
 
 
7956
           test_queue_has_question_false2);
 
 
7957
  test_add("/queue/has_question/true2",
 
 
7958
           test_queue_has_question_true2);
 
 
7959
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
 
7960
  test_add("/string_set/net-set-contains-nothing",
 
 
7961
           test_string_set_new_set_contains_nothing);
 
 
7962
  test_add("/string_set/with-added-string-contains-it",
 
 
7963
           test_string_set_with_added_string_contains_it);
 
 
7964
  test_add("/string_set/cleared-does-not-contain-string",
 
 
7965
           test_string_set_cleared_does_not_contain_str);
 
 
7966
  test_add("/string_set/swap/one-with-empty",
 
 
7967
           test_string_set_swap_one_with_empty);
 
 
7968
  test_add("/string_set/swap/empty-with-one",
 
 
7969
           test_string_set_swap_empty_with_one);
 
 
7970
  test_add("/string_set/swap/one-with-one",
 
 
7971
           test_string_set_swap_one_with_one);
 
 
7973
  /* A macro to add a test using the setup and teardown functions */
 
 
7974
#define test_add_st(path, func)                                 \
 
 
7976
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
 
7980
  /* Signal-related tests; these use setups and teardowns which
 
 
7981
     establish, during each test run, a signal handler for, and a
 
 
7982
     signal mask blocking, the SIGCHLD signal, just like main() */
 
 
7983
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
 
7984
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
 
7985
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
 
7986
  test_add_st("/run_queue/zeroes-next-run",
 
 
7987
              test_run_queue_zeroes_next_run);
 
 
7988
  test_add_st("/run_queue/clears-cancelled_filenames",
 
 
7989
              test_run_queue_clears_cancelled_filenames);
 
 
7990
  test_add_st("/run_queue/skips-cancelled-filenames",
 
 
7991
              test_run_queue_skips_cancelled_filenames);
 
 
7992
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
 
7993
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
 
7994
  test_add_st("/run_queue/two-tasks/quit",
 
 
7995
              test_run_queue_two_tasks_quit);
 
 
7996
  test_add_st("/run_queue/two-tasks-cleanup",
 
 
7997
              test_run_queue_two_tasks_cleanup);
 
 
7998
  test_add_st("/task-creators/start_mandos_client",
 
 
7999
              test_start_mandos_client);
 
 
8000
  test_add_st("/task-creators/start_mandos_client/execv",
 
 
8001
              test_start_mandos_client_execv);
 
 
8002
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
 
8003
              test_start_mandos_client_suid_euid);
 
 
8004
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
 
8005
              test_start_mandos_client_suid_egid);
 
 
8006
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
 
8007
              test_start_mandos_client_suid_ruid);
 
 
8008
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
 
8009
              test_start_mandos_client_suid_rgid);
 
 
8010
  test_add_st("/task-creators/start_mandos_client/read",
 
 
8011
              test_start_mandos_client_read);
 
 
8012
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
 
8013
              test_start_mandos_client_helper_directory);
 
 
8014
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
 
8015
              test_start_mandos_client_sigmask);
 
 
8016
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
 
8017
              test_wait_for_mandos_client_exit_badpid);
 
 
8018
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
 
8019
              test_wait_for_mandos_client_exit_noexit);
 
 
8020
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
 
8021
              test_wait_for_mandos_client_exit_success);
 
 
8022
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
 
8023
              test_wait_for_mandos_client_exit_failure);
 
 
8024
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
 
8025
              test_wait_for_mandos_client_exit_killed);
 
 
8026
  test_add_st("/task/read_mandos_client_output/readerror",
 
 
8027
              test_read_mandos_client_output_readerror);
 
 
8028
  test_add_st("/task/read_mandos_client_output/nodata",
 
 
8029
              test_read_mandos_client_output_nodata);
 
 
8030
  test_add_st("/task/read_mandos_client_output/eof",
 
 
8031
              test_read_mandos_client_output_eof);
 
 
8032
  test_add_st("/task/read_mandos_client_output/once",
 
 
8033
              test_read_mandos_client_output_once);
 
 
8034
  test_add_st("/task/read_mandos_client_output/malloc",
 
 
8035
              test_read_mandos_client_output_malloc);
 
 
8036
  test_add_st("/task/read_mandos_client_output/append",
 
 
8037
              test_read_mandos_client_output_append);
 
 
8038
  test_add_st("/task-creators/add_inotify_dir_watch",
 
 
8039
              test_add_inotify_dir_watch);
 
 
8040
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
 
8041
              test_add_inotify_dir_watch_fail);
 
 
8042
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
 
8043
              test_add_inotify_dir_watch_nondir);
 
 
8044
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
 
8045
              test_add_inotify_dir_watch_EAGAIN);
 
 
8046
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
 
8047
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
 
8048
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
 
8049
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
 
8050
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
 
8051
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
 
8052
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
 
8053
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
 
8054
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
 
8055
              test_add_inotify_dir_watch_IN_DELETE);
 
 
8056
  test_add_st("/task/read_inotify_event/readerror",
 
 
8057
              test_read_inotify_event_readerror);
 
 
8058
  test_add_st("/task/read_inotify_event/bad-epoll",
 
 
8059
              test_read_inotify_event_bad_epoll);
 
 
8060
  test_add_st("/task/read_inotify_event/nodata",
 
 
8061
              test_read_inotify_event_nodata);
 
 
8062
  test_add_st("/task/read_inotify_event/eof",
 
 
8063
              test_read_inotify_event_eof);
 
 
8064
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
 
8065
              test_read_inotify_event_IN_CLOSE_WRITE);
 
 
8066
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
 
8067
              test_read_inotify_event_IN_MOVED_TO);
 
 
8068
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
 
8069
              test_read_inotify_event_IN_MOVED_FROM);
 
 
8070
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
 
8071
              test_read_inotify_event_IN_DELETE);
 
 
8072
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
 
8073
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
 
8074
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
 
8075
              test_read_inotify_event_IN_MOVED_TO_badname);
 
 
8076
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
 
8077
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
 
8078
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
 
8079
              test_read_inotify_event_IN_DELETE_badname);
 
 
8080
  test_add_st("/task/open_and_parse_question/ENOENT",
 
 
8081
              test_open_and_parse_question_ENOENT);
 
 
8082
  test_add_st("/task/open_and_parse_question/EIO",
 
 
8083
              test_open_and_parse_question_EIO);
 
 
8084
  test_add_st("/task/open_and_parse_question/parse-error",
 
 
8085
              test_open_and_parse_question_parse_error);
 
 
8086
  test_add_st("/task/open_and_parse_question/nosocket",
 
 
8087
              test_open_and_parse_question_nosocket);
 
 
8088
  test_add_st("/task/open_and_parse_question/badsocket",
 
 
8089
              test_open_and_parse_question_badsocket);
 
 
8090
  test_add_st("/task/open_and_parse_question/nopid",
 
 
8091
              test_open_and_parse_question_nopid);
 
 
8092
  test_add_st("/task/open_and_parse_question/badpid",
 
 
8093
              test_open_and_parse_question_badpid);
 
 
8094
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
 
8095
              test_open_and_parse_question_noexist_pid);
 
 
8096
  test_add_st("/task/open_and_parse_question/no-notafter",
 
 
8097
              test_open_and_parse_question_no_notafter);
 
 
8098
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
 
8099
              test_open_and_parse_question_bad_notafter);
 
 
8100
  test_add_st("/task/open_and_parse_question/notafter-0",
 
 
8101
              test_open_and_parse_question_notafter_0);
 
 
8102
  test_add_st("/task/open_and_parse_question/notafter-1",
 
 
8103
              test_open_and_parse_question_notafter_1);
 
 
8104
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
 
8105
              test_open_and_parse_question_notafter_1_1);
 
 
8106
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
 
8107
              test_open_and_parse_question_notafter_1_2);
 
 
8108
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
 
8109
              test_open_and_parse_question_equal_notafter);
 
 
8110
  test_add_st("/task/open_and_parse_question/late-notafter",
 
 
8111
              test_open_and_parse_question_late_notafter);
 
 
8112
  test_add_st("/task/cancel_old_question/0-1-2",
 
 
8113
              test_cancel_old_question_0_1_2);
 
 
8114
  test_add_st("/task/cancel_old_question/0-2-1",
 
 
8115
              test_cancel_old_question_0_2_1);
 
 
8116
  test_add_st("/task/cancel_old_question/1-2-3",
 
 
8117
              test_cancel_old_question_1_2_3);
 
 
8118
  test_add_st("/task/cancel_old_question/1-3-2",
 
 
8119
              test_cancel_old_question_1_3_2);
 
 
8120
  test_add_st("/task/cancel_old_question/2-1-3",
 
 
8121
              test_cancel_old_question_2_1_3);
 
 
8122
  test_add_st("/task/cancel_old_question/2-3-1",
 
 
8123
              test_cancel_old_question_2_3_1);
 
 
8124
  test_add_st("/task/cancel_old_question/3-1-2",
 
 
8125
              test_cancel_old_question_3_1_2);
 
 
8126
  test_add_st("/task/cancel_old_question/3-2-1",
 
 
8127
              test_cancel_old_question_3_2_1);
 
 
8128
  test_add_st("/task/connect_question_socket/name-too-long",
 
 
8129
              test_connect_question_socket_name_too_long);
 
 
8130
  test_add_st("/task/connect_question_socket/connect-fail",
 
 
8131
              test_connect_question_socket_connect_fail);
 
 
8132
  test_add_st("/task/connect_question_socket/bad-epoll",
 
 
8133
              test_connect_question_socket_bad_epoll);
 
 
8134
  test_add_st("/task/connect_question_socket/usable",
 
 
8135
              test_connect_question_socket_usable);
 
 
8136
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
 
8137
              test_send_password_to_socket_client_not_exited);
 
 
8138
  test_add_st("/task/send_password_to_socket/password-not-read",
 
 
8139
              test_send_password_to_socket_password_not_read);
 
 
8140
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
 
8141
              test_send_password_to_socket_EMSGSIZE);
 
 
8142
  test_add_st("/task/send_password_to_socket/retry",
 
 
8143
              test_send_password_to_socket_retry);
 
 
8144
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
 
8145
              test_send_password_to_socket_bad_epoll);
 
 
8146
  test_add_st("/task/send_password_to_socket/null-password",
 
 
8147
              test_send_password_to_socket_null_password);
 
 
8148
  test_add_st("/task/send_password_to_socket/empty-password",
 
 
8149
              test_send_password_to_socket_empty_password);
 
 
8150
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
 
8151
              test_send_password_to_socket_empty_str_pass);
 
 
8152
  test_add_st("/task/send_password_to_socket/text-password",
 
 
8153
              test_send_password_to_socket_text_password);
 
 
8154
  test_add_st("/task/send_password_to_socket/binary-password",
 
 
8155
              test_send_password_to_socket_binary_password);
 
 
8156
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
 
8157
              test_send_password_to_socket_nuls_in_password);
 
 
8158
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
 
8159
              test_add_existing_questions_ENOENT);
 
 
8160
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
 
8161
              test_add_existing_questions_no_questions);
 
 
8162
  test_add_st("/task-creators/add_existing_questions/one-question",
 
 
8163
              test_add_existing_questions_one_question);
 
 
8164
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
 
8165
              test_add_existing_questions_two_questions);
 
 
8166
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
 
8167
              test_add_existing_questions_non_questions);
 
 
8168
  test_add_st("/task-creators/add_existing_questions/both-types",
 
 
8169
              test_add_existing_questions_both_types);
 
 
8171
  return g_test_run() == 0;
 
 
8174
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
 
8175
  GOptionContext *context = g_option_context_new("");
 
 
8177
  g_option_context_set_help_enabled(context, FALSE);
 
 
8178
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
 
8180
  gboolean should_run_tests = FALSE;
 
 
8181
  GOptionEntry entries[] = {
 
 
8182
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
 
8183
      &should_run_tests, "Run tests", NULL },
 
 
8186
  g_option_context_add_main_entries(context, entries, NULL);
 
 
8188
  GError *error = NULL;
 
 
8190
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
 
8191
    g_option_context_free(context);
 
 
8192
    g_error("Failed to parse options: %s", error->message);
 
 
8195
  g_option_context_free(context);
 
 
8196
  return should_run_tests != FALSE;
 
 
8203
  (if (not (funcall run-tests-in-test-buffer default-directory))
 
 
8204
      (funcall show-test-buffer-in-test-window)
 
 
8205
    (funcall remove-test-window)))
 
 
8206
run-tests-in-test-buffer:
 
 
8208
  (with-current-buffer (get-buffer-create "*Test*")
 
 
8209
    (setq buffer-read-only nil
 
 
8210
          default-directory dir)
 
 
8213
  (let ((process-result
 
 
8214
         (let ((inhibit-read-only t))
 
 
8215
           (process-file-shell-command
 
 
8216
            (funcall get-command-line) nil "*Test*"))))
 
 
8217
    (and (numberp process-result)
 
 
8218
         (= process-result 0))))
 
 
8223
        (funcall find-build-directory (buffer-file-name)))
 
 
8224
       (local-build-directory
 
 
8225
        (if (fboundp 'file-local-name)
 
 
8226
            (file-local-name build-directory)
 
 
8227
          (or (file-remote-p build-directory 'localname)
 
 
8230
        (file-relative-name (file-name-sans-extension
 
 
8231
                             (buffer-file-name)) build-directory))
 
 
8232
       (qbdir (shell-quote-argument local-build-directory))
 
 
8233
       (qcmd (shell-quote-argument command)))
 
 
8234
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
 
 
8235
             " && %s --test --verbose") qbdir qcmd qcmd)))
 
 
8236
find-build-directory:
 
 
8237
(lambda (try-directory &optional base-directory)
 
 
8238
  (let ((base-directory (or base-directory try-directory)))
 
 
8239
    (cond ((equal try-directory "/") base-directory)
 
 
8241
            (concat (file-name-as-directory try-directory)
 
 
8242
                    "Makefile")) try-directory)
 
 
8243
          ((funcall find-build-directory
 
 
8244
                    (directory-file-name (file-name-directory
 
 
8247
show-test-buffer-in-test-window:
 
 
8249
  (when (not (get-buffer-window-list "*Test*"))
 
 
8250
    (setq next-error-last-buffer (get-buffer "*Test*"))
 
 
8251
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
 
 
8252
           (display-buffer-overriding-action
 
 
8253
            `((display-buffer-in-side-window) (side . ,side)
 
 
8254
              (window-height . fit-window-to-buffer)
 
 
8255
              (window-width . fit-window-to-buffer))))
 
 
8256
      (display-buffer "*Test*"))))
 
 
8259
  (let ((test-window (get-buffer-window "*Test*")))
 
 
8260
    (if test-window (delete-window test-window))))
 
 
8261
eval: (add-hook 'after-save-hook run-tests 90 t)